1 /* PSPP - a program for statistical analysis.
2 Copyright (C) 2021 Free Software Foundation, Inc.
4 This program is free software: you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation, either version 3 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program. If not, see <http://www.gnu.org/licenses/>. */
22 #include "data/casereader.h"
23 #include "data/casewriter.h"
24 #include "data/data-in.h"
25 #include "data/data-out.h"
26 #include "data/dataset.h"
27 #include "data/dictionary.h"
28 #include "data/mrset.h"
29 #include "data/subcase.h"
30 #include "data/value-labels.h"
31 #include "language/command.h"
32 #include "language/lexer/format-parser.h"
33 #include "language/lexer/lexer.h"
34 #include "language/lexer/token.h"
35 #include "language/lexer/variable-parser.h"
36 #include "libpspp/array.h"
37 #include "libpspp/assertion.h"
38 #include "libpspp/hash-functions.h"
39 #include "libpspp/hmap.h"
40 #include "libpspp/i18n.h"
41 #include "libpspp/message.h"
42 #include "libpspp/string-array.h"
43 #include "math/mode.h"
44 #include "math/moments.h"
45 #include "math/percentiles.h"
46 #include "math/sort.h"
47 #include "output/pivot-table.h"
49 #include "gl/minmax.h"
50 #include "gl/xalloc.h"
53 #define _(msgid) gettext (msgid)
54 #define N_(msgid) (msgid)
58 CTVL_NONE = SETTINGS_VALUE_SHOW_DEFAULT,
59 CTVL_NAME = SETTINGS_VALUE_SHOW_VALUE,
60 CTVL_LABEL = SETTINGS_VALUE_SHOW_LABEL,
61 CTVL_BOTH = SETTINGS_VALUE_SHOW_BOTH,
65 - unweighted summaries (U*)
66 - lower confidence limits (*.LCL)
67 - upper confidence limits (*.UCL)
68 - standard error (*.SE)
71 /* All variables. */ \
72 S(CTSF_COUNT, "COUNT", N_("Count"), CTF_COUNT, CTFA_ALL) \
73 S(CTSF_ECOUNT, "ECOUNT", N_("Adjusted Count"), CTF_COUNT, CTFA_ALL) \
74 S(CTSF_ROWPCT_COUNT, "ROWPCT.COUNT", N_("Row %"), CTF_PERCENT, CTFA_ALL) \
75 S(CTSF_COLPCT_COUNT, "COLPCT.COUNT", N_("Column %"), CTF_PERCENT, CTFA_ALL) \
76 S(CTSF_TABLEPCT_COUNT, "TABLEPCT.COUNT", N_("Table %"), CTF_PERCENT, CTFA_ALL) \
77 S(CTSF_SUBTABLEPCT_COUNT, "SUBTABLEPCT.COUNT", N_("Subtable %"), CTF_PERCENT, CTFA_ALL) \
78 S(CTSF_LAYERPCT_COUNT, "LAYERPCT.COUNT", N_("Layer %"), CTF_PERCENT, CTFA_ALL) \
79 S(CTSF_LAYERROWPCT_COUNT, "LAYERROWPCT.COUNT", N_("Layer Row %"), CTF_PERCENT, CTFA_ALL) \
80 S(CTSF_LAYERCOLPCT_COUNT, "LAYERCOLPCT.COUNT", N_("Layer Column %"), CTF_PERCENT, CTFA_ALL) \
81 S(CTSF_ROWPCT_VALIDN, "ROWPCT.VALIDN", N_("Row Valid N %"), CTF_PERCENT, CTFA_ALL) \
82 S(CTSF_COLPCT_VALIDN, "COLPCT.VALIDN", N_("Column Valid N %"), CTF_PERCENT, CTFA_ALL) \
83 S(CTSF_TABLEPCT_VALIDN, "TABLEPCT.VALIDN", N_("Table Valid N %"), CTF_PERCENT, CTFA_ALL) \
84 S(CTSF_SUBTABLEPCT_VALIDN, "SUBTABLEPCT.VALIDN", N_("Subtable Valid N %"), CTF_PERCENT, CTFA_ALL) \
85 S(CTSF_LAYERPCT_VALIDN, "LAYERPCT.VALIDN", N_("Layer Valid N %"), CTF_PERCENT, CTFA_ALL) \
86 S(CTSF_LAYERROWPCT_VALIDN, "LAYERROWPCT.VALIDN", N_("Layer Row Valid N %"), CTF_PERCENT, CTFA_ALL) \
87 S(CTSF_LAYERCOLPCT_VALIDN, "LAYERCOLPCT.VALIDN", N_("Layer Column Valid N %"), CTF_PERCENT, CTFA_ALL) \
88 S(CTSF_ROWPCT_TOTALN, "ROWPCT.TOTALN", N_("Row Total N %"), CTF_PERCENT, CTFA_ALL) \
89 S(CTSF_COLPCT_TOTALN, "COLPCT.TOTALN", N_("Column Total N %"), CTF_PERCENT, CTFA_ALL) \
90 S(CTSF_TABLEPCT_TOTALN, "TABLEPCT.TOTALN", N_("Table Total N %"), CTF_PERCENT, CTFA_ALL) \
91 S(CTSF_SUBTABLEPCT_TOTALN, "SUBTABLEPCT.TOTALN", N_("Subtable Total N %"), CTF_PERCENT, CTFA_ALL) \
92 S(CTSF_LAYERPCT_TOTALN, "LAYERPCT.TOTALN", N_("Layer Total N %"), CTF_PERCENT, CTFA_ALL) \
93 S(CTSF_LAYERROWPCT_TOTALN, "LAYERROWPCT.TOTALN", N_("Layer Row Total N %"), CTF_PERCENT, CTFA_ALL) \
94 S(CTSF_LAYERCOLPCT_TOTALN, "LAYERCOLPCT.TOTALN", N_("Layer Column Total N %"), CTF_PERCENT, CTFA_ALL) \
96 /* Scale variables, totals, and subtotals. */ \
97 S(CTSF_MAXIMUM, "MAXIMUM", N_("Maximum"), CTF_GENERAL, CTFA_SCALE) \
98 S(CTSF_MEAN, "MEAN", N_("Mean"), CTF_GENERAL, CTFA_SCALE) \
99 S(CTSF_MEDIAN, "MEDIAN", N_("Median"), CTF_GENERAL, CTFA_SCALE) \
100 S(CTSF_MINIMUM, "MINIMUM", N_("Minimum"), CTF_GENERAL, CTFA_SCALE) \
101 S(CTSF_MISSING, "MISSING", N_("Missing"), CTF_GENERAL, CTFA_SCALE) \
102 S(CTSF_MODE, "MODE", N_("Mode"), CTF_GENERAL, CTFA_SCALE) \
103 S(CTSF_PTILE, "PTILE", N_("Percentile"), CTF_GENERAL, CTFA_SCALE) \
104 S(CTSF_RANGE, "RANGE", N_("Range"), CTF_GENERAL, CTFA_SCALE) \
105 S(CTSF_SEMEAN, "SEMEAN", N_("Std Error of Mean"), CTF_GENERAL, CTFA_SCALE) \
106 S(CTSF_STDDEV, "STDDEV", N_("Std Deviation"), CTF_GENERAL, CTFA_SCALE) \
107 S(CTSF_SUM, "SUM", N_("Sum"), CTF_GENERAL, CTFA_SCALE) \
108 S(CSTF_TOTALN, "TOTALN", N_("Total N"), CTF_COUNT, CTFA_SCALE) \
109 S(CTSF_ETOTALN, "ETOTALN", N_("Adjusted Total N"), CTF_COUNT, CTFA_SCALE) \
110 S(CTSF_VALIDN, "VALIDN", N_("Valid N"), CTF_COUNT, CTFA_SCALE) \
111 S(CTSF_EVALIDN, "EVALIDN", N_("Adjusted Valid N"), CTF_COUNT, CTFA_SCALE) \
112 S(CTSF_VARIANCE, "VARIANCE", N_("Variance"), CTF_GENERAL, CTFA_SCALE) \
113 S(CTSF_ROWPCT_SUM, "ROWPCT.SUM", N_("Row Sum %"), CTF_PERCENT, CTFA_SCALE) \
114 S(CTSF_COLPCT_SUM, "COLPCT.SUM", N_("Column Sum %"), CTF_PERCENT, CTFA_SCALE) \
115 S(CTSF_TABLEPCT_SUM, "TABLEPCT.SUM", N_("Table Sum %"), CTF_PERCENT, CTFA_SCALE) \
116 S(CTSF_SUBTABLEPCT_SUM, "SUBTABLEPCT.SUM", N_("Subtable Sum %"), CTF_PERCENT, CTFA_SCALE) \
117 S(CTSF_LAYERPCT_SUM, "LAYERPCT.SUM", N_("Layer Sum %"), CTF_PERCENT, CTFA_SCALE) \
118 S(CTSF_LAYERROWPCT_SUM, "LAYERROWPCT.SUM", N_("Layer Row Sum %"), CTF_PERCENT, CTFA_SCALE) \
119 S(CTSF_LAYERCOLPCT_SUM, "LAYERCOLPCT.SUM", N_("Layer Column Sum %"), CTF_PERCENT, CTFA_SCALE) \
121 #if 0 /* Multiple response sets not yet implemented. */
122 S(CTSF_RESPONSES, "RESPONSES", N_("Responses"), CTF_COUNT, CTFA_MRSETS) \
123 S(CTSF_ROWPCT_RESPONSES, "ROWPCT.RESPONSES", N_("Row Responses %"), CTF_PERCENT, CTFA_MRSETS) \
124 S(CTSF_COLPCT_RESPONSES, "COLPCT.RESPONSES", N_("Column Responses %"), CTF_PERCENT, CTFA_MRSETS) \
125 S(CTSF_TABLEPCT_RESPONSES, "TABLEPCT.RESPONSES", N_("Table Responses %"), CTF_PERCENT, CTFA_MRSETS) \
126 S(CTSF_SUBTABLEPCT_RESPONSES, "SUBTABLEPCT.RESPONSES", N_("Subtable Responses %"), CTF_PERCENT, CTFA_MRSETS) \
127 S(CTSF_LAYERPCT_RESPONSES, "LAYERPCT.RESPONSES", N_("Layer Responses %"), CTF_PERCENT, CTFA_MRSETS) \
128 S(CTSF_LAYERROWPCT_RESPONSES, "LAYERROWPCT.RESPONSES", N_("Layer Row Responses %"), CTF_PERCENT, CTFA_MRSETS) \
129 S(CTSF_LAYERCOLPCT_RESPONSES, "LAYERCOLPCT.RESPONSES", N_("Layer Column Responses %"), CTF_PERCENT, CTFA_MRSETS) \
130 S(CTSF_ROWPCT_RESPONSES_COUNT, "ROWPCT.RESPONSES.COUNT", N_("Row Responses % (Base: Count)"), CTF_PERCENT, CTFA_MRSETS) \
131 S(CTSF_COLPCT_RESPONSES_COUNT, "COLPCT.RESPONSES.COUNT", N_("Column Responses % (Base: Count)"), CTF_PERCENT, CTFA_MRSETS) \
132 S(CTSF_TABLEPCT_RESPONSES_COUNT, "TABLEPCT.RESPONSES.COUNT", N_("Table Responses % (Base: Count)"), CTF_PERCENT, CTFA_MRSETS) \
133 S(CTSF_SUBTABLEPCT_RESPONSES_COUNT, "SUBTABLEPCT.RESPONSES.COUNT", N_("Subtable Responses % (Base: Count)"), CTF_PERCENT, CTFA_MRSETS) \
134 S(CTSF_LAYERPCT_RESPONSES_COUNT, "LAYERPCT.RESPONSES.COUNT", N_("Layer Responses % (Base: Count)"), CTF_PERCENT, CTFA_MRSETS) \
135 S(CTSF_LAYERROWPCT_RESPONSES_COUNT, "LAYERROWPCT.RESPONSES.COUNT", N_("Layer Row Responses % (Base: Count)"), CTF_PERCENT, CTFA_MRSETS) \
136 S(CTSF_LAYERCOLPCT_RESPONSES_COUNT, "LAYERCOLPCT.RESPONSES.COUNT", N_("Layer Column Responses % (Base: Count)"), CTF_PERCENT, CTFA_MRSETS) \
137 S(CTSF_ROWPCT_COUNT_RESPONSES, "ROWPCT.COUNT.RESPONSES", N_("Row Count % (Base: Responses)"), CTF_PERCENT, CTFA_MRSETS) \
138 S(CTSF_COLPCT_COUNT_RESPONSES, "COLPCT.COUNT.RESPONSES", N_("Column Count % (Base: Responses)"), CTF_PERCENT, CTFA_MRSETS) \
139 S(CTSF_TABLEPCT_COUNT_RESPONSES, "TABLEPCT.COUNT.RESPONSES", N_("Table Count % (Base: Responses)"), CTF_PERCENT, CTFA_MRSETS) \
140 S(CTSF_SUBTABLEPCT_COUNT_RESPONSES, "SUBTABLEPCT.COUNT.RESPONSES", N_("Subtable Count % (Base: Responses)"), CTF_PERCENT, CTFA_MRSETS) \
141 S(CTSF_LAYERPCT_COUNT_RESPONSES, "LAYERPCT.COUNT.RESPONSES", N_("Layer Count % (Base: Responses)"), CTF_PERCENT, CTFA_MRSETS) \
142 S(CTSF_LAYERROWPCT_COUNT_RESPONSES, "LAYERROWPCT.COUNT.RESPONSES", N_("Layer Row Count % (Base: Responses)"), CTF_PERCENT, CTFA_MRSETS) \
143 S(CTSF_LAYERCOLPCT_COUNT_RESPONSES, "LAYERCOLPCT.RESPONSES.COUNT", N_("Layer Column Count % (Base: Responses)"), CTF_PERCENT, CTFA_MRSETS)
146 enum ctables_summary_function
148 #define S(ENUM, NAME, LABEL, FORMAT, AVAILABILITY) ENUM,
154 #define S(ENUM, NAME, LABEL, FORMAT, AVAILABILITY) +1
155 N_CTSF_FUNCTIONS = SUMMARIES
159 static bool ctables_summary_function_is_count (enum ctables_summary_function);
161 enum ctables_domain_type
163 /* Within a section, where stacked variables divide one section from
165 CTDT_TABLE, /* All layers of a whole section. */
166 CTDT_LAYER, /* One layer within a section. */
167 CTDT_LAYERROW, /* Row in one layer within a section. */
168 CTDT_LAYERCOL, /* Column in one layer within a section. */
170 /* Within a subtable, where a subtable pairs an innermost row variable with
171 an innermost column variable within a single layer. */
172 CTDT_SUBTABLE, /* Whole subtable. */
173 CTDT_ROW, /* Row within a subtable. */
174 CTDT_COL, /* Column within a subtable. */
178 struct ctables_domain
180 struct hmap_node node;
182 const struct ctables_cell *example;
184 double d_valid; /* Dictionary weight. */
187 double e_valid; /* Effective weight */
192 enum ctables_summary_variant
201 /* In struct ctables_section's 'cells' hmap. Indexed by all the values in
202 all the axes (except the scalar variable, if any). */
203 struct hmap_node node;
205 /* The domains that contain this cell. */
206 uint32_t omit_domains;
207 struct ctables_domain *domains[N_CTDTS];
212 enum ctables_summary_variant sv;
214 struct ctables_cell_axis
216 struct ctables_cell_value
218 const struct ctables_category *category;
226 union ctables_summary *summaries;
233 const struct dictionary *dict;
234 struct pivot_table_look *look;
236 /* CTABLES has a number of extra formats that we implement via custom
237 currency specifications on an alternate fmt_settings. */
238 #define CTEF_NEGPAREN FMT_CCA
239 #define CTEF_NEQUAL FMT_CCB
240 #define CTEF_PAREN FMT_CCC
241 #define CTEF_PCTPAREN FMT_CCD
242 struct fmt_settings ctables_formats;
244 /* If this is NULL, zeros are displayed using the normal print format.
245 Otherwise, this string is displayed. */
248 /* If this is NULL, missing values are displayed using the normal print
249 format. Otherwise, this string is displayed. */
252 /* Indexed by variable dictionary index. */
253 enum ctables_vlabel *vlabels;
255 struct hmap postcomputes; /* Contains "struct ctables_postcompute"s. */
257 bool mrsets_count_duplicates; /* MRSETS. */
258 bool smissing_listwise; /* SMISSING. */
259 struct variable *e_weight; /* WEIGHT. */
260 int hide_threshold; /* HIDESMALLCOUNTS. */
262 struct ctables_table **tables;
266 static struct ctables_postcompute *ctables_find_postcompute (struct ctables *,
269 struct ctables_postcompute
271 struct hmap_node hmap_node; /* In struct ctables's 'pcompute' hmap. */
272 char *name; /* Name, without leading &. */
274 struct msg_location *location; /* Location of definition. */
275 struct ctables_pcexpr *expr;
277 struct ctables_summary_spec_set *specs;
278 bool hide_source_cats;
281 struct ctables_pcexpr
291 enum ctables_postcompute_op
294 CTPO_CONSTANT, /* 5 */
295 CTPO_CAT_NUMBER, /* [5] */
296 CTPO_CAT_STRING, /* ["STRING"] */
297 CTPO_CAT_RANGE, /* [LO THRU 5] */
298 CTPO_CAT_MISSING, /* MISSING */
299 CTPO_CAT_OTHERNM, /* OTHERNM */
300 CTPO_CAT_SUBTOTAL, /* SUBTOTAL */
301 CTPO_CAT_TOTAL, /* TOTAL */
315 /* CTPO_CAT_NUMBER. */
318 /* CTPO_CAT_STRING, in dictionary encoding. */
319 struct substring string;
321 /* CTPO_CAT_RANGE. */
324 /* CTPO_CAT_SUBTOTAL. */
325 size_t subtotal_index;
327 /* Two elements: CTPO_ADD, CTPO_SUB, CTPO_MUL, CTPO_DIV, CTPO_POW.
328 One element: CTPO_NEG. */
329 struct ctables_pcexpr *subs[2];
332 /* Source location. */
333 struct msg_location *location;
336 static void ctables_pcexpr_destroy (struct ctables_pcexpr *);
337 static struct ctables_pcexpr *ctables_pcexpr_allocate_binary (
338 enum ctables_postcompute_op, struct ctables_pcexpr *sub0,
339 struct ctables_pcexpr *sub1);
341 struct ctables_summary_spec_set
343 struct ctables_summary_spec *specs;
347 /* The variable to which the summary specs are applied. */
348 struct variable *var;
350 /* Whether the variable to which the summary specs are applied is a scale
351 variable for the purpose of summarization.
353 (VALIDN and TOTALN act differently for summarizing scale and categorical
357 /* If any of these optional additional scale variables are missing, then
358 treat 'var' as if it's missing too. This is for implementing
359 SMISSING=LISTWISE. */
360 struct variable **listwise_vars;
361 size_t n_listwise_vars;
364 static void ctables_summary_spec_set_clone (struct ctables_summary_spec_set *,
365 const struct ctables_summary_spec_set *);
366 static void ctables_summary_spec_set_uninit (struct ctables_summary_spec_set *);
368 /* A nested sequence of variables, e.g. a > b > c. */
371 struct variable **vars;
374 size_t *domains[N_CTDTS];
375 size_t n_domains[N_CTDTS];
378 struct ctables_summary_spec_set specs[N_CSVS];
381 /* A stack of nestings, e.g. nest1 + nest2 + ... + nestN. */
384 struct ctables_nest *nests;
390 struct hmap_node node;
395 struct ctables_occurrence
397 struct hmap_node node;
401 struct ctables_section
403 struct ctables_table *table;
404 struct ctables_nest *nests[PIVOT_N_AXES];
405 struct hmap *occurrences[PIVOT_N_AXES];
406 struct hmap cells; /* Contains "struct ctable_cell"s. */
407 struct hmap domains[N_CTDTS]; /* Contains "struct ctable_domain"s. */
412 struct ctables *ctables;
413 struct ctables_axis *axes[PIVOT_N_AXES];
414 struct ctables_stack stacks[PIVOT_N_AXES];
415 struct ctables_section *sections;
417 enum pivot_axis_type summary_axis;
418 struct ctables_summary_spec_set summary_specs;
420 const struct variable *clabels_example;
421 struct hmap clabels_values_map;
422 struct ctables_value **clabels_values;
423 size_t n_clabels_values;
425 enum pivot_axis_type slabels_axis;
426 bool slabels_visible;
428 /* The innermost category labels for axis 'a' appear on axis label_axis[a].
430 Most commonly, label_axis[a] == a, and in particular we always have
431 label_axis{PIVOT_AXIS_LAYER] == PIVOT_AXIS_LAYER.
433 If ROWLABELS or COLLABELS is specified, then one of
434 label_axis[PIVOT_AXIS_ROW] or label_axis[PIVOT_AXIS_COLUMN] can be the
435 opposite axis or PIVOT_AXIS_LAYER. Only one of them will differ.
437 enum pivot_axis_type label_axis[PIVOT_N_AXES];
438 enum pivot_axis_type clabels_from_axis;
440 /* Indexed by variable dictionary index. */
441 struct ctables_categories **categories;
450 struct ctables_chisq *chisq;
451 struct ctables_pairwise *pairwise;
454 struct ctables_categories
457 struct ctables_category *cats;
462 struct ctables_category
464 enum ctables_category_type
466 /* Explicit category lists. */
469 CCT_NRANGE, /* Numerical range. */
470 CCT_SRANGE, /* String range. */
475 /* Totals and subtotals. */
479 /* Implicit category lists. */
484 /* For contributing to TOTALN. */
485 CCT_EXCLUDED_MISSING,
489 struct ctables_category *subtotal;
495 double number; /* CCT_NUMBER. */
496 struct substring string; /* CCT_STRING, in dictionary encoding. */
497 double nrange[2]; /* CCT_NRANGE. */
498 struct substring srange[2]; /* CCT_SRANGE. */
502 char *total_label; /* CCT_SUBTOTAL, CCT_TOTAL. */
503 bool hide_subcategories; /* CCT_SUBTOTAL. */
506 const struct ctables_postcompute *pc; /* CCT_POSTCOMPUTE. */
508 /* CCT_VALUE, CCT_LABEL, CCT_FUNCTION. */
511 bool include_missing;
515 enum ctables_summary_function sort_function;
516 struct variable *sort_var;
521 /* Source location. This is null for CCT_TOTAL, CCT_VALUE, CCT_LABEL,
522 CCT_FUNCTION, CCT_EXCLUDED_MISSING. */
523 struct msg_location *location;
527 ctables_category_uninit (struct ctables_category *cat)
538 case CCT_POSTCOMPUTE:
542 ss_dealloc (&cat->string);
546 ss_dealloc (&cat->srange[0]);
547 ss_dealloc (&cat->srange[1]);
552 free (cat->total_label);
560 case CCT_EXCLUDED_MISSING:
566 nullable_substring_equal (const struct substring *a,
567 const struct substring *b)
569 return !a->string ? !b->string : b->string && ss_equals (*a, *b);
573 ctables_category_equal (const struct ctables_category *a,
574 const struct ctables_category *b)
576 if (a->type != b->type)
582 return a->number == b->number;
585 return ss_equals (a->string, b->string);
588 return a->nrange[0] == b->nrange[0] && a->nrange[1] == b->nrange[1];
591 return (nullable_substring_equal (&a->srange[0], &b->srange[0])
592 && nullable_substring_equal (&a->srange[1], &b->srange[1]));
598 case CCT_POSTCOMPUTE:
599 return a->pc == b->pc;
603 return !strcmp (a->total_label, b->total_label);
608 return (a->include_missing == b->include_missing
609 && a->sort_ascending == b->sort_ascending
610 && a->sort_function == b->sort_function
611 && a->sort_var == b->sort_var
612 && a->percentile == b->percentile);
614 case CCT_EXCLUDED_MISSING:
622 ctables_categories_unref (struct ctables_categories *c)
627 assert (c->n_refs > 0);
631 for (size_t i = 0; i < c->n_cats; i++)
632 ctables_category_uninit (&c->cats[i]);
638 ctables_categories_equal (const struct ctables_categories *a,
639 const struct ctables_categories *b)
641 if (a->n_cats != b->n_cats || a->show_empty != b->show_empty)
644 for (size_t i = 0; i < a->n_cats; i++)
645 if (!ctables_category_equal (&a->cats[i], &b->cats[i]))
651 /* Chi-square test (SIGTEST). */
659 /* Pairwise comparison test (COMPARETEST). */
660 struct ctables_pairwise
662 enum { PROP, MEAN } type;
665 bool meansvariance_allcats;
667 enum { BONFERRONI = 1, BH } adjust;
691 struct variable *var;
693 struct ctables_summary_spec_set specs[N_CSVS];
697 struct ctables_axis *subs[2];
700 struct msg_location *loc;
703 static void ctables_axis_destroy (struct ctables_axis *);
712 enum ctables_function_availability
714 CTFA_ALL, /* Any variables. */
715 CTFA_SCALE, /* Only scale variables, totals, and subtotals. */
716 CTFA_MRSETS, /* Only multiple-response sets */
719 struct ctables_summary_spec
721 enum ctables_summary_function function;
722 double percentile; /* CTSF_PTILE only. */
725 struct fmt_spec format;
726 bool is_ctables_format; /* Is 'format' one of CTEF_*? */
732 ctables_summary_spec_clone (struct ctables_summary_spec *dst,
733 const struct ctables_summary_spec *src)
736 dst->label = xstrdup_if_nonnull (src->label);
740 ctables_summary_spec_uninit (struct ctables_summary_spec *s)
747 ctables_summary_spec_set_clone (struct ctables_summary_spec_set *dst,
748 const struct ctables_summary_spec_set *src)
750 struct ctables_summary_spec *specs = xnmalloc (src->n, sizeof *specs);
751 for (size_t i = 0; i < src->n; i++)
752 ctables_summary_spec_clone (&specs[i], &src->specs[i]);
754 *dst = (struct ctables_summary_spec_set) {
759 .is_scale = src->is_scale,
764 ctables_summary_spec_set_uninit (struct ctables_summary_spec_set *set)
766 for (size_t i = 0; i < set->n; i++)
767 ctables_summary_spec_uninit (&set->specs[i]);
772 parse_col_width (struct lexer *lexer, const char *name, double *width)
774 lex_match (lexer, T_EQUALS);
775 if (lex_match_id (lexer, "DEFAULT"))
777 else if (lex_force_num_range_closed (lexer, name, 0, DBL_MAX))
779 *width = lex_number (lexer);
789 parse_bool (struct lexer *lexer, bool *b)
791 if (lex_match_id (lexer, "NO"))
793 else if (lex_match_id (lexer, "YES"))
797 lex_error_expecting (lexer, "YES", "NO");
803 static enum ctables_function_availability
804 ctables_function_availability (enum ctables_summary_function f)
806 static enum ctables_function_availability availability[] = {
807 #define S(ENUM, NAME, LABEL, FORMAT, AVAILABILITY) [ENUM] = AVAILABILITY,
812 return availability[f];
816 ctables_summary_function_is_count (enum ctables_summary_function f)
822 case CTSF_ROWPCT_COUNT:
823 case CTSF_COLPCT_COUNT:
824 case CTSF_TABLEPCT_COUNT:
825 case CTSF_SUBTABLEPCT_COUNT:
826 case CTSF_LAYERPCT_COUNT:
827 case CTSF_LAYERROWPCT_COUNT:
828 case CTSF_LAYERCOLPCT_COUNT:
831 case CTSF_ROWPCT_VALIDN:
832 case CTSF_COLPCT_VALIDN:
833 case CTSF_TABLEPCT_VALIDN:
834 case CTSF_SUBTABLEPCT_VALIDN:
835 case CTSF_LAYERPCT_VALIDN:
836 case CTSF_LAYERROWPCT_VALIDN:
837 case CTSF_LAYERCOLPCT_VALIDN:
838 case CTSF_ROWPCT_TOTALN:
839 case CTSF_COLPCT_TOTALN:
840 case CTSF_TABLEPCT_TOTALN:
841 case CTSF_SUBTABLEPCT_TOTALN:
842 case CTSF_LAYERPCT_TOTALN:
843 case CTSF_LAYERROWPCT_TOTALN:
844 case CTSF_LAYERCOLPCT_TOTALN:
861 case CTSF_ROWPCT_SUM:
862 case CTSF_COLPCT_SUM:
863 case CTSF_TABLEPCT_SUM:
864 case CTSF_SUBTABLEPCT_SUM:
865 case CTSF_LAYERPCT_SUM:
866 case CTSF_LAYERROWPCT_SUM:
867 case CTSF_LAYERCOLPCT_SUM:
875 parse_ctables_summary_function (struct lexer *lexer,
876 enum ctables_summary_function *f)
880 enum ctables_summary_function function;
881 struct substring name;
883 static struct pair names[] = {
884 #define S(ENUM, NAME, LABEL, FORMAT, AVAILABILITY) \
885 { ENUM, SS_LITERAL_INITIALIZER (NAME) },
888 /* The .COUNT suffix may be omitted. */
889 S(CTSF_ROWPCT_COUNT, "ROWPCT", _, _, _)
890 S(CTSF_COLPCT_COUNT, "COLPCT", _, _, _)
891 S(CTSF_TABLEPCT_COUNT, "TABLEPCT", _, _, _)
892 S(CTSF_SUBTABLEPCT_COUNT, "SUBTABLEPCT", _, _, _)
893 S(CTSF_LAYERPCT_COUNT, "LAYERPCT", _, _, _)
894 S(CTSF_LAYERROWPCT_COUNT, "LAYERROWPCT", _, _, _)
895 S(CTSF_LAYERCOLPCT_COUNT, "LAYERCOLPCT", _, _, _)
899 if (!lex_force_id (lexer))
902 for (size_t i = 0; i < sizeof names / sizeof *names; i++)
903 if (ss_equals_case (names[i].name, lex_tokss (lexer)))
905 *f = names[i].function;
910 lex_error (lexer, _("Expecting summary function name."));
915 ctables_axis_destroy (struct ctables_axis *axis)
923 for (size_t i = 0; i < N_CSVS; i++)
924 ctables_summary_spec_set_uninit (&axis->specs[i]);
929 ctables_axis_destroy (axis->subs[0]);
930 ctables_axis_destroy (axis->subs[1]);
933 msg_location_destroy (axis->loc);
937 static struct ctables_axis *
938 ctables_axis_new_nonterminal (enum ctables_axis_op op,
939 struct ctables_axis *sub0,
940 struct ctables_axis *sub1,
941 struct lexer *lexer, int start_ofs)
943 struct ctables_axis *axis = xmalloc (sizeof *axis);
944 *axis = (struct ctables_axis) {
946 .subs = { sub0, sub1 },
947 .loc = lex_ofs_location (lexer, start_ofs, lex_ofs (lexer) - 1),
952 struct ctables_axis_parse_ctx
955 struct dictionary *dict;
957 struct ctables_table *t;
960 static struct fmt_spec
961 ctables_summary_default_format (enum ctables_summary_function function,
962 const struct variable *var)
964 static const enum ctables_format default_formats[] = {
965 #define S(ENUM, NAME, LABEL, FORMAT, AVAILABILITY) [ENUM] = FORMAT,
969 switch (default_formats[function])
972 return (struct fmt_spec) { .type = FMT_F, .w = 40 };
975 return (struct fmt_spec) { .type = FMT_PCT, .w = 40, .d = 1 };
978 return *var_get_print_format (var);
986 ctables_summary_default_label (enum ctables_summary_function function,
989 static const char *default_labels[] = {
990 #define S(ENUM, NAME, LABEL, FORMAT, AVAILABILITY) [ENUM] = LABEL,
995 return (function == CTSF_PTILE
996 ? xasprintf (_("Percentile %.2f"), percentile)
997 : xstrdup (gettext (default_labels[function])));
1001 ctables_summary_function_name (enum ctables_summary_function function)
1003 static const char *names[] = {
1004 #define S(ENUM, NAME, LABEL, FORMAT, AVAILABILITY) [ENUM] = NAME,
1008 return names[function];
1012 add_summary_spec (struct ctables_axis *axis,
1013 enum ctables_summary_function function, double percentile,
1014 const char *label, const struct fmt_spec *format,
1015 bool is_ctables_format, const struct msg_location *loc,
1016 enum ctables_summary_variant sv)
1018 if (axis->op == CTAO_VAR)
1020 const char *function_name = ctables_summary_function_name (function);
1021 const char *var_name = var_get_name (axis->var);
1022 switch (ctables_function_availability (function))
1025 msg_at (SE, loc, _("Summary function %s applies only to multiple "
1026 "response sets."), function_name);
1027 msg_at (SN, axis->loc, _("'%s' is not a multiple response set."),
1036 _("Summary function %s applies only to scale variables."),
1038 msg_at (SN, axis->loc, _("'%s' is not a scale variable."),
1049 struct ctables_summary_spec_set *set = &axis->specs[sv];
1050 if (set->n >= set->allocated)
1051 set->specs = x2nrealloc (set->specs, &set->allocated,
1052 sizeof *set->specs);
1054 struct ctables_summary_spec *dst = &set->specs[set->n++];
1055 *dst = (struct ctables_summary_spec) {
1056 .function = function,
1057 .percentile = percentile,
1058 .label = xstrdup (label),
1059 .format = (format ? *format
1060 : ctables_summary_default_format (function, axis->var)),
1061 .is_ctables_format = is_ctables_format,
1067 for (size_t i = 0; i < 2; i++)
1068 if (!add_summary_spec (axis->subs[i], function, percentile, label,
1069 format, is_ctables_format, loc, sv))
1075 static struct ctables_axis *ctables_axis_parse_stack (
1076 struct ctables_axis_parse_ctx *);
1079 static struct ctables_axis *
1080 ctables_axis_parse_primary (struct ctables_axis_parse_ctx *ctx)
1082 if (lex_match (ctx->lexer, T_LPAREN))
1084 struct ctables_axis *sub = ctables_axis_parse_stack (ctx);
1085 if (!sub || !lex_force_match (ctx->lexer, T_RPAREN))
1087 ctables_axis_destroy (sub);
1093 if (!lex_force_id (ctx->lexer))
1096 int start_ofs = lex_ofs (ctx->lexer);
1097 struct variable *var = parse_variable (ctx->lexer, ctx->dict);
1101 struct ctables_axis *axis = xmalloc (sizeof *axis);
1102 *axis = (struct ctables_axis) { .op = CTAO_VAR, .var = var };
1104 /* XXX should figure out default measures by reading data */
1105 axis->scale = (lex_match_phrase (ctx->lexer, "[S]") ? true
1106 : lex_match_phrase (ctx->lexer, "[C]") ? false
1107 : var_get_measure (var) == MEASURE_SCALE);
1108 axis->loc = lex_ofs_location (ctx->lexer, start_ofs,
1109 lex_ofs (ctx->lexer) - 1);
1110 if (axis->scale && var_is_alpha (var))
1112 msg_at (SE, axis->loc, _("Cannot use string variable %s as a scale "
1114 var_get_name (var));
1115 ctables_axis_destroy (axis);
1123 has_digit (const char *s)
1125 return s[strcspn (s, "0123456789")] != '\0';
1129 parse_ctables_format_specifier (struct lexer *lexer, struct fmt_spec *format,
1130 bool *is_ctables_format)
1132 char type[FMT_TYPE_LEN_MAX + 1];
1133 if (!parse_abstract_format_specifier__ (lexer, type, &format->w, &format->d))
1136 if (!strcasecmp (type, "NEGPAREN"))
1137 format->type = CTEF_NEGPAREN;
1138 else if (!strcasecmp (type, "NEQUAL"))
1139 format->type = CTEF_NEQUAL;
1140 else if (!strcasecmp (type, "PAREN"))
1141 format->type = CTEF_PAREN;
1142 else if (!strcasecmp (type, "PCTPAREN"))
1143 format->type = CTEF_PCTPAREN;
1146 *is_ctables_format = false;
1147 return (parse_format_specifier (lexer, format)
1148 && fmt_check_output (format)
1149 && fmt_check_type_compat (format, VAL_NUMERIC));
1154 msg (SE, _("Output format %s requires width 2 or greater."), type);
1157 else if (format->d > format->w - 1)
1159 msg (SE, _("Output format %s requires width greater than decimals."),
1165 *is_ctables_format = true;
1170 static struct ctables_axis *
1171 ctables_axis_parse_postfix (struct ctables_axis_parse_ctx *ctx)
1173 struct ctables_axis *sub = ctables_axis_parse_primary (ctx);
1174 if (!sub || !lex_match (ctx->lexer, T_LBRACK))
1177 enum ctables_summary_variant sv = CSV_CELL;
1180 int start_ofs = lex_ofs (ctx->lexer);
1182 /* Parse function. */
1183 enum ctables_summary_function function;
1184 if (!parse_ctables_summary_function (ctx->lexer, &function))
1187 /* Parse percentile. */
1188 double percentile = 0;
1189 if (function == CTSF_PTILE)
1191 if (!lex_force_num_range_closed (ctx->lexer, "PTILE", 0, 100))
1193 percentile = lex_number (ctx->lexer);
1194 lex_get (ctx->lexer);
1199 if (lex_is_string (ctx->lexer))
1201 label = ss_xstrdup (lex_tokss (ctx->lexer));
1202 lex_get (ctx->lexer);
1205 label = ctables_summary_default_label (function, percentile);
1208 struct fmt_spec format;
1209 const struct fmt_spec *formatp;
1210 bool is_ctables_format = false;
1211 if (lex_token (ctx->lexer) == T_ID
1212 && has_digit (lex_tokcstr (ctx->lexer)))
1214 if (!parse_ctables_format_specifier (ctx->lexer, &format,
1215 &is_ctables_format))
1225 struct msg_location *loc = lex_ofs_location (ctx->lexer, start_ofs,
1226 lex_ofs (ctx->lexer) - 1);
1227 add_summary_spec (sub, function, percentile, label, formatp,
1228 is_ctables_format, loc, sv);
1230 msg_location_destroy (loc);
1232 lex_match (ctx->lexer, T_COMMA);
1233 if (sv == CSV_CELL && lex_match_id (ctx->lexer, "TOTALS"))
1235 if (!lex_force_match (ctx->lexer, T_LBRACK))
1239 else if (lex_match (ctx->lexer, T_RBRACK))
1241 if (sv == CSV_TOTAL && !lex_force_match (ctx->lexer, T_RBRACK))
1248 ctables_axis_destroy (sub);
1252 static const struct ctables_axis *
1253 find_scale (const struct ctables_axis *axis)
1257 else if (axis->op == CTAO_VAR)
1258 return axis->scale ? axis : NULL;
1261 for (size_t i = 0; i < 2; i++)
1263 const struct ctables_axis *scale = find_scale (axis->subs[i]);
1271 static const struct ctables_axis *
1272 find_categorical_summary_spec (const struct ctables_axis *axis)
1276 else if (axis->op == CTAO_VAR)
1277 return !axis->scale && axis->specs[CSV_CELL].n ? axis : NULL;
1280 for (size_t i = 0; i < 2; i++)
1282 const struct ctables_axis *sum
1283 = find_categorical_summary_spec (axis->subs[i]);
1291 static struct ctables_axis *
1292 ctables_axis_parse_nest (struct ctables_axis_parse_ctx *ctx)
1294 int start_ofs = lex_ofs (ctx->lexer);
1295 struct ctables_axis *lhs = ctables_axis_parse_postfix (ctx);
1299 while (lex_match (ctx->lexer, T_GT))
1301 struct ctables_axis *rhs = ctables_axis_parse_postfix (ctx);
1305 struct ctables_axis *nest = ctables_axis_new_nonterminal (
1306 CTAO_NEST, lhs, rhs, ctx->lexer, start_ofs);
1308 const struct ctables_axis *outer_scale = find_scale (lhs);
1309 const struct ctables_axis *inner_scale = find_scale (rhs);
1310 if (outer_scale && inner_scale)
1312 msg_at (SE, nest->loc, _("Cannot nest scale variables."));
1313 msg_at (SN, outer_scale->loc, _("This is an outer scale variable."));
1314 msg_at (SN, inner_scale->loc, _("This is an inner scale variable."));
1315 ctables_axis_destroy (nest);
1319 const struct ctables_axis *outer_sum = find_categorical_summary_spec (lhs);
1322 msg_at (SE, nest->loc,
1323 _("Summaries may only be requested for categorical variables "
1324 "at the innermost nesting level."));
1325 msg_at (SN, outer_sum->loc,
1326 _("This outer categorical variable has a summary."));
1327 ctables_axis_destroy (nest);
1337 static struct ctables_axis *
1338 ctables_axis_parse_stack (struct ctables_axis_parse_ctx *ctx)
1340 int start_ofs = lex_ofs (ctx->lexer);
1341 struct ctables_axis *lhs = ctables_axis_parse_nest (ctx);
1345 while (lex_match (ctx->lexer, T_PLUS))
1347 struct ctables_axis *rhs = ctables_axis_parse_nest (ctx);
1351 lhs = ctables_axis_new_nonterminal (CTAO_STACK, lhs, rhs,
1352 ctx->lexer, start_ofs);
1359 ctables_axis_parse (struct lexer *lexer, struct dictionary *dict,
1360 struct ctables *ct, struct ctables_table *t,
1361 enum pivot_axis_type a)
1363 if (lex_token (lexer) == T_BY
1364 || lex_token (lexer) == T_SLASH
1365 || lex_token (lexer) == T_ENDCMD)
1368 struct ctables_axis_parse_ctx ctx = {
1374 t->axes[a] = ctables_axis_parse_stack (&ctx);
1375 return t->axes[a] != NULL;
1379 ctables_chisq_destroy (struct ctables_chisq *chisq)
1385 ctables_pairwise_destroy (struct ctables_pairwise *pairwise)
1391 ctables_table_destroy (struct ctables_table *t)
1396 for (size_t i = 0; i < t->n_categories; i++)
1397 ctables_categories_unref (t->categories[i]);
1398 free (t->categories);
1400 ctables_axis_destroy (t->axes[PIVOT_AXIS_COLUMN]);
1401 ctables_axis_destroy (t->axes[PIVOT_AXIS_ROW]);
1402 ctables_axis_destroy (t->axes[PIVOT_AXIS_LAYER]);
1406 ctables_chisq_destroy (t->chisq);
1407 ctables_pairwise_destroy (t->pairwise);
1412 ctables_destroy (struct ctables *ct)
1417 pivot_table_look_unref (ct->look);
1421 for (size_t i = 0; i < ct->n_tables; i++)
1422 ctables_table_destroy (ct->tables[i]);
1427 static struct ctables_category
1428 cct_nrange (double low, double high)
1430 return (struct ctables_category) {
1432 .nrange = { low, high }
1436 static struct ctables_category
1437 cct_srange (struct substring low, struct substring high)
1439 return (struct ctables_category) {
1441 .srange = { low, high }
1446 ctables_table_parse_subtotal (struct lexer *lexer, bool hide_subcategories,
1447 struct ctables_category *cat)
1450 if (lex_match (lexer, T_EQUALS))
1452 if (!lex_force_string (lexer))
1455 total_label = ss_xstrdup (lex_tokss (lexer));
1459 total_label = xstrdup (_("Subtotal"));
1461 *cat = (struct ctables_category) {
1462 .type = CCT_SUBTOTAL,
1463 .hide_subcategories = hide_subcategories,
1464 .total_label = total_label
1469 static struct substring
1470 parse_substring (struct lexer *lexer, struct dictionary *dict)
1472 struct substring s = recode_substring_pool (
1473 dict_get_encoding (dict), "UTF-8", lex_tokss (lexer), NULL);
1474 ss_rtrim (&s, ss_cstr (" "));
1480 ctables_table_parse_explicit_category (struct lexer *lexer,
1481 struct dictionary *dict,
1483 struct ctables_category *cat)
1485 if (lex_match_id (lexer, "OTHERNM"))
1486 *cat = (struct ctables_category) { .type = CCT_OTHERNM };
1487 else if (lex_match_id (lexer, "MISSING"))
1488 *cat = (struct ctables_category) { .type = CCT_MISSING };
1489 else if (lex_match_id (lexer, "SUBTOTAL"))
1490 return ctables_table_parse_subtotal (lexer, false, cat);
1491 else if (lex_match_id (lexer, "HSUBTOTAL"))
1492 return ctables_table_parse_subtotal (lexer, true, cat);
1493 else if (lex_match_id (lexer, "LO"))
1495 if (!lex_force_match_id (lexer, "THRU"))
1497 if (lex_is_string (lexer))
1499 struct substring sr0 = { .string = NULL };
1500 struct substring sr1 = parse_substring (lexer, dict);
1501 *cat = cct_srange (sr0, sr1);
1503 else if (lex_force_num (lexer))
1505 *cat = cct_nrange (-DBL_MAX, lex_number (lexer));
1511 else if (lex_is_number (lexer))
1513 double number = lex_number (lexer);
1515 if (lex_match_id (lexer, "THRU"))
1517 if (lex_match_id (lexer, "HI"))
1518 *cat = cct_nrange (number, DBL_MAX);
1521 if (!lex_force_num (lexer))
1523 *cat = cct_nrange (number, lex_number (lexer));
1528 *cat = (struct ctables_category) {
1533 else if (lex_is_string (lexer))
1535 struct substring s = parse_substring (lexer, dict);
1536 if (lex_match_id (lexer, "THRU"))
1538 if (lex_match_id (lexer, "HI"))
1540 struct substring sr1 = { .string = NULL };
1541 *cat = cct_srange (s, sr1);
1545 if (!lex_force_string (lexer))
1547 struct substring sr1 = parse_substring (lexer, dict);
1548 *cat = cct_srange (s, sr1);
1552 *cat = (struct ctables_category) { .type = CCT_STRING, .string = s };
1554 else if (lex_match (lexer, T_AND))
1556 if (!lex_force_id (lexer))
1558 struct ctables_postcompute *pc = ctables_find_postcompute (
1559 ct, lex_tokcstr (lexer));
1562 struct msg_location *loc = lex_get_location (lexer, -1, 0);
1563 msg_at (SE, loc, _("Unknown postcompute &%s."),
1564 lex_tokcstr (lexer));
1565 msg_location_destroy (loc);
1570 *cat = (struct ctables_category) { .type = CCT_POSTCOMPUTE, .pc = pc };
1574 lex_error (lexer, NULL);
1581 static struct ctables_category *
1582 ctables_find_category_for_postcompute (const struct ctables_categories *cats,
1583 const struct ctables_pcexpr *e)
1585 struct ctables_category *best = NULL;
1586 size_t n_subtotals = 0;
1587 for (size_t i = 0; i < cats->n_cats; i++)
1589 struct ctables_category *cat = &cats->cats[i];
1592 case CTPO_CAT_NUMBER:
1593 if (cat->type == CCT_NUMBER && cat->number == e->number)
1597 case CTPO_CAT_STRING:
1598 if (cat->type == CCT_STRING && ss_equals (cat->string, e->string))
1602 case CTPO_CAT_RANGE:
1603 if (cat->type == CCT_NRANGE
1604 && cat->nrange[0] == e->range[0]
1605 && cat->nrange[1] == e->range[1])
1609 case CTPO_CAT_MISSING:
1610 if (cat->type == CCT_MISSING)
1614 case CTPO_CAT_OTHERNM:
1615 if (cat->type == CCT_OTHERNM)
1619 case CTPO_CAT_SUBTOTAL:
1620 if (cat->type == CCT_SUBTOTAL)
1623 if (e->subtotal_index == n_subtotals)
1625 else if (e->subtotal_index == 0)
1630 case CTPO_CAT_TOTAL:
1631 if (cat->type == CCT_TOTAL)
1645 if (e->op == CTPO_CAT_SUBTOTAL && e->subtotal_index == 0 && n_subtotals > 1)
1651 ctables_recursive_check_postcompute (const struct ctables_pcexpr *e,
1652 struct ctables_category *pc_cat,
1653 const struct ctables_categories *cats,
1654 const struct msg_location *cats_location)
1658 case CTPO_CAT_NUMBER:
1659 case CTPO_CAT_STRING:
1660 case CTPO_CAT_RANGE:
1661 case CTPO_CAT_MISSING:
1662 case CTPO_CAT_OTHERNM:
1663 case CTPO_CAT_SUBTOTAL:
1664 case CTPO_CAT_TOTAL:
1666 struct ctables_category *cat = ctables_find_category_for_postcompute (
1670 if (e->op == CTPO_CAT_SUBTOTAL && e->subtotal_index == 0)
1672 size_t n_subtotals = 0;
1673 for (size_t i = 0; i < cats->n_cats; i++)
1674 n_subtotals += cats->cats[i].type == CCT_SUBTOTAL;
1675 if (n_subtotals > 1)
1677 msg_at (SE, cats_location,
1678 ngettext ("These categories include %zu instance "
1679 "of SUBTOTAL or HSUBTOTAL, so references "
1680 "from computed categories must refer to "
1681 "subtotals by position.",
1682 "These categories include %zu instances "
1683 "of SUBTOTAL or HSUBTOTAL, so references "
1684 "from computed categories must refer to "
1685 "subtotals by position.",
1688 msg_at (SN, e->location,
1689 _("This is the reference that lacks a position."));
1694 msg_at (SE, pc_cat->location,
1695 _("Computed category &%s references a category not included "
1696 "in the category list."),
1698 msg_at (SN, e->location, _("This is the missing category."));
1699 msg_at (SN, cats_location,
1700 _("To fix the problem, add the missing category to the "
1701 "list of categories here."));
1704 if (pc_cat->pc->hide_source_cats)
1718 for (size_t i = 0; i < 2; i++)
1719 if (e->subs[i] && !ctables_recursive_check_postcompute (
1720 e->subs[i], pc_cat, cats, cats_location))
1730 parse_category_string (const struct ctables_category *cat,
1731 struct substring s, struct dictionary *dict,
1732 enum fmt_type format, double *n)
1735 char *error = data_in (s, dict_get_encoding (dict), format,
1736 settings_get_fmt_settings (), &v, 0, NULL);
1739 msg_at (SE, cat->location,
1740 _("Failed to parse category specification as format %s: %s."),
1741 fmt_name (format), error);
1751 all_strings (struct variable **vars, size_t n_vars,
1752 const struct ctables_category *cat)
1754 for (size_t j = 0; j < n_vars; j++)
1755 if (var_is_numeric (vars[j]))
1757 msg_at (SE, cat->location,
1758 _("This category specification may be applied only to string "
1759 "variables, but this subcommand tries to apply it to "
1760 "numeric variable %s."),
1761 var_get_name (vars[j]));
1768 ctables_table_parse_categories (struct lexer *lexer, struct dictionary *dict,
1769 struct ctables *ct, struct ctables_table *t)
1771 if (!lex_match_id (lexer, "VARIABLES"))
1773 lex_match (lexer, T_EQUALS);
1775 struct variable **vars;
1777 if (!parse_variables (lexer, dict, &vars, &n_vars, PV_NO_SCRATCH))
1780 const struct fmt_spec *common_format = var_get_print_format (vars[0]);
1781 for (size_t i = 1; i < n_vars; i++)
1783 const struct fmt_spec *f = var_get_print_format (vars[i]);
1784 if (f->type != common_format->type)
1786 common_format = NULL;
1792 && (fmt_get_category (common_format->type)
1793 & (FMT_CAT_DATE | FMT_CAT_TIME | FMT_CAT_DATE_COMPONENT)));
1795 struct ctables_categories *c = xmalloc (sizeof *c);
1796 *c = (struct ctables_categories) { .n_refs = n_vars, .show_empty = true };
1797 for (size_t i = 0; i < n_vars; i++)
1799 struct ctables_categories **cp
1800 = &t->categories[var_get_dict_index (vars[i])];
1801 ctables_categories_unref (*cp);
1805 size_t allocated_cats = 0;
1806 if (lex_match (lexer, T_LBRACK))
1808 int cats_start_ofs = lex_ofs (lexer);
1811 if (c->n_cats >= allocated_cats)
1812 c->cats = x2nrealloc (c->cats, &allocated_cats, sizeof *c->cats);
1814 int start_ofs = lex_ofs (lexer);
1815 struct ctables_category *cat = &c->cats[c->n_cats];
1816 if (!ctables_table_parse_explicit_category (lexer, dict, ct, cat))
1818 cat->location = lex_ofs_location (lexer, start_ofs, lex_ofs (lexer) - 1);
1821 lex_match (lexer, T_COMMA);
1823 while (!lex_match (lexer, T_RBRACK));
1825 struct msg_location *cats_location
1826 = lex_ofs_location (lexer, cats_start_ofs, lex_ofs (lexer) - 1);
1827 for (size_t i = 0; i < c->n_cats; i++)
1829 struct ctables_category *cat = &c->cats[i];
1832 case CCT_POSTCOMPUTE:
1833 if (!ctables_recursive_check_postcompute (cat->pc->expr, cat,
1840 for (size_t j = 0; j < n_vars; j++)
1841 if (var_is_alpha (vars[j]))
1843 msg_at (SE, cat->location,
1844 _("This category specification may be applied "
1845 "only to numeric variables, but this "
1846 "subcommand tries to apply it to string "
1848 var_get_name (vars[j]));
1857 if (!parse_category_string (cat, cat->string, dict,
1858 common_format->type, &n))
1861 ss_dealloc (&cat->string);
1863 cat->type = CCT_NUMBER;
1866 else if (!all_strings (vars, n_vars, cat))
1875 if (!cat->srange[0].string)
1877 else if (!parse_category_string (cat, cat->srange[0], dict,
1878 common_format->type, &n[0]))
1881 if (!cat->srange[1].string)
1883 else if (!parse_category_string (cat, cat->srange[1], dict,
1884 common_format->type, &n[1]))
1887 ss_dealloc (&cat->srange[0]);
1888 ss_dealloc (&cat->srange[1]);
1890 cat->type = CCT_NRANGE;
1891 cat->nrange[0] = n[0];
1892 cat->nrange[1] = n[1];
1894 else if (!all_strings (vars, n_vars, cat))
1905 case CCT_EXCLUDED_MISSING:
1911 struct ctables_category cat = {
1913 .include_missing = false,
1914 .sort_ascending = true,
1916 bool show_totals = false;
1917 char *total_label = NULL;
1918 bool totals_before = false;
1919 while (lex_token (lexer) != T_SLASH && lex_token (lexer) != T_ENDCMD)
1921 if (!c->n_cats && lex_match_id (lexer, "ORDER"))
1923 lex_match (lexer, T_EQUALS);
1924 if (lex_match_id (lexer, "A"))
1925 cat.sort_ascending = true;
1926 else if (lex_match_id (lexer, "D"))
1927 cat.sort_ascending = false;
1930 lex_error_expecting (lexer, "A", "D");
1934 else if (!c->n_cats && lex_match_id (lexer, "KEY"))
1936 lex_match (lexer, T_EQUALS);
1937 if (lex_match_id (lexer, "VALUE"))
1938 cat.type = CCT_VALUE;
1939 else if (lex_match_id (lexer, "LABEL"))
1940 cat.type = CCT_LABEL;
1943 cat.type = CCT_FUNCTION;
1944 if (!parse_ctables_summary_function (lexer, &cat.sort_function))
1947 if (lex_match (lexer, T_LPAREN))
1949 cat.sort_var = parse_variable (lexer, dict);
1953 if (cat.sort_function == CTSF_PTILE)
1955 lex_match (lexer, T_COMMA);
1956 if (!lex_force_num_range_closed (lexer, "PTILE", 0, 100))
1958 cat.percentile = lex_number (lexer);
1962 if (!lex_force_match (lexer, T_RPAREN))
1965 else if (ctables_function_availability (cat.sort_function)
1968 bool UNUSED b = lex_force_match (lexer, T_LPAREN);
1973 else if (!c->n_cats && lex_match_id (lexer, "MISSING"))
1975 lex_match (lexer, T_EQUALS);
1976 if (lex_match_id (lexer, "INCLUDE"))
1977 cat.include_missing = true;
1978 else if (lex_match_id (lexer, "EXCLUDE"))
1979 cat.include_missing = false;
1982 lex_error_expecting (lexer, "INCLUDE", "EXCLUDE");
1986 else if (lex_match_id (lexer, "TOTAL"))
1988 lex_match (lexer, T_EQUALS);
1989 if (!parse_bool (lexer, &show_totals))
1992 else if (lex_match_id (lexer, "LABEL"))
1994 lex_match (lexer, T_EQUALS);
1995 if (!lex_force_string (lexer))
1998 total_label = ss_xstrdup (lex_tokss (lexer));
2001 else if (lex_match_id (lexer, "POSITION"))
2003 lex_match (lexer, T_EQUALS);
2004 if (lex_match_id (lexer, "BEFORE"))
2005 totals_before = true;
2006 else if (lex_match_id (lexer, "AFTER"))
2007 totals_before = false;
2010 lex_error_expecting (lexer, "BEFORE", "AFTER");
2014 else if (lex_match_id (lexer, "EMPTY"))
2016 lex_match (lexer, T_EQUALS);
2017 if (lex_match_id (lexer, "INCLUDE"))
2018 c->show_empty = true;
2019 else if (lex_match_id (lexer, "EXCLUDE"))
2020 c->show_empty = false;
2023 lex_error_expecting (lexer, "INCLUDE", "EXCLUDE");
2030 lex_error_expecting (lexer, "ORDER", "KEY", "MISSING",
2031 "TOTAL", "LABEL", "POSITION", "EMPTY");
2033 lex_error_expecting (lexer, "TOTAL", "LABEL", "POSITION", "EMPTY");
2040 if (c->n_cats >= allocated_cats)
2041 c->cats = x2nrealloc (c->cats, &allocated_cats, sizeof *c->cats);
2042 c->cats[c->n_cats++] = cat;
2047 if (c->n_cats >= allocated_cats)
2048 c->cats = x2nrealloc (c->cats, &allocated_cats, sizeof *c->cats);
2050 struct ctables_category *totals;
2053 insert_element (c->cats, c->n_cats, sizeof *c->cats, 0);
2054 totals = &c->cats[0];
2057 totals = &c->cats[c->n_cats];
2060 *totals = (struct ctables_category) {
2062 .total_label = total_label ? total_label : xstrdup (_("Total")),
2066 struct ctables_category *subtotal = NULL;
2067 for (size_t i = totals_before ? 0 : c->n_cats;
2068 totals_before ? i < c->n_cats : i-- > 0;
2069 totals_before ? i++ : 0)
2071 struct ctables_category *cat = &c->cats[i];
2080 cat->subtotal = subtotal;
2083 case CCT_POSTCOMPUTE:
2094 case CCT_EXCLUDED_MISSING:
2103 ctables_nest_uninit (struct ctables_nest *nest)
2110 ctables_stack_uninit (struct ctables_stack *stack)
2114 for (size_t i = 0; i < stack->n; i++)
2115 ctables_nest_uninit (&stack->nests[i]);
2116 free (stack->nests);
2120 static struct ctables_stack
2121 nest_fts (struct ctables_stack s0, struct ctables_stack s1)
2128 struct ctables_stack stack = { .nests = xnmalloc (s0.n, s1.n * sizeof *stack.nests) };
2129 for (size_t i = 0; i < s0.n; i++)
2130 for (size_t j = 0; j < s1.n; j++)
2132 const struct ctables_nest *a = &s0.nests[i];
2133 const struct ctables_nest *b = &s1.nests[j];
2135 size_t allocate = a->n + b->n;
2136 struct variable **vars = xnmalloc (allocate, sizeof *vars);
2137 enum pivot_axis_type *axes = xnmalloc (allocate, sizeof *axes);
2139 for (size_t k = 0; k < a->n; k++)
2140 vars[n++] = a->vars[k];
2141 for (size_t k = 0; k < b->n; k++)
2142 vars[n++] = b->vars[k];
2143 assert (n == allocate);
2145 const struct ctables_nest *summary_src;
2146 if (!a->specs[CSV_CELL].var)
2148 else if (!b->specs[CSV_CELL].var)
2153 struct ctables_nest *new = &stack.nests[stack.n++];
2154 *new = (struct ctables_nest) {
2156 .scale_idx = (a->scale_idx != SIZE_MAX ? a->scale_idx
2157 : b->scale_idx != SIZE_MAX ? a->n + b->scale_idx
2161 for (enum ctables_summary_variant sv = 0; sv < N_CSVS; sv++)
2162 ctables_summary_spec_set_clone (&new->specs[sv], &summary_src->specs[sv]);
2164 ctables_stack_uninit (&s0);
2165 ctables_stack_uninit (&s1);
2169 static struct ctables_stack
2170 stack_fts (struct ctables_stack s0, struct ctables_stack s1)
2172 struct ctables_stack stack = { .nests = xnmalloc (s0.n + s1.n, sizeof *stack.nests) };
2173 for (size_t i = 0; i < s0.n; i++)
2174 stack.nests[stack.n++] = s0.nests[i];
2175 for (size_t i = 0; i < s1.n; i++)
2177 stack.nests[stack.n] = s1.nests[i];
2178 stack.nests[stack.n].group_head += s0.n;
2181 assert (stack.n == s0.n + s1.n);
2187 static struct ctables_stack
2188 var_fts (const struct ctables_axis *a)
2190 struct variable **vars = xmalloc (sizeof *vars);
2193 struct ctables_nest *nest = xmalloc (sizeof *nest);
2194 *nest = (struct ctables_nest) {
2197 .scale_idx = a->scale ? 0 : SIZE_MAX,
2199 if (a->specs[CSV_CELL].n || a->scale)
2200 for (enum ctables_summary_variant sv = 0; sv < N_CSVS; sv++)
2202 ctables_summary_spec_set_clone (&nest->specs[sv], &a->specs[sv]);
2203 nest->specs[sv].var = a->var;
2204 nest->specs[sv].is_scale = a->scale;
2206 return (struct ctables_stack) { .nests = nest, .n = 1 };
2209 static struct ctables_stack
2210 enumerate_fts (enum pivot_axis_type axis_type, const struct ctables_axis *a)
2213 return (struct ctables_stack) { .n = 0 };
2221 return stack_fts (enumerate_fts (axis_type, a->subs[0]),
2222 enumerate_fts (axis_type, a->subs[1]));
2225 /* This should consider any of the scale variables found in the result to
2226 be linked to each other listwise for SMISSING=LISTWISE. */
2227 return nest_fts (enumerate_fts (axis_type, a->subs[0]),
2228 enumerate_fts (axis_type, a->subs[1]));
2234 union ctables_summary
2236 /* COUNT, VALIDN, TOTALN. */
2239 /* MINIMUM, MAXIMUM, RANGE. */
2246 /* MEAN, SEMEAN, STDDEV, SUM, VARIANCE, *.SUM. */
2247 struct moments1 *moments;
2249 /* MEDIAN, MODE, PTILE. */
2252 struct casewriter *writer;
2257 /* XXX multiple response */
2261 ctables_summary_init (union ctables_summary *s,
2262 const struct ctables_summary_spec *ss)
2264 switch (ss->function)
2268 case CTSF_ROWPCT_COUNT:
2269 case CTSF_COLPCT_COUNT:
2270 case CTSF_TABLEPCT_COUNT:
2271 case CTSF_SUBTABLEPCT_COUNT:
2272 case CTSF_LAYERPCT_COUNT:
2273 case CTSF_LAYERROWPCT_COUNT:
2274 case CTSF_LAYERCOLPCT_COUNT:
2275 case CTSF_ROWPCT_VALIDN:
2276 case CTSF_COLPCT_VALIDN:
2277 case CTSF_TABLEPCT_VALIDN:
2278 case CTSF_SUBTABLEPCT_VALIDN:
2279 case CTSF_LAYERPCT_VALIDN:
2280 case CTSF_LAYERROWPCT_VALIDN:
2281 case CTSF_LAYERCOLPCT_VALIDN:
2282 case CTSF_ROWPCT_TOTALN:
2283 case CTSF_COLPCT_TOTALN:
2284 case CTSF_TABLEPCT_TOTALN:
2285 case CTSF_SUBTABLEPCT_TOTALN:
2286 case CTSF_LAYERPCT_TOTALN:
2287 case CTSF_LAYERROWPCT_TOTALN:
2288 case CTSF_LAYERCOLPCT_TOTALN:
2300 s->min = s->max = SYSMIS;
2308 case CTSF_ROWPCT_SUM:
2309 case CTSF_COLPCT_SUM:
2310 case CTSF_TABLEPCT_SUM:
2311 case CTSF_SUBTABLEPCT_SUM:
2312 case CTSF_LAYERPCT_SUM:
2313 case CTSF_LAYERROWPCT_SUM:
2314 case CTSF_LAYERCOLPCT_SUM:
2315 s->moments = moments1_create (MOMENT_VARIANCE);
2322 struct caseproto *proto = caseproto_create ();
2323 proto = caseproto_add_width (proto, 0);
2324 proto = caseproto_add_width (proto, 0);
2326 struct subcase ordering;
2327 subcase_init (&ordering, 0, 0, SC_ASCEND);
2328 s->writer = sort_create_writer (&ordering, proto);
2329 subcase_uninit (&ordering);
2330 caseproto_unref (proto);
2340 ctables_summary_uninit (union ctables_summary *s,
2341 const struct ctables_summary_spec *ss)
2343 switch (ss->function)
2347 case CTSF_ROWPCT_COUNT:
2348 case CTSF_COLPCT_COUNT:
2349 case CTSF_TABLEPCT_COUNT:
2350 case CTSF_SUBTABLEPCT_COUNT:
2351 case CTSF_LAYERPCT_COUNT:
2352 case CTSF_LAYERROWPCT_COUNT:
2353 case CTSF_LAYERCOLPCT_COUNT:
2354 case CTSF_ROWPCT_VALIDN:
2355 case CTSF_COLPCT_VALIDN:
2356 case CTSF_TABLEPCT_VALIDN:
2357 case CTSF_SUBTABLEPCT_VALIDN:
2358 case CTSF_LAYERPCT_VALIDN:
2359 case CTSF_LAYERROWPCT_VALIDN:
2360 case CTSF_LAYERCOLPCT_VALIDN:
2361 case CTSF_ROWPCT_TOTALN:
2362 case CTSF_COLPCT_TOTALN:
2363 case CTSF_TABLEPCT_TOTALN:
2364 case CTSF_SUBTABLEPCT_TOTALN:
2365 case CTSF_LAYERPCT_TOTALN:
2366 case CTSF_LAYERROWPCT_TOTALN:
2367 case CTSF_LAYERCOLPCT_TOTALN:
2385 case CTSF_ROWPCT_SUM:
2386 case CTSF_COLPCT_SUM:
2387 case CTSF_TABLEPCT_SUM:
2388 case CTSF_SUBTABLEPCT_SUM:
2389 case CTSF_LAYERPCT_SUM:
2390 case CTSF_LAYERROWPCT_SUM:
2391 case CTSF_LAYERCOLPCT_SUM:
2392 moments1_destroy (s->moments);
2398 casewriter_destroy (s->writer);
2404 ctables_summary_add (union ctables_summary *s,
2405 const struct ctables_summary_spec *ss,
2406 const struct variable *var, const union value *value,
2407 bool is_scale, bool is_scale_missing,
2408 bool is_missing, bool excluded_missing,
2409 double d_weight, double e_weight)
2411 /* To determine whether a case is included in a given table for a particular
2412 kind of summary, consider the following charts for each variable in the
2413 table. Only if "yes" appears for every variable for the summary is the
2416 Categorical variables: VALIDN COUNT TOTALN
2417 Valid values in included categories yes yes yes
2418 Missing values in included categories --- yes yes
2419 Missing values in excluded categories --- --- yes
2420 Valid values in excluded categories --- --- ---
2422 Scale variables: VALIDN COUNT TOTALN
2423 Valid value yes yes yes
2424 Missing value --- yes yes
2426 Missing values include both user- and system-missing. (The system-missing
2427 value is always in an excluded category.)
2429 switch (ss->function)
2432 case CTSF_ROWPCT_TOTALN:
2433 case CTSF_COLPCT_TOTALN:
2434 case CTSF_TABLEPCT_TOTALN:
2435 case CTSF_SUBTABLEPCT_TOTALN:
2436 case CTSF_LAYERPCT_TOTALN:
2437 case CTSF_LAYERROWPCT_TOTALN:
2438 case CTSF_LAYERCOLPCT_TOTALN:
2439 s->count += d_weight;
2443 case CTSF_ROWPCT_COUNT:
2444 case CTSF_COLPCT_COUNT:
2445 case CTSF_TABLEPCT_COUNT:
2446 case CTSF_SUBTABLEPCT_COUNT:
2447 case CTSF_LAYERPCT_COUNT:
2448 case CTSF_LAYERROWPCT_COUNT:
2449 case CTSF_LAYERCOLPCT_COUNT:
2450 if (is_scale || !excluded_missing)
2451 s->count += d_weight;
2455 case CTSF_ROWPCT_VALIDN:
2456 case CTSF_COLPCT_VALIDN:
2457 case CTSF_TABLEPCT_VALIDN:
2458 case CTSF_SUBTABLEPCT_VALIDN:
2459 case CTSF_LAYERPCT_VALIDN:
2460 case CTSF_LAYERROWPCT_VALIDN:
2461 case CTSF_LAYERCOLPCT_VALIDN:
2465 s->count += d_weight;
2470 s->count += d_weight;
2474 if (is_scale || !excluded_missing)
2475 s->count += e_weight;
2482 s->count += e_weight;
2486 s->count += e_weight;
2492 if (!is_scale_missing)
2494 assert (!var_is_alpha (var)); /* XXX? */
2495 if (s->min == SYSMIS || value->f < s->min)
2497 if (s->max == SYSMIS || value->f > s->max)
2507 case CTSF_ROWPCT_SUM:
2508 case CTSF_COLPCT_SUM:
2509 case CTSF_TABLEPCT_SUM:
2510 case CTSF_SUBTABLEPCT_SUM:
2511 case CTSF_LAYERPCT_SUM:
2512 case CTSF_LAYERROWPCT_SUM:
2513 case CTSF_LAYERCOLPCT_SUM:
2514 if (!is_scale_missing)
2515 moments1_add (s->moments, value->f, e_weight);
2521 if (!is_scale_missing)
2523 s->ovalid += e_weight;
2525 struct ccase *c = case_create (casewriter_get_proto (s->writer));
2526 *case_num_rw_idx (c, 0) = value->f;
2527 *case_num_rw_idx (c, 1) = e_weight;
2528 casewriter_write (s->writer, c);
2534 static enum ctables_domain_type
2535 ctables_function_domain (enum ctables_summary_function function)
2559 case CTSF_COLPCT_COUNT:
2560 case CTSF_COLPCT_SUM:
2561 case CTSF_COLPCT_TOTALN:
2562 case CTSF_COLPCT_VALIDN:
2565 case CTSF_LAYERCOLPCT_COUNT:
2566 case CTSF_LAYERCOLPCT_SUM:
2567 case CTSF_LAYERCOLPCT_TOTALN:
2568 case CTSF_LAYERCOLPCT_VALIDN:
2569 return CTDT_LAYERCOL;
2571 case CTSF_LAYERPCT_COUNT:
2572 case CTSF_LAYERPCT_SUM:
2573 case CTSF_LAYERPCT_TOTALN:
2574 case CTSF_LAYERPCT_VALIDN:
2577 case CTSF_LAYERROWPCT_COUNT:
2578 case CTSF_LAYERROWPCT_SUM:
2579 case CTSF_LAYERROWPCT_TOTALN:
2580 case CTSF_LAYERROWPCT_VALIDN:
2581 return CTDT_LAYERROW;
2583 case CTSF_ROWPCT_COUNT:
2584 case CTSF_ROWPCT_SUM:
2585 case CTSF_ROWPCT_TOTALN:
2586 case CTSF_ROWPCT_VALIDN:
2589 case CTSF_SUBTABLEPCT_COUNT:
2590 case CTSF_SUBTABLEPCT_SUM:
2591 case CTSF_SUBTABLEPCT_TOTALN:
2592 case CTSF_SUBTABLEPCT_VALIDN:
2593 return CTDT_SUBTABLE;
2595 case CTSF_TABLEPCT_COUNT:
2596 case CTSF_TABLEPCT_SUM:
2597 case CTSF_TABLEPCT_TOTALN:
2598 case CTSF_TABLEPCT_VALIDN:
2606 ctables_summary_value (const struct ctables_cell *cell,
2607 union ctables_summary *s,
2608 const struct ctables_summary_spec *ss)
2610 switch (ss->function)
2616 case CTSF_ROWPCT_COUNT:
2617 case CTSF_COLPCT_COUNT:
2618 case CTSF_TABLEPCT_COUNT:
2619 case CTSF_SUBTABLEPCT_COUNT:
2620 case CTSF_LAYERPCT_COUNT:
2621 case CTSF_LAYERROWPCT_COUNT:
2622 case CTSF_LAYERCOLPCT_COUNT:
2624 enum ctables_domain_type d = ctables_function_domain (ss->function);
2625 return (cell->domains[d]->e_count
2626 ? s->count / cell->domains[d]->e_count * 100
2630 case CTSF_ROWPCT_VALIDN:
2631 case CTSF_COLPCT_VALIDN:
2632 case CTSF_TABLEPCT_VALIDN:
2633 case CTSF_SUBTABLEPCT_VALIDN:
2634 case CTSF_LAYERPCT_VALIDN:
2635 case CTSF_LAYERROWPCT_VALIDN:
2636 case CTSF_LAYERCOLPCT_VALIDN:
2638 enum ctables_domain_type d = ctables_function_domain (ss->function);
2639 return (cell->domains[d]->e_valid
2640 ? s->count / cell->domains[d]->e_valid * 100
2644 case CTSF_ROWPCT_TOTALN:
2645 case CTSF_COLPCT_TOTALN:
2646 case CTSF_TABLEPCT_TOTALN:
2647 case CTSF_SUBTABLEPCT_TOTALN:
2648 case CTSF_LAYERPCT_TOTALN:
2649 case CTSF_LAYERROWPCT_TOTALN:
2650 case CTSF_LAYERCOLPCT_TOTALN:
2652 enum ctables_domain_type d = ctables_function_domain (ss->function);
2653 return (cell->domains[d]->e_total
2654 ? s->count / cell->domains[d]->e_total * 100
2678 return s->max != SYSMIS && s->min != SYSMIS ? s->max - s->min : SYSMIS;
2683 moments1_calculate (s->moments, NULL, &mean, NULL, NULL, NULL);
2689 double weight, variance;
2690 moments1_calculate (s->moments, &weight, NULL, &variance, NULL, NULL);
2691 return calc_semean (variance, weight);
2697 moments1_calculate (s->moments, NULL, NULL, &variance, NULL, NULL);
2698 return variance != SYSMIS ? sqrt (variance) : SYSMIS;
2703 double weight, mean;
2704 moments1_calculate (s->moments, &weight, &mean, NULL, NULL, NULL);
2705 return weight != SYSMIS && mean != SYSMIS ? weight * mean : SYSMIS;
2711 moments1_calculate (s->moments, NULL, NULL, &variance, NULL, NULL);
2715 case CTSF_ROWPCT_SUM:
2716 case CTSF_COLPCT_SUM:
2717 case CTSF_TABLEPCT_SUM:
2718 case CTSF_SUBTABLEPCT_SUM:
2719 case CTSF_LAYERPCT_SUM:
2720 case CTSF_LAYERROWPCT_SUM:
2721 case CTSF_LAYERCOLPCT_SUM:
2728 struct casereader *reader = casewriter_make_reader (s->writer);
2731 struct percentile *ptile = percentile_create (
2732 ss->function == CTSF_PTILE ? ss->percentile : 0.5, s->ovalid);
2733 struct order_stats *os = &ptile->parent;
2734 order_stats_accumulate_idx (&os, 1, reader, 1, 0);
2735 s->ovalue = percentile_calculate (ptile, PC_HAVERAGE);
2736 statistic_destroy (&ptile->parent.parent);
2743 struct casereader *reader = casewriter_make_reader (s->writer);
2746 struct mode *mode = mode_create ();
2747 struct order_stats *os = &mode->parent;
2748 order_stats_accumulate_idx (&os, 1, reader, 1, 0);
2749 s->ovalue = mode->mode;
2750 statistic_destroy (&mode->parent.parent);
2758 struct ctables_cell_sort_aux
2760 const struct ctables_nest *nest;
2761 enum pivot_axis_type a;
2765 ctables_cell_compare_3way (const void *a_, const void *b_, const void *aux_)
2767 const struct ctables_cell_sort_aux *aux = aux_;
2768 struct ctables_cell *const *ap = a_;
2769 struct ctables_cell *const *bp = b_;
2770 const struct ctables_cell *a = *ap;
2771 const struct ctables_cell *b = *bp;
2773 const struct ctables_nest *nest = aux->nest;
2774 for (size_t i = 0; i < nest->n; i++)
2775 if (i != nest->scale_idx)
2777 const struct variable *var = nest->vars[i];
2778 const struct ctables_cell_value *a_cv = &a->axes[aux->a].cvs[i];
2779 const struct ctables_cell_value *b_cv = &b->axes[aux->a].cvs[i];
2780 if (a_cv->category != b_cv->category)
2781 return a_cv->category > b_cv->category ? 1 : -1;
2783 const union value *a_val = &a_cv->value;
2784 const union value *b_val = &b_cv->value;
2785 switch (a_cv->category->type)
2791 case CCT_POSTCOMPUTE:
2792 case CCT_EXCLUDED_MISSING:
2793 /* Must be equal. */
2801 int cmp = value_compare_3way (a_val, b_val, var_get_width (var));
2809 int cmp = value_compare_3way (a_val, b_val, var_get_width (var));
2811 return a_cv->category->sort_ascending ? cmp : -cmp;
2817 const char *a_label = var_lookup_value_label (var, a_val);
2818 const char *b_label = var_lookup_value_label (var, b_val);
2820 ? (b_label ? strcmp (a_label, b_label) : 1)
2821 : (b_label ? -1 : value_compare_3way (
2822 a_val, b_val, var_get_width (var))));
2824 return a_cv->category->sort_ascending ? cmp : -cmp;
2838 For each ctables_table:
2839 For each combination of row vars:
2840 For each combination of column vars:
2841 For each combination of layer vars:
2843 Make a table of row values:
2844 Sort entries by row values
2845 Assign a 0-based index to each actual value
2846 Construct a dimension
2847 Make a table of column values
2848 Make a table of layer values
2850 Fill the table entry using the indexes from before.
2853 static struct ctables_domain *
2854 ctables_domain_insert (struct ctables_section *s, struct ctables_cell *cell,
2855 enum ctables_domain_type domain)
2858 for (enum pivot_axis_type a = 0; a < PIVOT_N_AXES; a++)
2860 const struct ctables_nest *nest = s->nests[a];
2861 for (size_t i = 0; i < nest->n_domains[domain]; i++)
2863 size_t v_idx = nest->domains[domain][i];
2864 struct ctables_cell_value *cv = &cell->axes[a].cvs[v_idx];
2865 hash = hash_pointer (cv->category, hash);
2866 if (cv->category->type != CCT_TOTAL
2867 && cv->category->type != CCT_SUBTOTAL
2868 && cv->category->type != CCT_POSTCOMPUTE)
2869 hash = value_hash (&cv->value,
2870 var_get_width (nest->vars[v_idx]), hash);
2874 struct ctables_domain *d;
2875 HMAP_FOR_EACH_WITH_HASH (d, struct ctables_domain, node, hash, &s->domains[domain])
2877 const struct ctables_cell *df = d->example;
2878 for (enum pivot_axis_type a = 0; a < PIVOT_N_AXES; a++)
2880 const struct ctables_nest *nest = s->nests[a];
2881 for (size_t i = 0; i < nest->n_domains[domain]; i++)
2883 size_t v_idx = nest->domains[domain][i];
2884 struct ctables_cell_value *cv1 = &df->axes[a].cvs[v_idx];
2885 struct ctables_cell_value *cv2 = &cell->axes[a].cvs[v_idx];
2886 if (cv1->category != cv2->category
2887 || (cv1->category->type != CCT_TOTAL
2888 && cv1->category->type != CCT_SUBTOTAL
2889 && cv1->category->type != CCT_POSTCOMPUTE
2890 && !value_equal (&cv1->value, &cv2->value,
2891 var_get_width (nest->vars[v_idx]))))
2900 d = xmalloc (sizeof *d);
2901 *d = (struct ctables_domain) { .example = cell };
2902 hmap_insert (&s->domains[domain], &d->node, hash);
2906 static struct substring
2907 rtrim_value (const union value *v, const struct variable *var)
2909 struct substring s = ss_buffer (CHAR_CAST (char *, v->s),
2910 var_get_width (var));
2911 ss_rtrim (&s, ss_cstr (" "));
2916 in_string_range (const union value *v, const struct variable *var,
2917 const struct substring *srange)
2919 struct substring s = rtrim_value (v, var);
2920 return ((!srange[0].string || ss_compare (s, srange[0]) >= 0)
2921 && (!srange[1].string || ss_compare (s, srange[1]) <= 0));
2924 static const struct ctables_category *
2925 ctables_categories_match (const struct ctables_categories *c,
2926 const union value *v, const struct variable *var)
2928 if (var_is_numeric (var) && v->f == SYSMIS)
2931 const struct ctables_category *othernm = NULL;
2932 for (size_t i = c->n_cats; i-- > 0; )
2934 const struct ctables_category *cat = &c->cats[i];
2938 if (cat->number == v->f)
2943 if (ss_equals (cat->string, rtrim_value (v, var)))
2948 if ((cat->nrange[0] == -DBL_MAX || v->f >= cat->nrange[0])
2949 && (cat->nrange[1] == DBL_MAX || v->f <= cat->nrange[1]))
2954 if (in_string_range (v, var, cat->srange))
2959 if (var_is_value_missing (var, v))
2963 case CCT_POSTCOMPUTE:
2978 return (cat->include_missing || !var_is_value_missing (var, v) ? cat
2981 case CCT_EXCLUDED_MISSING:
2986 return var_is_value_missing (var, v) ? NULL : othernm;
2989 static const struct ctables_category *
2990 ctables_categories_total (const struct ctables_categories *c)
2992 const struct ctables_category *first = &c->cats[0];
2993 const struct ctables_category *last = &c->cats[c->n_cats - 1];
2994 return (first->type == CCT_TOTAL ? first
2995 : last->type == CCT_TOTAL ? last
2999 static struct ctables_cell *
3000 ctables_cell_insert__ (struct ctables_section *s, const struct ccase *c,
3001 const struct ctables_category *cats[PIVOT_N_AXES][10])
3004 enum ctables_summary_variant sv = CSV_CELL;
3005 for (enum pivot_axis_type a = 0; a < PIVOT_N_AXES; a++)
3007 const struct ctables_nest *nest = s->nests[a];
3008 for (size_t i = 0; i < nest->n; i++)
3009 if (i != nest->scale_idx)
3011 hash = hash_pointer (cats[a][i], hash);
3012 if (cats[a][i]->type != CCT_TOTAL
3013 && cats[a][i]->type != CCT_SUBTOTAL
3014 && cats[a][i]->type != CCT_POSTCOMPUTE)
3015 hash = value_hash (case_data (c, nest->vars[i]),
3016 var_get_width (nest->vars[i]), hash);
3022 struct ctables_cell *cell;
3023 HMAP_FOR_EACH_WITH_HASH (cell, struct ctables_cell, node, hash, &s->cells)
3025 for (enum pivot_axis_type a = 0; a < PIVOT_N_AXES; a++)
3027 const struct ctables_nest *nest = s->nests[a];
3028 for (size_t i = 0; i < nest->n; i++)
3029 if (i != nest->scale_idx
3030 && (cats[a][i] != cell->axes[a].cvs[i].category
3031 || (cats[a][i]->type != CCT_TOTAL
3032 && cats[a][i]->type != CCT_SUBTOTAL
3033 && cats[a][i]->type != CCT_POSTCOMPUTE
3034 && !value_equal (case_data (c, nest->vars[i]),
3035 &cell->axes[a].cvs[i].value,
3036 var_get_width (nest->vars[i])))))
3045 cell = xmalloc (sizeof *cell);
3048 cell->omit_domains = 0;
3049 cell->postcompute = false;
3050 //struct string name = DS_EMPTY_INITIALIZER;
3051 for (enum pivot_axis_type a = 0; a < PIVOT_N_AXES; a++)
3053 const struct ctables_nest *nest = s->nests[a];
3054 cell->axes[a].cvs = (nest->n
3055 ? xnmalloc (nest->n, sizeof *cell->axes[a].cvs)
3057 for (size_t i = 0; i < nest->n; i++)
3059 const struct ctables_category *cat = cats[a][i];
3060 const struct variable *var = nest->vars[i];
3061 const union value *value = case_data (c, var);
3062 if (i != nest->scale_idx)
3064 const struct ctables_category *subtotal = cat->subtotal;
3065 if (cat->hide || (subtotal && subtotal->hide_subcategories))
3068 if (cat->type == CCT_TOTAL
3069 || cat->type == CCT_SUBTOTAL
3070 || cat->type == CCT_POSTCOMPUTE)
3072 /* XXX these should be more encompassing I think.*/
3076 case PIVOT_AXIS_COLUMN:
3077 cell->omit_domains |= ((1u << CTDT_TABLE) |
3078 (1u << CTDT_LAYER) |
3079 (1u << CTDT_LAYERCOL) |
3080 (1u << CTDT_SUBTABLE) |
3083 case PIVOT_AXIS_ROW:
3084 cell->omit_domains |= ((1u << CTDT_TABLE) |
3085 (1u << CTDT_LAYER) |
3086 (1u << CTDT_LAYERROW) |
3087 (1u << CTDT_SUBTABLE) |
3090 case PIVOT_AXIS_LAYER:
3091 cell->omit_domains |= ((1u << CTDT_TABLE) |
3092 (1u << CTDT_LAYER));
3096 if (cat->type == CCT_POSTCOMPUTE)
3097 cell->postcompute = true;
3100 cell->axes[a].cvs[i].category = cat;
3101 value_clone (&cell->axes[a].cvs[i].value, value, var_get_width (var));
3104 if (i != nest->scale_idx)
3106 if (!ds_is_empty (&name))
3107 ds_put_cstr (&name, ", ");
3108 char *value_s = data_out (value, var_get_encoding (var),
3109 var_get_print_format (var),
3110 settings_get_fmt_settings ());
3111 if (cat->type == CCT_TOTAL
3112 || cat->type == CCT_SUBTOTAL
3113 || cat->type == CCT_POSTCOMPUTE)
3114 ds_put_format (&name, "%s=total", var_get_name (var));
3116 ds_put_format (&name, "%s=%s", var_get_name (var),
3117 value_s + strspn (value_s, " "));
3123 //cell->name = ds_steal_cstr (&name);
3125 const struct ctables_nest *ss = s->nests[s->table->summary_axis];
3126 const struct ctables_summary_spec_set *specs = &ss->specs[cell->sv];
3127 cell->summaries = xmalloc (specs->n * sizeof *cell->summaries);
3128 for (size_t i = 0; i < specs->n; i++)
3129 ctables_summary_init (&cell->summaries[i], &specs->specs[i]);
3130 for (enum ctables_domain_type dt = 0; dt < N_CTDTS; dt++)
3131 cell->domains[dt] = ctables_domain_insert (s, cell, dt);
3132 hmap_insert (&s->cells, &cell->node, hash);
3137 is_scale_missing (const struct ctables_summary_spec_set *specs,
3138 const struct ccase *c)
3140 if (!specs->is_scale)
3143 if (var_is_num_missing (specs->var, case_num (c, specs->var)))
3146 for (size_t i = 0; i < specs->n_listwise_vars; i++)
3148 const struct variable *var = specs->listwise_vars[i];
3149 if (var_is_num_missing (var, case_num (c, var)))
3157 ctables_cell_add__ (struct ctables_section *s, const struct ccase *c,
3158 const struct ctables_category *cats[PIVOT_N_AXES][10],
3159 bool is_missing, bool excluded_missing,
3160 double d_weight, double e_weight)
3162 struct ctables_cell *cell = ctables_cell_insert__ (s, c, cats);
3163 const struct ctables_nest *ss = s->nests[s->table->summary_axis];
3165 const struct ctables_summary_spec_set *specs = &ss->specs[cell->sv];
3167 bool scale_missing = is_scale_missing (specs, c);
3168 for (size_t i = 0; i < specs->n; i++)
3169 ctables_summary_add (&cell->summaries[i], &specs->specs[i],
3170 specs->var, case_data (c, specs->var), specs->is_scale,
3171 scale_missing, is_missing, excluded_missing,
3172 d_weight, e_weight);
3173 for (enum ctables_domain_type dt = 0; dt < N_CTDTS; dt++)
3174 if (!(cell->omit_domains && (1u << dt)))
3176 struct ctables_domain *d = cell->domains[dt];
3177 d->d_total += d_weight;
3178 d->e_total += e_weight;
3179 if (!excluded_missing)
3181 d->d_count += d_weight;
3182 d->e_count += e_weight;
3186 d->d_valid += d_weight;
3187 d->e_valid += e_weight;
3193 recurse_totals (struct ctables_section *s, const struct ccase *c,
3194 const struct ctables_category *cats[PIVOT_N_AXES][10],
3195 bool is_missing, bool excluded_missing,
3196 double d_weight, double e_weight,
3197 enum pivot_axis_type start_axis, size_t start_nest)
3199 for (enum pivot_axis_type a = start_axis; a < PIVOT_N_AXES; a++)
3201 const struct ctables_nest *nest = s->nests[a];
3202 for (size_t i = start_nest; i < nest->n; i++)
3204 if (i == nest->scale_idx)
3207 const struct variable *var = nest->vars[i];
3209 const struct ctables_category *total = ctables_categories_total (
3210 s->table->categories[var_get_dict_index (var)]);
3213 const struct ctables_category *save = cats[a][i];
3215 ctables_cell_add__ (s, c, cats, is_missing, excluded_missing,
3216 d_weight, e_weight);
3217 recurse_totals (s, c, cats, is_missing, excluded_missing,
3218 d_weight, e_weight, a, i + 1);
3227 recurse_subtotals (struct ctables_section *s, const struct ccase *c,
3228 const struct ctables_category *cats[PIVOT_N_AXES][10],
3229 bool is_missing, bool excluded_missing,
3230 double d_weight, double e_weight,
3231 enum pivot_axis_type start_axis, size_t start_nest)
3233 for (enum pivot_axis_type a = start_axis; a < PIVOT_N_AXES; a++)
3235 const struct ctables_nest *nest = s->nests[a];
3236 for (size_t i = start_nest; i < nest->n; i++)
3238 if (i == nest->scale_idx)
3241 const struct ctables_category *save = cats[a][i];
3244 cats[a][i] = save->subtotal;
3245 ctables_cell_add__ (s, c, cats, is_missing, excluded_missing,
3246 d_weight, e_weight);
3247 recurse_subtotals (s, c, cats, is_missing, excluded_missing,
3248 d_weight, e_weight, a, i + 1);
3257 ctables_add_occurrence (const struct variable *var,
3258 const union value *value,
3259 struct hmap *occurrences)
3261 int width = var_get_width (var);
3262 unsigned int hash = value_hash (value, width, 0);
3264 struct ctables_occurrence *o;
3265 HMAP_FOR_EACH_WITH_HASH (o, struct ctables_occurrence, node, hash,
3267 if (value_equal (value, &o->value, width))
3270 o = xmalloc (sizeof *o);
3271 value_clone (&o->value, value, width);
3272 hmap_insert (occurrences, &o->node, hash);
3276 ctables_cell_insert (struct ctables_section *s,
3277 const struct ccase *c,
3278 double d_weight, double e_weight)
3280 const struct ctables_category *cats[PIVOT_N_AXES][10]; /* XXX */
3282 /* Does at least one categorical variable have a missing value in an included
3283 or excluded category? */
3284 bool is_missing = false;
3286 /* Does at least one categorical variable have a missing value in an excluded
3288 bool excluded_missing = false;
3290 for (enum pivot_axis_type a = 0; a < PIVOT_N_AXES; a++)
3292 const struct ctables_nest *nest = s->nests[a];
3293 for (size_t i = 0; i < nest->n; i++)
3295 if (i == nest->scale_idx)
3298 const struct variable *var = nest->vars[i];
3299 const union value *value = case_data (c, var);
3301 bool var_missing = var_is_value_missing (var, value) != 0;
3305 cats[a][i] = ctables_categories_match (
3306 s->table->categories[var_get_dict_index (var)], value, var);
3312 static const struct ctables_category cct_excluded_missing = {
3313 .type = CCT_EXCLUDED_MISSING,
3316 cats[a][i] = &cct_excluded_missing;
3317 excluded_missing = true;
3322 if (!excluded_missing)
3323 for (enum pivot_axis_type a = 0; a < PIVOT_N_AXES; a++)
3325 const struct ctables_nest *nest = s->nests[a];
3326 for (size_t i = 0; i < nest->n; i++)
3327 if (i != nest->scale_idx)
3329 const struct variable *var = nest->vars[i];
3330 const union value *value = case_data (c, var);
3331 ctables_add_occurrence (var, value, &s->occurrences[a][i]);
3335 ctables_cell_add__ (s, c, cats, is_missing, excluded_missing,
3336 d_weight, e_weight);
3338 //if (!excluded_missing)
3340 recurse_totals (s, c, cats, is_missing, excluded_missing,
3341 d_weight, e_weight, 0, 0);
3342 recurse_subtotals (s, c, cats, is_missing, excluded_missing,
3343 d_weight, e_weight, 0, 0);
3349 const struct ctables_summary_spec_set *set;
3354 merge_item_compare_3way (const struct merge_item *a, const struct merge_item *b)
3356 const struct ctables_summary_spec *as = &a->set->specs[a->ofs];
3357 const struct ctables_summary_spec *bs = &b->set->specs[b->ofs];
3358 if (as->function != bs->function)
3359 return as->function > bs->function ? 1 : -1;
3360 else if (as->percentile != bs->percentile)
3361 return as->percentile < bs->percentile ? 1 : -1;
3362 return strcmp (as->label, bs->label);
3365 static struct pivot_value *
3366 ctables_category_create_label__ (const struct ctables_category *cat,
3367 const struct variable *var,
3368 const union value *value)
3370 return (cat->type == CCT_TOTAL || cat->type == CCT_SUBTOTAL
3371 ? pivot_value_new_user_text (cat->total_label, SIZE_MAX)
3372 : pivot_value_new_var_value (var, value));
3375 static struct pivot_value *
3376 ctables_postcompute_label (const struct ctables_categories *cats,
3377 const struct ctables_category *cat,
3378 const struct variable *var,
3379 const union value *value)
3381 struct substring in = ss_cstr (cat->pc->label);
3382 struct substring target = ss_cstr (")LABEL[");
3384 struct string out = DS_EMPTY_INITIALIZER;
3387 size_t chunk = ss_find_substring (in, target);
3388 if (chunk == SIZE_MAX)
3390 if (ds_is_empty (&out))
3391 return pivot_value_new_user_text (in.string, in.length);
3394 ds_put_substring (&out, in);
3395 return pivot_value_new_user_text_nocopy (ds_steal_cstr (&out));
3399 ds_put_substring (&out, ss_head (in, chunk));
3400 ss_advance (&in, chunk + target.length);
3402 struct substring idx_s;
3403 if (!ss_get_until (&in, ']', &idx_s))
3406 long int idx = strtol (idx_s.string, &tail, 10);
3407 if (idx < 1 || idx > cats->n_cats || tail != ss_end (idx_s))
3410 struct ctables_category *cat2 = &cats->cats[idx - 1];
3411 struct pivot_value *label2
3412 = ctables_category_create_label__ (cat2, var, value);
3413 char *label2_s = pivot_value_to_string_defaults (label2);
3414 ds_put_cstr (&out, label2_s);
3416 pivot_value_destroy (label2);
3421 return pivot_value_new_user_text (cat->pc->label, SIZE_MAX);
3424 static struct pivot_value *
3425 ctables_category_create_label (const struct ctables_categories *cats,
3426 const struct ctables_category *cat,
3427 const struct variable *var,
3428 const union value *value)
3430 return (cat->type == CCT_POSTCOMPUTE && cat->pc->label
3431 ? ctables_postcompute_label (cats, cat, var, value)
3432 : ctables_category_create_label__ (cat, var, value));
3435 static struct ctables_value *
3436 ctables_value_find__ (struct ctables_table *t, const union value *value,
3437 int width, unsigned int hash)
3439 struct ctables_value *clv;
3440 HMAP_FOR_EACH_WITH_HASH (clv, struct ctables_value, node,
3441 hash, &t->clabels_values_map)
3442 if (value_equal (value, &clv->value, width))
3448 ctables_value_insert (struct ctables_table *t, const union value *value,
3451 unsigned int hash = value_hash (value, width, 0);
3452 struct ctables_value *clv = ctables_value_find__ (t, value, width, hash);
3455 clv = xmalloc (sizeof *clv);
3456 value_clone (&clv->value, value, width);
3457 hmap_insert (&t->clabels_values_map, &clv->node, hash);
3461 static struct ctables_value *
3462 ctables_value_find (struct ctables_table *t,
3463 const union value *value, int width)
3465 return ctables_value_find__ (t, value, width,
3466 value_hash (value, width, 0));
3470 ctables_table_add_section (struct ctables_table *t, enum pivot_axis_type a,
3471 size_t ix[PIVOT_N_AXES])
3473 if (a < PIVOT_N_AXES)
3475 size_t limit = MAX (t->stacks[a].n, 1);
3476 for (ix[a] = 0; ix[a] < limit; ix[a]++)
3477 ctables_table_add_section (t, a + 1, ix);
3481 struct ctables_section *s = &t->sections[t->n_sections++];
3482 *s = (struct ctables_section) {
3484 .cells = HMAP_INITIALIZER (s->cells),
3486 for (a = 0; a < PIVOT_N_AXES; a++)
3489 struct ctables_nest *nest = &t->stacks[a].nests[ix[a]];
3491 s->occurrences[a] = xnmalloc (nest->n, sizeof *s->occurrences[a]);
3492 for (size_t i = 0; i < nest->n; i++)
3493 hmap_init (&s->occurrences[a][i]);
3495 for (size_t i = 0; i < N_CTDTS; i++)
3496 hmap_init (&s->domains[i]);
3501 ctpo_add (double a, double b)
3507 ctpo_sub (double a, double b)
3513 ctpo_mul (double a, double b)
3519 ctpo_div (double a, double b)
3521 return b ? a / b : SYSMIS;
3525 ctpo_pow (double a, double b)
3527 int save_errno = errno;
3529 double result = pow (a, b);
3537 ctpo_neg (double a, double b UNUSED)
3542 struct ctables_pcexpr_evaluate_ctx
3544 const struct ctables_cell *cell;
3545 const struct ctables_section *section;
3546 const struct ctables_categories *cats;
3547 enum pivot_axis_type pc_a;
3552 static double ctables_pcexpr_evaluate (
3553 const struct ctables_pcexpr_evaluate_ctx *, const struct ctables_pcexpr *);
3556 ctables_pcexpr_evaluate_nonterminal (
3557 const struct ctables_pcexpr_evaluate_ctx *ctx,
3558 const struct ctables_pcexpr *e, size_t n_args,
3559 double evaluate (double, double))
3561 double args[2] = { 0, 0 };
3562 for (size_t i = 0; i < n_args; i++)
3564 args[i] = ctables_pcexpr_evaluate (ctx, e->subs[i]);
3565 if (!isfinite (args[i]) || args[i] == SYSMIS)
3568 return evaluate (args[0], args[1]);
3572 ctables_pcexpr_evaluate_category (const struct ctables_pcexpr_evaluate_ctx *ctx,
3573 const struct ctables_cell_value *pc_cv)
3575 const struct ctables_section *s = ctx->section;
3578 for (enum pivot_axis_type a = 0; a < PIVOT_N_AXES; a++)
3580 const struct ctables_nest *nest = s->nests[a];
3581 for (size_t i = 0; i < nest->n; i++)
3582 if (i != nest->scale_idx)
3584 const struct ctables_cell_value *cv
3585 = (a == ctx->pc_a && i == ctx->pc_a_idx ? pc_cv
3586 : &ctx->cell->axes[a].cvs[i]);
3587 hash = hash_pointer (cv->category, hash);
3588 if (cv->category->type != CCT_TOTAL
3589 && cv->category->type != CCT_SUBTOTAL
3590 && cv->category->type != CCT_POSTCOMPUTE)
3591 hash = value_hash (&cv->value,
3592 var_get_width (nest->vars[i]), hash);
3596 struct ctables_cell *tc;
3597 HMAP_FOR_EACH_WITH_HASH (tc, struct ctables_cell, node, hash, &s->cells)
3599 for (enum pivot_axis_type a = 0; a < PIVOT_N_AXES; a++)
3601 const struct ctables_nest *nest = s->nests[a];
3602 for (size_t i = 0; i < nest->n; i++)
3603 if (i != nest->scale_idx)
3605 const struct ctables_cell_value *p_cv
3606 = (a == ctx->pc_a && i == ctx->pc_a_idx ? pc_cv
3607 : &ctx->cell->axes[a].cvs[i]);
3608 const struct ctables_cell_value *t_cv = &tc->axes[a].cvs[i];
3609 if (p_cv->category != t_cv->category
3610 || (p_cv->category->type != CCT_TOTAL
3611 && p_cv->category->type != CCT_SUBTOTAL
3612 && p_cv->category->type != CCT_POSTCOMPUTE
3613 && !value_equal (&p_cv->value,
3615 var_get_width (nest->vars[i]))))
3627 const struct ctables_table *t = s->table;
3628 const struct ctables_nest *specs_nest = s->nests[t->summary_axis];
3629 const struct ctables_summary_spec_set *specs = &specs_nest->specs[tc->sv];
3630 return ctables_summary_value (tc, &tc->summaries[ctx->summary_idx],
3631 &specs->specs[ctx->summary_idx]);
3635 ctables_pcexpr_evaluate (const struct ctables_pcexpr_evaluate_ctx *ctx,
3636 const struct ctables_pcexpr *e)
3643 case CTPO_CAT_RANGE:
3645 struct ctables_cell_value cv = {
3646 .category = ctables_find_category_for_postcompute (ctx->cats, e)
3648 assert (cv.category != NULL);
3650 struct hmap *occurrences = &ctx->section->occurrences[ctx->pc_a][ctx->pc_a_idx];
3651 const struct ctables_occurrence *o;
3654 const struct variable *var = ctx->section->nests[ctx->pc_a]->vars[ctx->pc_a_idx];
3655 HMAP_FOR_EACH (o, struct ctables_occurrence, node, occurrences)
3656 if (ctables_categories_match (ctx->cats, &o->value, var) == cv.category)
3658 cv.value = o->value;
3659 sum += ctables_pcexpr_evaluate_category (ctx, &cv);
3664 case CTPO_CAT_NUMBER:
3665 case CTPO_CAT_STRING:
3666 case CTPO_CAT_MISSING:
3667 case CTPO_CAT_OTHERNM:
3668 case CTPO_CAT_SUBTOTAL:
3669 case CTPO_CAT_TOTAL:
3671 struct ctables_cell_value cv = {
3672 .category = ctables_find_category_for_postcompute (ctx->cats, e),
3673 .value = { .f = e->number },
3675 assert (cv.category != NULL);
3676 return ctables_pcexpr_evaluate_category (ctx, &cv);
3680 return ctables_pcexpr_evaluate_nonterminal (ctx, e, 2, ctpo_add);
3683 return ctables_pcexpr_evaluate_nonterminal (ctx, e, 2, ctpo_sub);
3686 return ctables_pcexpr_evaluate_nonterminal (ctx, e, 2, ctpo_mul);
3689 return ctables_pcexpr_evaluate_nonterminal (ctx, e, 2, ctpo_div);
3692 return ctables_pcexpr_evaluate_nonterminal (ctx, e, 2, ctpo_pow);
3695 return ctables_pcexpr_evaluate_nonterminal (ctx, e, 1, ctpo_neg);
3701 /* XXX what if there is a postcompute in more than one dimension?? */
3702 static const struct ctables_postcompute *
3703 ctables_cell_postcompute (const struct ctables_section *s,
3704 const struct ctables_cell *cell,
3705 enum pivot_axis_type *pc_a_p,
3708 assert (cell->postcompute);
3709 for (enum pivot_axis_type pc_a = 0; ; pc_a++)
3711 assert (pc_a < PIVOT_N_AXES);
3712 for (size_t pc_a_idx = 0; pc_a_idx < s->nests[pc_a]->n; pc_a_idx++)
3714 const struct ctables_cell_value *cv = &cell->axes[pc_a].cvs[pc_a_idx];
3715 if (cv->category->type == CCT_POSTCOMPUTE)
3720 *pc_a_idx_p = pc_a_idx;
3721 return cv->category->pc;
3730 ctables_cell_calculate_postcompute (const struct ctables_section *s,
3731 const struct ctables_cell *cell,
3732 const struct ctables_summary_spec *ss,
3733 struct fmt_spec *format,
3734 bool *is_ctables_format,
3737 enum pivot_axis_type pc_a;
3739 const struct ctables_postcompute *pc = ctables_cell_postcompute (
3740 s, cell, &pc_a, &pc_a_idx);
3744 for (size_t i = 0; i < pc->specs->n; i++)
3746 const struct ctables_summary_spec *ss2 = &pc->specs->specs[i];
3747 if (ss->function == ss2->function
3748 && ss->percentile == ss2->percentile)
3750 *format = ss2->format;
3751 *is_ctables_format = ss2->is_ctables_format;
3757 const struct variable *var = s->nests[pc_a]->vars[pc_a_idx];
3758 const struct ctables_categories *cats = s->table->categories[
3759 var_get_dict_index (var)];
3760 struct ctables_pcexpr_evaluate_ctx ctx = {
3765 .pc_a_idx = pc_a_idx,
3766 .summary_idx = summary_idx,
3768 return ctables_pcexpr_evaluate (&ctx, pc->expr);
3772 ctables_table_output (struct ctables *ct, struct ctables_table *t)
3774 struct pivot_table *pt = pivot_table_create__ (
3776 ? pivot_value_new_user_text (t->title, SIZE_MAX)
3777 : pivot_value_new_text (N_("Custom Tables"))),
3780 pivot_table_set_caption (
3781 pt, pivot_value_new_user_text (t->caption, SIZE_MAX));
3783 pivot_table_set_corner_text (
3784 pt, pivot_value_new_user_text (t->corner, SIZE_MAX));
3786 bool summary_dimension = (t->summary_axis != t->slabels_axis
3787 || (!t->slabels_visible
3788 && t->summary_specs.n > 1));
3789 if (summary_dimension)
3791 struct pivot_dimension *d = pivot_dimension_create (
3792 pt, t->slabels_axis, N_("Statistics"));
3793 const struct ctables_summary_spec_set *specs = &t->summary_specs;
3794 if (!t->slabels_visible)
3795 d->hide_all_labels = true;
3796 for (size_t i = 0; i < specs->n; i++)
3797 pivot_category_create_leaf (
3798 d->root, pivot_value_new_text (specs->specs[i].label));
3801 bool categories_dimension = t->clabels_example != NULL;
3802 if (categories_dimension)
3804 struct pivot_dimension *d = pivot_dimension_create (
3805 pt, t->label_axis[t->clabels_from_axis],
3806 t->clabels_from_axis == PIVOT_AXIS_ROW
3807 ? N_("Row Categories")
3808 : N_("Column Categories"));
3809 const struct variable *var = t->clabels_example;
3810 const struct ctables_categories *c = t->categories[var_get_dict_index (var)];
3811 for (size_t i = 0; i < t->n_clabels_values; i++)
3813 const struct ctables_value *value = t->clabels_values[i];
3814 const struct ctables_category *cat = ctables_categories_match (c, &value->value, var);
3815 assert (cat != NULL);
3816 pivot_category_create_leaf (d->root, ctables_category_create_label (
3817 c, cat, t->clabels_example,
3822 pivot_table_set_look (pt, ct->look);
3823 struct pivot_dimension *d[PIVOT_N_AXES];
3824 for (enum pivot_axis_type a = 0; a < PIVOT_N_AXES; a++)
3826 static const char *names[] = {
3827 [PIVOT_AXIS_ROW] = N_("Rows"),
3828 [PIVOT_AXIS_COLUMN] = N_("Columns"),
3829 [PIVOT_AXIS_LAYER] = N_("Layers"),
3831 d[a] = (t->axes[a] || a == t->summary_axis
3832 ? pivot_dimension_create (pt, a, names[a])
3837 assert (t->axes[a]);
3839 for (size_t i = 0; i < t->stacks[a].n; i++)
3841 struct ctables_nest *nest = &t->stacks[a].nests[i];
3842 struct ctables_section **sections = xnmalloc (t->n_sections,
3844 size_t n_sections = 0;
3846 size_t n_total_cells = 0;
3847 size_t max_depth = 0;
3848 for (size_t j = 0; j < t->n_sections; j++)
3849 if (t->sections[j].nests[a] == nest)
3851 struct ctables_section *s = &t->sections[j];
3852 sections[n_sections++] = s;
3853 n_total_cells += s->cells.count;
3855 size_t depth = s->nests[a]->n;
3856 max_depth = MAX (depth, max_depth);
3859 struct ctables_cell **sorted = xnmalloc (n_total_cells,
3861 size_t n_sorted = 0;
3863 for (size_t j = 0; j < n_sections; j++)
3865 struct ctables_section *s = sections[j];
3867 struct ctables_cell *cell;
3868 HMAP_FOR_EACH (cell, struct ctables_cell, node, &s->cells)
3870 sorted[n_sorted++] = cell;
3871 assert (n_sorted <= n_total_cells);
3874 struct ctables_cell_sort_aux aux = { .nest = nest, .a = a };
3875 sort (sorted, n_sorted, sizeof *sorted, ctables_cell_compare_3way, &aux);
3878 for (size_t j = 0; j < n_sorted; j++)
3880 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);
3885 struct ctables_level
3887 enum ctables_level_type
3889 CTL_VAR, /* Variable label for nest->vars[var_idx]. */
3890 CTL_CATEGORY, /* Category for nest->vars[var_idx]. */
3891 CTL_SUMMARY, /* Summary functions. */
3895 enum settings_value_show vlabel; /* CTL_VAR only. */
3898 struct ctables_level *levels = xnmalloc (1 + 2 * max_depth, sizeof *levels);
3899 size_t n_levels = 0;
3900 for (size_t k = 0; k < nest->n; k++)
3902 enum ctables_vlabel vlabel = ct->vlabels[var_get_dict_index (nest->vars[k])];
3903 if (vlabel != CTVL_NONE)
3905 levels[n_levels++] = (struct ctables_level) {
3907 .vlabel = (enum settings_value_show) vlabel,
3912 if (nest->scale_idx != k
3913 && (k != nest->n - 1 || t->label_axis[a] == a))
3915 levels[n_levels++] = (struct ctables_level) {
3916 .type = CTL_CATEGORY,
3922 if (!summary_dimension && a == t->slabels_axis)
3924 levels[n_levels++] = (struct ctables_level) {
3925 .type = CTL_SUMMARY,
3926 .var_idx = SIZE_MAX,
3930 /* Pivot categories:
3932 - variable label for nest->vars[0], if vlabel != CTVL_NONE
3933 - category for nest->vars[0], if nest->scale_idx != 0
3934 - variable label for nest->vars[1], if vlabel != CTVL_NONE
3935 - category for nest->vars[1], if nest->scale_idx != 1
3937 - variable label for nest->vars[n - 1], if vlabel != CTVL_NONE
3938 - category for nest->vars[n - 1], if t->label_axis[a] == a && nest->scale_idx != n - 1.
3939 - summary function, if 'a == t->slabels_axis && a ==
3942 Additional dimensions:
3944 - If 'a == t->slabels_axis && a != t->summary_axis', add a summary
3946 - If 't->label_axis[b] == a' for some 'b != a', add a category
3951 struct pivot_category **groups = xnmalloc (1 + 2 * max_depth, sizeof *groups);
3953 for (size_t j = 0; j < n_sorted; j++)
3955 struct ctables_cell *cell = sorted[j];
3956 struct ctables_cell *prev = j > 0 ? sorted[j - 1] : NULL;
3958 size_t n_common = 0;
3961 for (; n_common < n_levels; n_common++)
3963 const struct ctables_level *level = &levels[n_common];
3964 if (level->type == CTL_CATEGORY)
3966 size_t var_idx = level->var_idx;
3967 const struct ctables_category *c = cell->axes[a].cvs[var_idx].category;
3968 if (prev->axes[a].cvs[var_idx].category != c)
3970 else if (c->type != CCT_SUBTOTAL
3971 && c->type != CCT_TOTAL
3972 && c->type != CCT_POSTCOMPUTE
3973 && !value_equal (&prev->axes[a].cvs[var_idx].value,
3974 &cell->axes[a].cvs[var_idx].value,
3975 var_get_type (nest->vars[var_idx])))
3981 for (size_t k = n_common; k < n_levels; k++)
3983 const struct ctables_level *level = &levels[k];
3984 struct pivot_category *parent = k ? groups[k - 1] : d[a]->root;
3985 if (level->type == CTL_SUMMARY)
3987 assert (k == n_levels - 1);
3989 const struct ctables_summary_spec_set *specs = &t->summary_specs;
3990 for (size_t m = 0; m < specs->n; m++)
3992 int leaf = pivot_category_create_leaf (
3993 parent, pivot_value_new_text (specs->specs[m].label));
4000 const struct variable *var = nest->vars[level->var_idx];
4001 struct pivot_value *label;
4002 if (level->type == CTL_VAR)
4004 label = pivot_value_new_variable (var);
4005 label->variable.show = level->vlabel;
4007 else if (level->type == CTL_CATEGORY)
4009 const struct ctables_cell_value *cv = &cell->axes[a].cvs[level->var_idx];
4010 label = ctables_category_create_label (
4011 t->categories[var_get_dict_index (var)],
4012 cv->category, var, &cv->value);
4017 if (k == n_levels - 1)
4018 prev_leaf = pivot_category_create_leaf (parent, label);
4020 groups[k] = pivot_category_create_group__ (parent, label);
4024 cell->axes[a].leaf = prev_leaf;
4031 for (size_t i = 0; i < t->n_sections; i++)
4033 struct ctables_section *s = &t->sections[i];
4035 struct ctables_cell *cell;
4036 HMAP_FOR_EACH (cell, struct ctables_cell, node, &s->cells)
4041 const struct ctables_nest *specs_nest = s->nests[t->summary_axis];
4042 const struct ctables_summary_spec_set *specs = &specs_nest->specs[cell->sv];
4043 for (size_t j = 0; j < specs->n; j++)
4046 size_t n_dindexes = 0;
4048 if (summary_dimension)
4049 dindexes[n_dindexes++] = specs->specs[j].axis_idx;
4051 if (categories_dimension)
4053 const struct ctables_nest *clabels_nest = s->nests[t->clabels_from_axis];
4054 const struct variable *var = clabels_nest->vars[clabels_nest->n - 1];
4055 const union value *value = &cell->axes[t->clabels_from_axis].cvs[clabels_nest->n - 1].value;
4056 const struct ctables_value *ctv = ctables_value_find (t, value, var_get_width (var));
4059 dindexes[n_dindexes++] = ctv->leaf;
4062 for (enum pivot_axis_type a = 0; a < PIVOT_N_AXES; a++)
4065 int leaf = cell->axes[a].leaf;
4066 if (a == t->summary_axis && !summary_dimension)
4068 dindexes[n_dindexes++] = leaf;
4071 const struct ctables_summary_spec *ss = &specs->specs[j];
4073 struct fmt_spec format = specs->specs[j].format;
4074 bool is_ctables_format = ss->is_ctables_format;
4075 double d = (cell->postcompute
4076 ? ctables_cell_calculate_postcompute (
4077 s, cell, ss, &format, &is_ctables_format, j)
4078 : ctables_summary_value (cell, &cell->summaries[j],
4081 struct pivot_value *value;
4082 if (ct->hide_threshold != 0
4083 && d < ct->hide_threshold
4084 && ctables_summary_function_is_count (ss->function))
4086 value = pivot_value_new_user_text_nocopy (
4087 xasprintf ("<%d", ct->hide_threshold));
4089 else if (d == 0 && ct->zero)
4090 value = pivot_value_new_user_text (ct->zero, SIZE_MAX);
4091 else if (d == SYSMIS && ct->missing)
4092 value = pivot_value_new_user_text (ct->missing, SIZE_MAX);
4093 else if (is_ctables_format)
4095 char *s = data_out_stretchy (&(union value) { .f = d },
4097 &ct->ctables_formats, NULL);
4098 value = pivot_value_new_user_text_nocopy (s);
4102 value = pivot_value_new_number (d);
4103 value->numeric.format = format;
4105 pivot_table_put (pt, dindexes, n_dindexes, value);
4110 pivot_table_submit (pt);
4114 ctables_check_label_position (struct ctables_table *t, enum pivot_axis_type a)
4116 enum pivot_axis_type label_pos = t->label_axis[a];
4120 t->clabels_from_axis = a;
4122 const char *subcommand_name = a == PIVOT_AXIS_ROW ? "ROWLABELS" : "COLLABELS";
4123 const char *pos_name = label_pos == PIVOT_AXIS_LAYER ? "LAYER" : "OPPOSITE";
4125 const struct ctables_stack *stack = &t->stacks[a];
4129 const struct ctables_nest *n0 = &stack->nests[0];
4131 const struct variable *v0 = n0->vars[n0->n - 1];
4132 struct ctables_categories *c0 = t->categories[var_get_dict_index (v0)];
4133 t->clabels_example = v0;
4135 for (size_t i = 0; i < c0->n_cats; i++)
4136 if (c0->cats[i].type == CCT_FUNCTION)
4138 msg (SE, _("%s=%s is not allowed with sorting based "
4139 "on a summary function."),
4140 subcommand_name, pos_name);
4143 if (n0->n - 1 == n0->scale_idx)
4145 msg (SE, _("%s=%s requires the variables to be moved to be categorical, "
4146 "but %s is a scale variable."),
4147 subcommand_name, pos_name, var_get_name (v0));
4151 for (size_t i = 1; i < stack->n; i++)
4153 const struct ctables_nest *ni = &stack->nests[i];
4155 const struct variable *vi = ni->vars[ni->n - 1];
4156 struct ctables_categories *ci = t->categories[var_get_dict_index (vi)];
4158 if (ni->n - 1 == ni->scale_idx)
4160 msg (SE, _("%s=%s requires the variables to be moved to be "
4161 "categorical, but %s is a scale variable."),
4162 subcommand_name, pos_name, var_get_name (vi));
4165 if (var_get_width (v0) != var_get_width (vi))
4167 msg (SE, _("%s=%s requires the variables to be "
4168 "moved to have the same width, but %s has "
4169 "width %d and %s has width %d."),
4170 subcommand_name, pos_name,
4171 var_get_name (v0), var_get_width (v0),
4172 var_get_name (vi), var_get_width (vi));
4175 if (!val_labs_equal (var_get_value_labels (v0),
4176 var_get_value_labels (vi)))
4178 msg (SE, _("%s=%s requires the variables to be "
4179 "moved to have the same value labels, but %s "
4180 "and %s have different value labels."),
4181 subcommand_name, pos_name,
4182 var_get_name (v0), var_get_name (vi));
4185 if (!ctables_categories_equal (c0, ci))
4187 msg (SE, _("%s=%s requires the variables to be "
4188 "moved to have the same category "
4189 "specifications, but %s and %s have different "
4190 "category specifications."),
4191 subcommand_name, pos_name,
4192 var_get_name (v0), var_get_name (vi));
4201 ctables_prepare_table (struct ctables_table *t)
4203 for (enum pivot_axis_type a = 0; a < PIVOT_N_AXES; a++)
4206 t->stacks[a] = enumerate_fts (a, t->axes[a]);
4208 for (size_t j = 0; j < t->stacks[a].n; j++)
4210 struct ctables_nest *nest = &t->stacks[a].nests[j];
4211 for (enum ctables_domain_type dt = 0; dt < N_CTDTS; dt++)
4213 nest->domains[dt] = xmalloc (nest->n * sizeof *nest->domains[dt]);
4214 nest->n_domains[dt] = 0;
4216 for (size_t k = 0; k < nest->n; k++)
4218 if (k == nest->scale_idx)
4227 if (a != PIVOT_AXIS_LAYER)
4234 if (dt == CTDT_SUBTABLE ? a != PIVOT_AXIS_LAYER
4235 : dt == CTDT_ROW ? a == PIVOT_AXIS_COLUMN
4236 : a == PIVOT_AXIS_ROW)
4238 if (k == nest->n - 1
4239 || (nest->scale_idx == nest->n - 1
4240 && k == nest->n - 2))
4246 if (a == PIVOT_AXIS_COLUMN)
4251 if (a == PIVOT_AXIS_ROW)
4256 nest->domains[dt][nest->n_domains[dt]++] = k;
4263 struct ctables_nest *nest = xmalloc (sizeof *nest);
4264 *nest = (struct ctables_nest) { .n = 0 };
4265 t->stacks[a] = (struct ctables_stack) { .nests = nest, .n = 1 };
4268 struct ctables_stack *stack = &t->stacks[t->summary_axis];
4269 for (size_t i = 0; i < stack->n; i++)
4271 struct ctables_nest *nest = &stack->nests[i];
4272 if (!nest->specs[CSV_CELL].n)
4274 struct ctables_summary_spec_set *specs = &nest->specs[CSV_CELL];
4275 specs->specs = xmalloc (sizeof *specs->specs);
4278 enum ctables_summary_function function
4279 = specs->is_scale ? CTSF_MEAN : CTSF_COUNT;
4281 *specs->specs = (struct ctables_summary_spec) {
4282 .function = function,
4283 .format = ctables_summary_default_format (function, specs->var),
4284 .label = ctables_summary_default_label (function, 0),
4287 specs->var = nest->vars[0];
4289 ctables_summary_spec_set_clone (&nest->specs[CSV_TOTAL],
4290 &nest->specs[CSV_CELL]);
4292 else if (!nest->specs[CSV_TOTAL].n)
4293 ctables_summary_spec_set_clone (&nest->specs[CSV_TOTAL],
4294 &nest->specs[CSV_CELL]);
4296 if (t->ctables->smissing_listwise)
4298 struct variable **listwise_vars = NULL;
4300 size_t allocated = 0;
4302 for (size_t j = nest->group_head; j < stack->n; j++)
4304 const struct ctables_nest *other_nest = &stack->nests[j];
4305 if (other_nest->group_head != nest->group_head)
4308 if (nest != other_nest && other_nest->scale_idx < other_nest->n)
4311 listwise_vars = x2nrealloc (listwise_vars, &allocated,
4312 sizeof *listwise_vars);
4313 listwise_vars[n++] = other_nest->vars[other_nest->scale_idx];
4316 for (size_t j = 0; j < N_CSVS; j++)
4318 nest->specs[j].listwise_vars = listwise_vars;
4319 nest->specs[j].n_listwise_vars = n;
4324 struct ctables_summary_spec_set *merged = &t->summary_specs;
4325 struct merge_item *items = xnmalloc (2 * stack->n, sizeof *items);
4327 for (size_t j = 0; j < stack->n; j++)
4329 const struct ctables_nest *nest = &stack->nests[j];
4331 for (enum ctables_summary_variant sv = 0; sv < N_CSVS; sv++)
4332 items[n_left++] = (struct merge_item) { .set = &nest->specs[sv] };
4337 struct merge_item min = items[0];
4338 for (size_t j = 1; j < n_left; j++)
4339 if (merge_item_compare_3way (&items[j], &min) < 0)
4342 if (merged->n >= merged->allocated)
4343 merged->specs = x2nrealloc (merged->specs, &merged->allocated,
4344 sizeof *merged->specs);
4345 merged->specs[merged->n++] = min.set->specs[min.ofs];
4347 for (size_t j = 0; j < n_left; )
4349 if (merge_item_compare_3way (&items[j], &min) == 0)
4351 struct merge_item *item = &items[j];
4352 item->set->specs[item->ofs].axis_idx = merged->n - 1;
4353 if (++item->ofs >= item->set->n)
4355 items[j] = items[--n_left];
4364 for (size_t j = 0; j < merged->n; j++)
4365 printf ("%s\n", ctables_summary_function_name (merged->specs[j].function));
4367 for (size_t j = 0; j < stack->n; j++)
4369 const struct ctables_nest *nest = &stack->nests[j];
4370 for (enum ctables_summary_variant sv = 0; sv < N_CSVS; sv++)
4372 const struct ctables_summary_spec_set *specs = &nest->specs[sv];
4373 for (size_t k = 0; k < specs->n; k++)
4374 printf ("(%s, %zu) ", ctables_summary_function_name (specs->specs[k].function),
4375 specs->specs[k].axis_idx);
4381 return (ctables_check_label_position (t, PIVOT_AXIS_ROW)
4382 && ctables_check_label_position (t, PIVOT_AXIS_COLUMN));
4386 ctables_insert_clabels_values (struct ctables_table *t, const struct ccase *c,
4387 enum pivot_axis_type a)
4389 struct ctables_stack *stack = &t->stacks[a];
4390 for (size_t i = 0; i < stack->n; i++)
4392 const struct ctables_nest *nest = &stack->nests[i];
4393 const struct variable *var = nest->vars[nest->n - 1];
4394 const union value *value = case_data (c, var);
4396 if (var_is_numeric (var) && value->f == SYSMIS)
4399 if (ctables_categories_match (t->categories [var_get_dict_index (var)],
4401 ctables_value_insert (t, value, var_get_width (var));
4406 compare_clabels_values_3way (const void *a_, const void *b_, const void *width_)
4408 const struct ctables_value *const *ap = a_;
4409 const struct ctables_value *const *bp = b_;
4410 const struct ctables_value *a = *ap;
4411 const struct ctables_value *b = *bp;
4412 const int *width = width_;
4413 return value_compare_3way (&a->value, &b->value, *width);
4417 ctables_sort_clabels_values (struct ctables_table *t)
4419 const struct variable *v0 = t->clabels_example;
4420 int width = var_get_width (v0);
4422 struct ctables_categories *c0 = t->categories[var_get_dict_index (v0)];
4425 const struct val_labs *val_labs = var_get_value_labels (v0);
4426 for (const struct val_lab *vl = val_labs_first (val_labs); vl;
4427 vl = val_labs_next (val_labs, vl))
4428 if (ctables_categories_match (c0, &vl->value, v0))
4429 ctables_value_insert (t, &vl->value, width);
4432 size_t n = hmap_count (&t->clabels_values_map);
4433 t->clabels_values = xnmalloc (n, sizeof *t->clabels_values);
4435 struct ctables_value *clv;
4437 HMAP_FOR_EACH (clv, struct ctables_value, node, &t->clabels_values_map)
4438 t->clabels_values[i++] = clv;
4439 t->n_clabels_values = n;
4442 sort (t->clabels_values, n, sizeof *t->clabels_values,
4443 compare_clabels_values_3way, &width);
4445 for (size_t i = 0; i < n; i++)
4446 t->clabels_values[i]->leaf = i;
4450 ctables_add_category_occurrences (const struct variable *var,
4451 struct hmap *occurrences,
4452 const struct ctables_categories *cats)
4454 const struct val_labs *val_labs = var_get_value_labels (var);
4456 for (size_t i = 0; i < cats->n_cats; i++)
4458 const struct ctables_category *c = &cats->cats[i];
4462 ctables_add_occurrence (var, &(const union value) { .f = c->number },
4468 int width = var_get_width (var);
4470 value_init (&value, width);
4471 value_copy_buf_rpad (&value, width,
4472 CHAR_CAST (uint8_t *, c->string.string),
4473 c->string.length, ' ');
4474 ctables_add_occurrence (var, &value, occurrences);
4475 value_destroy (&value, width);
4480 assert (var_is_numeric (var));
4481 for (const struct val_lab *vl = val_labs_first (val_labs); vl;
4482 vl = val_labs_next (val_labs, vl))
4483 if (vl->value.f >= c->nrange[0] && vl->value.f <= c->nrange[1])
4484 ctables_add_occurrence (var, &vl->value, occurrences);
4488 assert (var_is_alpha (var));
4489 for (const struct val_lab *vl = val_labs_first (val_labs); vl;
4490 vl = val_labs_next (val_labs, vl))
4491 if (in_string_range (&vl->value, var, c->srange))
4492 ctables_add_occurrence (var, &vl->value, occurrences);
4496 for (const struct val_lab *vl = val_labs_first (val_labs); vl;
4497 vl = val_labs_next (val_labs, vl))
4498 if (var_is_value_missing (var, &vl->value))
4499 ctables_add_occurrence (var, &vl->value, occurrences);
4503 for (const struct val_lab *vl = val_labs_first (val_labs); vl;
4504 vl = val_labs_next (val_labs, vl))
4505 ctables_add_occurrence (var, &vl->value, occurrences);
4508 case CCT_POSTCOMPUTE:
4518 for (const struct val_lab *vl = val_labs_first (val_labs); vl;
4519 vl = val_labs_next (val_labs, vl))
4520 if (c->include_missing || !var_is_value_missing (var, &vl->value))
4521 ctables_add_occurrence (var, &vl->value, occurrences);
4524 case CCT_EXCLUDED_MISSING:
4531 ctables_section_recurse_add_empty_categories (
4532 struct ctables_section *s,
4533 const struct ctables_category *cats[PIVOT_N_AXES][10], struct ccase *c,
4534 enum pivot_axis_type a, size_t a_idx)
4536 if (a >= PIVOT_N_AXES)
4537 ctables_cell_insert__ (s, c, cats);
4538 else if (!s->nests[a] || a_idx >= s->nests[a]->n)
4539 ctables_section_recurse_add_empty_categories (s, cats, c, a + 1, 0);
4542 const struct variable *var = s->nests[a]->vars[a_idx];
4543 const struct ctables_categories *categories = s->table->categories[
4544 var_get_dict_index (var)];
4545 int width = var_get_width (var);
4546 const struct hmap *occurrences = &s->occurrences[a][a_idx];
4547 const struct ctables_occurrence *o;
4548 HMAP_FOR_EACH (o, struct ctables_occurrence, node, occurrences)
4550 union value *value = case_data_rw (c, var);
4551 value_destroy (value, width);
4552 value_clone (value, &o->value, width);
4553 cats[a][a_idx] = ctables_categories_match (categories, value, var);
4554 assert (cats[a][a_idx] != NULL);
4555 ctables_section_recurse_add_empty_categories (s, cats, c, a, a_idx + 1);
4558 for (size_t i = 0; i < categories->n_cats; i++)
4560 const struct ctables_category *cat = &categories->cats[i];
4561 if (cat->type == CCT_POSTCOMPUTE)
4563 cats[a][a_idx] = cat;
4564 ctables_section_recurse_add_empty_categories (s, cats, c, a, a_idx + 1);
4571 ctables_section_add_empty_categories (struct ctables_section *s)
4573 bool show_empty = false;
4574 for (size_t a = 0; a < PIVOT_N_AXES; a++)
4576 for (size_t k = 0; k < s->nests[a]->n; k++)
4577 if (k != s->nests[a]->scale_idx)
4579 const struct variable *var = s->nests[a]->vars[k];
4580 const struct ctables_categories *cats = s->table->categories[
4581 var_get_dict_index (var)];
4582 if (cats->show_empty)
4585 ctables_add_category_occurrences (var, &s->occurrences[a][k], cats);
4591 const struct ctables_category *cats[PIVOT_N_AXES][10]; /* XXX */
4592 struct ccase *c = case_create (dict_get_proto (s->table->ctables->dict));
4593 ctables_section_recurse_add_empty_categories (s, cats, c, 0, 0);
4598 ctables_execute (struct dataset *ds, struct ctables *ct)
4600 for (size_t i = 0; i < ct->n_tables; i++)
4602 struct ctables_table *t = ct->tables[i];
4603 t->sections = xnmalloc (MAX (1, t->stacks[PIVOT_AXIS_ROW].n) *
4604 MAX (1, t->stacks[PIVOT_AXIS_COLUMN].n) *
4605 MAX (1, t->stacks[PIVOT_AXIS_LAYER].n),
4606 sizeof *t->sections);
4607 size_t ix[PIVOT_N_AXES];
4608 ctables_table_add_section (t, 0, ix);
4611 struct casereader *input = proc_open (ds);
4612 bool warn_on_invalid = true;
4613 for (struct ccase *c = casereader_read (input); c;
4614 case_unref (c), c = casereader_read (input))
4616 double d_weight = dict_get_case_weight (dataset_dict (ds), c,
4618 double e_weight = (ct->e_weight
4619 ? var_force_valid_weight (ct->e_weight,
4620 case_num (c, ct->e_weight),
4624 for (size_t i = 0; i < ct->n_tables; i++)
4626 struct ctables_table *t = ct->tables[i];
4628 for (size_t j = 0; j < t->n_sections; j++)
4629 ctables_cell_insert (&t->sections[j], c, d_weight, e_weight);
4631 for (enum pivot_axis_type a = 0; a < PIVOT_N_AXES; a++)
4632 if (t->label_axis[a] != a)
4633 ctables_insert_clabels_values (t, c, a);
4636 casereader_destroy (input);
4638 for (size_t i = 0; i < ct->n_tables; i++)
4640 struct ctables_table *t = ct->tables[i];
4642 if (t->clabels_example)
4643 ctables_sort_clabels_values (t);
4645 for (size_t j = 0; j < t->n_sections; j++)
4646 ctables_section_add_empty_categories (&t->sections[j]);
4648 ctables_table_output (ct, ct->tables[i]);
4650 return proc_commit (ds);
4655 typedef struct ctables_pcexpr *parse_recursively_func (struct lexer *,
4656 struct dictionary *);
4659 ctables_pcexpr_destroy (struct ctables_pcexpr *e)
4665 case CTPO_CAT_STRING:
4666 ss_dealloc (&e->string);
4675 for (size_t i = 0; i < 2; i++)
4676 ctables_pcexpr_destroy (e->subs[i]);
4680 case CTPO_CAT_NUMBER:
4681 case CTPO_CAT_RANGE:
4682 case CTPO_CAT_MISSING:
4683 case CTPO_CAT_OTHERNM:
4684 case CTPO_CAT_SUBTOTAL:
4685 case CTPO_CAT_TOTAL:
4689 msg_location_destroy (e->location);
4694 static struct ctables_pcexpr *
4695 ctables_pcexpr_allocate_binary (enum ctables_postcompute_op op,
4696 struct ctables_pcexpr *sub0,
4697 struct ctables_pcexpr *sub1)
4699 struct ctables_pcexpr *e = xmalloc (sizeof *e);
4700 *e = (struct ctables_pcexpr) {
4702 .subs = { sub0, sub1 },
4703 .location = msg_location_merged (sub0->location, sub1->location),
4708 /* How to parse an operator. */
4711 enum token_type token;
4712 enum ctables_postcompute_op op;
4715 static const struct operator *
4716 ctable_pcexpr_match_operator (struct lexer *lexer,
4717 const struct operator ops[], size_t n_ops)
4719 for (const struct operator *op = ops; op < ops + n_ops; op++)
4720 if (lex_token (lexer) == op->token)
4722 if (op->token != T_NEG_NUM)
4731 static struct ctables_pcexpr *
4732 ctable_pcexpr_parse_binary_operators__ (
4733 struct lexer *lexer, struct dictionary *dict,
4734 const struct operator ops[], size_t n_ops,
4735 parse_recursively_func *parse_next_level,
4736 const char *chain_warning, struct ctables_pcexpr *lhs)
4738 for (int op_count = 0; ; op_count++)
4740 const struct operator *op
4741 = ctable_pcexpr_match_operator (lexer, ops, n_ops);
4744 if (op_count > 1 && chain_warning)
4745 msg_at (SW, lhs->location, "%s", chain_warning);
4750 struct ctables_pcexpr *rhs = parse_next_level (lexer, dict);
4753 ctables_pcexpr_destroy (lhs);
4757 lhs = ctables_pcexpr_allocate_binary (op->op, lhs, rhs);
4761 static struct ctables_pcexpr *
4762 ctable_pcexpr_parse_binary_operators (struct lexer *lexer,
4763 struct dictionary *dict,
4764 const struct operator ops[], size_t n_ops,
4765 parse_recursively_func *parse_next_level,
4766 const char *chain_warning)
4768 struct ctables_pcexpr *lhs = parse_next_level (lexer, dict);
4772 return ctable_pcexpr_parse_binary_operators__ (lexer, dict, ops, n_ops,
4774 chain_warning, lhs);
4777 static struct ctables_pcexpr *ctable_pcexpr_parse_add (struct lexer *,
4778 struct dictionary *);
4780 static struct ctables_pcexpr
4781 ctpo_cat_range (double low, double high)
4783 return (struct ctables_pcexpr) {
4784 .op = CTPO_CAT_RANGE,
4785 .range = { low, high },
4789 static struct ctables_pcexpr *
4790 ctable_pcexpr_parse_primary (struct lexer *lexer, struct dictionary *dict)
4792 int start_ofs = lex_ofs (lexer);
4793 struct ctables_pcexpr e;
4794 if (lex_is_number (lexer))
4796 e = (struct ctables_pcexpr) { .op = CTPO_CONSTANT,
4797 .number = lex_number (lexer) };
4800 else if (lex_match_id (lexer, "MISSING"))
4801 e = (struct ctables_pcexpr) { .op = CTPO_CAT_MISSING };
4802 else if (lex_match_id (lexer, "OTHERNM"))
4803 e = (struct ctables_pcexpr) { .op = CTPO_CAT_OTHERNM };
4804 else if (lex_match_id (lexer, "TOTAL"))
4805 e = (struct ctables_pcexpr) { .op = CTPO_CAT_TOTAL };
4806 else if (lex_match_id (lexer, "SUBTOTAL"))
4808 size_t subtotal_index = 0;
4809 if (lex_match (lexer, T_LBRACK))
4811 if (!lex_force_int_range (lexer, "SUBTOTAL", 1, LONG_MAX))
4813 subtotal_index = lex_integer (lexer);
4815 if (!lex_force_match (lexer, T_RBRACK))
4818 e = (struct ctables_pcexpr) { .op = CTPO_CAT_SUBTOTAL,
4819 .subtotal_index = subtotal_index };
4821 else if (lex_match (lexer, T_LBRACK))
4823 if (lex_match_id (lexer, "LO"))
4825 if (!lex_force_match_id (lexer, "THRU") || lex_force_num (lexer))
4827 e = ctpo_cat_range (-DBL_MAX, lex_number (lexer));
4830 else if (lex_is_number (lexer))
4832 double number = lex_number (lexer);
4834 if (lex_match_id (lexer, "THRU"))
4836 if (lex_match_id (lexer, "HI"))
4837 e = ctpo_cat_range (number, DBL_MAX);
4840 if (!lex_force_num (lexer))
4842 e = ctpo_cat_range (number, lex_number (lexer));
4847 e = (struct ctables_pcexpr) { .op = CTPO_CAT_NUMBER,
4850 else if (lex_is_string (lexer))
4852 struct substring s = recode_substring_pool (
4853 dict_get_encoding (dict), "UTF-8", lex_tokss (lexer), NULL);
4854 ss_rtrim (&s, ss_cstr (" "));
4856 e = (struct ctables_pcexpr) { .op = CTPO_CAT_STRING, .string = s };
4861 lex_error (lexer, NULL);
4865 if (!lex_force_match (lexer, T_RBRACK))
4867 if (e.op == CTPO_CAT_STRING)
4868 ss_dealloc (&e.string);
4872 else if (lex_match (lexer, T_LPAREN))
4874 struct ctables_pcexpr *ep = ctable_pcexpr_parse_add (lexer, dict);
4877 if (!lex_force_match (lexer, T_RPAREN))
4879 ctables_pcexpr_destroy (ep);
4886 lex_error (lexer, NULL);
4890 e.location = lex_ofs_location (lexer, start_ofs, lex_ofs (lexer) - 1);
4891 return xmemdup (&e, sizeof e);
4894 static struct ctables_pcexpr *
4895 ctables_pcexpr_allocate_neg (struct ctables_pcexpr *sub,
4896 struct lexer *lexer, int start_ofs)
4898 struct ctables_pcexpr *e = xmalloc (sizeof *e);
4899 *e = (struct ctables_pcexpr) {
4902 .location = lex_ofs_location (lexer, start_ofs, lex_ofs (lexer) - 1),
4907 static struct ctables_pcexpr *
4908 ctable_pcexpr_parse_exp (struct lexer *lexer, struct dictionary *dict)
4910 static const struct operator op = { T_EXP, CTPO_POW };
4912 const char *chain_warning =
4913 _("The exponentiation operator (`**') is left-associative: "
4914 "`a**b**c' equals `(a**b)**c', not `a**(b**c)'. "
4915 "To disable this warning, insert parentheses.");
4917 if (lex_token (lexer) != T_NEG_NUM || lex_next_token (lexer, 1) != T_EXP)
4918 return ctable_pcexpr_parse_binary_operators (lexer, dict, &op, 1,
4919 ctable_pcexpr_parse_primary,
4922 /* Special case for situations like "-5**6", which must be parsed as
4925 int start_ofs = lex_ofs (lexer);
4926 struct ctables_pcexpr *lhs = xmalloc (sizeof *lhs);
4927 *lhs = (struct ctables_pcexpr) {
4928 .op = CTPO_CONSTANT,
4929 .number = -lex_tokval (lexer),
4930 .location = lex_ofs_location (lexer, start_ofs, lex_ofs (lexer)),
4934 struct ctables_pcexpr *node = ctable_pcexpr_parse_binary_operators__ (
4935 lexer, dict, &op, 1,
4936 ctable_pcexpr_parse_primary, chain_warning, lhs);
4940 return ctables_pcexpr_allocate_neg (node, lexer, start_ofs);
4943 /* Parses the unary minus level. */
4944 static struct ctables_pcexpr *
4945 ctable_pcexpr_parse_neg (struct lexer *lexer, struct dictionary *dict)
4947 int start_ofs = lex_ofs (lexer);
4948 if (!lex_match (lexer, T_DASH))
4949 return ctable_pcexpr_parse_exp (lexer, dict);
4951 struct ctables_pcexpr *inner = ctable_pcexpr_parse_neg (lexer, dict);
4955 return ctables_pcexpr_allocate_neg (inner, lexer, start_ofs);
4958 /* Parses the multiplication and division level. */
4959 static struct ctables_pcexpr *
4960 ctable_pcexpr_parse_mul (struct lexer *lexer, struct dictionary *dict)
4962 static const struct operator ops[] =
4964 { T_ASTERISK, CTPO_MUL },
4965 { T_SLASH, CTPO_DIV },
4968 return ctable_pcexpr_parse_binary_operators (lexer, dict, ops,
4969 sizeof ops / sizeof *ops,
4970 ctable_pcexpr_parse_neg, NULL);
4973 /* Parses the addition and subtraction level. */
4974 static struct ctables_pcexpr *
4975 ctable_pcexpr_parse_add (struct lexer *lexer, struct dictionary *dict)
4977 static const struct operator ops[] =
4979 { T_PLUS, CTPO_ADD },
4980 { T_DASH, CTPO_SUB },
4981 { T_NEG_NUM, CTPO_ADD },
4984 return ctable_pcexpr_parse_binary_operators (lexer, dict,
4985 ops, sizeof ops / sizeof *ops,
4986 ctable_pcexpr_parse_mul, NULL);
4989 static struct ctables_postcompute *
4990 ctables_find_postcompute (struct ctables *ct, const char *name)
4992 struct ctables_postcompute *pc;
4993 HMAP_FOR_EACH_WITH_HASH (pc, struct ctables_postcompute, hmap_node,
4994 utf8_hash_case_string (name, 0), &ct->postcomputes)
4995 if (!utf8_strcasecmp (pc->name, name))
5001 ctables_parse_pcompute (struct lexer *lexer, struct dictionary *dict,
5004 int pcompute_start = lex_ofs (lexer) - 1;
5006 if (!lex_force_match (lexer, T_AND) || !lex_force_id (lexer))
5009 char *name = ss_xstrdup (lex_tokss (lexer));
5012 if (!lex_force_match (lexer, T_EQUALS)
5013 || !lex_force_match_id (lexer, "EXPR")
5014 || !lex_force_match (lexer, T_LPAREN))
5020 int expr_start = lex_ofs (lexer);
5021 struct ctables_pcexpr *expr = ctable_pcexpr_parse_add (lexer, dict);
5022 int expr_end = lex_ofs (lexer) - 1;
5023 if (!expr || !lex_force_match (lexer, T_RPAREN))
5028 int pcompute_end = lex_ofs (lexer) - 1;
5030 struct msg_location *location = lex_ofs_location (lexer, pcompute_start,
5033 struct ctables_postcompute *pc = ctables_find_postcompute (ct, name);
5036 msg_at (SW, location, _("New definition of &%s will override the "
5037 "previous definition."),
5039 msg_at (SN, pc->location, _("This is the previous definition."));
5041 ctables_pcexpr_destroy (pc->expr);
5042 msg_location_destroy (pc->location);
5047 pc = xmalloc (sizeof *pc);
5048 *pc = (struct ctables_postcompute) { .name = name };
5049 hmap_insert (&ct->postcomputes, &pc->hmap_node,
5050 utf8_hash_case_string (pc->name, 0));
5053 pc->location = location;
5055 pc->label = lex_ofs_representation (lexer, expr_start, expr_end);
5060 ctables_parse_pproperties_format (struct lexer *lexer,
5061 struct ctables_summary_spec_set *sss)
5063 *sss = (struct ctables_summary_spec_set) { .n = 0 };
5065 while (lex_token (lexer) != T_ENDCMD && lex_token (lexer) != T_SLASH
5066 && !(lex_token (lexer) == T_ID
5067 && (lex_id_match (ss_cstr ("LABEL"), lex_tokss (lexer))
5068 || lex_id_match (ss_cstr ("HIDESOURCECATS"),
5069 lex_tokss (lexer)))))
5071 /* Parse function. */
5072 enum ctables_summary_function function;
5073 if (!parse_ctables_summary_function (lexer, &function))
5076 /* Parse percentile. */
5077 double percentile = 0;
5078 if (function == CTSF_PTILE)
5080 if (!lex_force_num_range_closed (lexer, "PTILE", 0, 100))
5082 percentile = lex_number (lexer);
5087 struct fmt_spec format;
5088 bool is_ctables_format;
5089 if (!parse_ctables_format_specifier (lexer, &format, &is_ctables_format))
5092 if (sss->n >= sss->allocated)
5093 sss->specs = x2nrealloc (sss->specs, &sss->allocated,
5094 sizeof *sss->specs);
5095 sss->specs[sss->n++] = (struct ctables_summary_spec) {
5096 .function = function,
5097 .percentile = percentile,
5099 .is_ctables_format = is_ctables_format,
5105 ctables_summary_spec_set_uninit (sss);
5110 ctables_parse_pproperties (struct lexer *lexer, struct ctables *ct)
5112 struct ctables_postcompute **pcs = NULL;
5114 size_t allocated_pcs = 0;
5116 while (lex_match (lexer, T_AND))
5118 if (!lex_force_id (lexer))
5120 struct ctables_postcompute *pc
5121 = ctables_find_postcompute (ct, lex_tokcstr (lexer));
5124 msg (SE, _("Unknown computed category &%s."), lex_tokcstr (lexer));
5129 if (n_pcs >= allocated_pcs)
5130 pcs = x2nrealloc (pcs, &allocated_pcs, sizeof *pcs);
5134 while (lex_token (lexer) != T_SLASH && lex_token (lexer) != T_ENDCMD)
5136 if (lex_match_id (lexer, "LABEL"))
5138 lex_match (lexer, T_EQUALS);
5139 if (!lex_force_string (lexer))
5142 for (size_t i = 0; i < n_pcs; i++)
5144 free (pcs[i]->label);
5145 pcs[i]->label = ss_xstrdup (lex_tokss (lexer));
5150 else if (lex_match_id (lexer, "FORMAT"))
5152 lex_match (lexer, T_EQUALS);
5154 struct ctables_summary_spec_set sss;
5155 if (!ctables_parse_pproperties_format (lexer, &sss))
5158 for (size_t i = 0; i < n_pcs; i++)
5161 ctables_summary_spec_set_uninit (pcs[i]->specs);
5163 pcs[i]->specs = xmalloc (sizeof *pcs[i]->specs);
5164 ctables_summary_spec_set_clone (pcs[i]->specs, &sss);
5166 ctables_summary_spec_set_uninit (&sss);
5168 else if (lex_match_id (lexer, "HIDESOURCECATS"))
5170 lex_match (lexer, T_EQUALS);
5171 bool hide_source_cats;
5172 if (!parse_bool (lexer, &hide_source_cats))
5174 for (size_t i = 0; i < n_pcs; i++)
5175 pcs[i]->hide_source_cats = hide_source_cats;
5179 lex_error_expecting (lexer, "LABEL", "FORMAT", "HIDESOURCECATS");
5192 put_strftime (struct string *out, time_t now, const char *format)
5194 const struct tm *tm = localtime (&now);
5196 strftime (value, sizeof value, format, tm);
5197 ds_put_cstr (out, value);
5201 skip_prefix (struct substring *s, struct substring prefix)
5203 if (ss_starts_with (*s, prefix))
5205 ss_advance (s, prefix.length);
5213 put_table_expression (struct string *out, struct lexer *lexer,
5214 struct dictionary *dict, int expr_start, int expr_end)
5217 for (int ofs = expr_start; ofs < expr_end; ofs++)
5219 const struct token *t = lex_ofs_token (lexer, ofs);
5220 if (t->type == T_LBRACK)
5222 else if (t->type == T_RBRACK && nest > 0)
5228 else if (t->type == T_ID)
5230 const struct variable *var
5231 = dict_lookup_var (dict, t->string.string);
5232 const char *label = var ? var_get_label (var) : NULL;
5233 ds_put_cstr (out, label ? label : t->string.string);
5237 if (ofs != expr_start && t->type != T_RPAREN && ds_last (out) != ' ')
5238 ds_put_byte (out, ' ');
5240 char *repr = lex_ofs_representation (lexer, ofs, ofs);
5241 ds_put_cstr (out, repr);
5244 if (ofs + 1 != expr_end && t->type != T_LPAREN)
5245 ds_put_byte (out, ' ');
5251 put_title_text (struct string *out, struct substring in, time_t now,
5252 struct lexer *lexer, struct dictionary *dict,
5253 int expr_start, int expr_end)
5257 size_t chunk = ss_find_byte (in, ')');
5258 ds_put_substring (out, ss_head (in, chunk));
5259 ss_advance (&in, chunk);
5260 if (ss_is_empty (in))
5263 if (skip_prefix (&in, ss_cstr (")DATE")))
5264 put_strftime (out, now, "%x");
5265 else if (skip_prefix (&in, ss_cstr (")TIME")))
5266 put_strftime (out, now, "%X");
5267 else if (skip_prefix (&in, ss_cstr (")TABLE")))
5268 put_table_expression (out, lexer, dict, expr_start, expr_end);
5271 ds_put_byte (out, ')');
5272 ss_advance (&in, 1);
5278 cmd_ctables (struct lexer *lexer, struct dataset *ds)
5280 size_t n_vars = dict_get_n_vars (dataset_dict (ds));
5281 enum ctables_vlabel *vlabels = xnmalloc (n_vars, sizeof *vlabels);
5282 enum settings_value_show tvars = settings_get_show_variables ();
5283 for (size_t i = 0; i < n_vars; i++)
5284 vlabels[i] = (enum ctables_vlabel) tvars;
5286 struct pivot_table_look *look = pivot_table_look_unshare (
5287 pivot_table_look_ref (pivot_table_look_get_default ()));
5288 look->omit_empty = false;
5290 struct ctables *ct = xmalloc (sizeof *ct);
5291 *ct = (struct ctables) {
5292 .dict = dataset_dict (ds),
5294 .ctables_formats = FMT_SETTINGS_INIT,
5296 .postcomputes = HMAP_INITIALIZER (ct->postcomputes),
5299 time_t now = time (NULL);
5304 const char *dot_string;
5305 const char *comma_string;
5307 static const struct ctf ctfs[4] = {
5308 { CTEF_NEGPAREN, "(,,,)", "(...)" },
5309 { CTEF_NEQUAL, "-,N=,,", "-.N=.." },
5310 { CTEF_PAREN, "-,(,),", "-.(.)." },
5311 { CTEF_PCTPAREN, "-,(,%),", "-.(.%)." },
5313 bool is_dot = settings_get_fmt_settings ()->decimal == '.';
5314 for (size_t i = 0; i < 4; i++)
5316 const char *s = is_dot ? ctfs[i].dot_string : ctfs[i].comma_string;
5317 fmt_settings_set_cc (&ct->ctables_formats, ctfs[i].type,
5318 fmt_number_style_from_string (s));
5321 if (!lex_force_match (lexer, T_SLASH))
5324 while (!lex_match_id (lexer, "TABLE"))
5326 if (lex_match_id (lexer, "FORMAT"))
5328 double widths[2] = { SYSMIS, SYSMIS };
5329 double units_per_inch = 72.0;
5331 while (lex_token (lexer) != T_SLASH)
5333 if (lex_match_id (lexer, "MINCOLWIDTH"))
5335 if (!parse_col_width (lexer, "MINCOLWIDTH", &widths[0]))
5338 else if (lex_match_id (lexer, "MAXCOLWIDTH"))
5340 if (!parse_col_width (lexer, "MAXCOLWIDTH", &widths[1]))
5343 else if (lex_match_id (lexer, "UNITS"))
5345 lex_match (lexer, T_EQUALS);
5346 if (lex_match_id (lexer, "POINTS"))
5347 units_per_inch = 72.0;
5348 else if (lex_match_id (lexer, "INCHES"))
5349 units_per_inch = 1.0;
5350 else if (lex_match_id (lexer, "CM"))
5351 units_per_inch = 2.54;
5354 lex_error_expecting (lexer, "POINTS", "INCHES", "CM");
5358 else if (lex_match_id (lexer, "EMPTY"))
5363 lex_match (lexer, T_EQUALS);
5364 if (lex_match_id (lexer, "ZERO"))
5366 /* Nothing to do. */
5368 else if (lex_match_id (lexer, "BLANK"))
5369 ct->zero = xstrdup ("");
5370 else if (lex_force_string (lexer))
5372 ct->zero = ss_xstrdup (lex_tokss (lexer));
5378 else if (lex_match_id (lexer, "MISSING"))
5380 lex_match (lexer, T_EQUALS);
5381 if (!lex_force_string (lexer))
5385 ct->missing = (strcmp (lex_tokcstr (lexer), ".")
5386 ? ss_xstrdup (lex_tokss (lexer))
5392 lex_error_expecting (lexer, "MINCOLWIDTH", "MAXCOLWIDTH",
5393 "UNITS", "EMPTY", "MISSING");
5398 if (widths[0] != SYSMIS && widths[1] != SYSMIS
5399 && widths[0] > widths[1])
5401 msg (SE, _("MINCOLWIDTH must not be greater than MAXCOLWIDTH."));
5405 for (size_t i = 0; i < 2; i++)
5406 if (widths[i] != SYSMIS)
5408 int *wr = ct->look->width_ranges[TABLE_HORZ];
5409 wr[i] = widths[i] / units_per_inch * 96.0;
5414 else if (lex_match_id (lexer, "VLABELS"))
5416 if (!lex_force_match_id (lexer, "VARIABLES"))
5418 lex_match (lexer, T_EQUALS);
5420 struct variable **vars;
5422 if (!parse_variables (lexer, dataset_dict (ds), &vars, &n_vars,
5426 if (!lex_force_match_id (lexer, "DISPLAY"))
5431 lex_match (lexer, T_EQUALS);
5433 enum ctables_vlabel vlabel;
5434 if (lex_match_id (lexer, "DEFAULT"))
5435 vlabel = (enum ctables_vlabel) settings_get_show_variables ();
5436 else if (lex_match_id (lexer, "NAME"))
5438 else if (lex_match_id (lexer, "LABEL"))
5439 vlabel = CTVL_LABEL;
5440 else if (lex_match_id (lexer, "BOTH"))
5442 else if (lex_match_id (lexer, "NONE"))
5446 lex_error_expecting (lexer, "DEFAULT", "NAME", "LABEL",
5452 for (size_t i = 0; i < n_vars; i++)
5453 ct->vlabels[var_get_dict_index (vars[i])] = vlabel;
5456 else if (lex_match_id (lexer, "MRSETS"))
5458 if (!lex_force_match_id (lexer, "COUNTDUPLICATES"))
5460 lex_match (lexer, T_EQUALS);
5461 if (!parse_bool (lexer, &ct->mrsets_count_duplicates))
5464 else if (lex_match_id (lexer, "SMISSING"))
5466 if (lex_match_id (lexer, "VARIABLE"))
5467 ct->smissing_listwise = false;
5468 else if (lex_match_id (lexer, "LISTWISE"))
5469 ct->smissing_listwise = true;
5472 lex_error_expecting (lexer, "VARIABLE", "LISTWISE");
5476 else if (lex_match_id (lexer, "PCOMPUTE"))
5478 if (!ctables_parse_pcompute (lexer, dataset_dict (ds), ct))
5481 else if (lex_match_id (lexer, "PPROPERTIES"))
5483 if (!ctables_parse_pproperties (lexer, ct))
5486 else if (lex_match_id (lexer, "WEIGHT"))
5488 if (!lex_force_match_id (lexer, "VARIABLE"))
5490 lex_match (lexer, T_EQUALS);
5491 ct->e_weight = parse_variable (lexer, dataset_dict (ds));
5495 else if (lex_match_id (lexer, " HIDESMALLCOUNTS"))
5497 if (lex_match_id (lexer, "COUNT"))
5499 lex_match (lexer, T_EQUALS);
5500 if (!lex_force_int_range (lexer, "HIDESMALLCOUNTS COUNT",
5503 ct->hide_threshold = lex_integer (lexer);
5506 else if (ct->hide_threshold == 0)
5507 ct->hide_threshold = 5;
5511 lex_error_expecting (lexer, "FORMAT", "VLABELS", "MRSETS",
5512 "SMISSING", "PCOMPUTE", "PPROPERTIES",
5513 "WEIGHT", "HIDESMALLCOUNTS", "TABLE");
5517 if (!lex_force_match (lexer, T_SLASH))
5521 size_t allocated_tables = 0;
5524 if (ct->n_tables >= allocated_tables)
5525 ct->tables = x2nrealloc (ct->tables, &allocated_tables,
5526 sizeof *ct->tables);
5528 struct ctables_category *cat = xmalloc (sizeof *cat);
5529 *cat = (struct ctables_category) {
5531 .include_missing = false,
5532 .sort_ascending = true,
5535 struct ctables_categories *c = xmalloc (sizeof *c);
5536 size_t n_vars = dict_get_n_vars (dataset_dict (ds));
5537 *c = (struct ctables_categories) {
5544 struct ctables_categories **categories = xnmalloc (n_vars,
5545 sizeof *categories);
5546 for (size_t i = 0; i < n_vars; i++)
5549 struct ctables_table *t = xmalloc (sizeof *t);
5550 *t = (struct ctables_table) {
5552 .slabels_axis = PIVOT_AXIS_COLUMN,
5553 .slabels_visible = true,
5554 .clabels_values_map = HMAP_INITIALIZER (t->clabels_values_map),
5556 [PIVOT_AXIS_ROW] = PIVOT_AXIS_ROW,
5557 [PIVOT_AXIS_COLUMN] = PIVOT_AXIS_COLUMN,
5558 [PIVOT_AXIS_LAYER] = PIVOT_AXIS_LAYER,
5560 .clabels_from_axis = PIVOT_AXIS_LAYER,
5561 .categories = categories,
5562 .n_categories = n_vars,
5565 ct->tables[ct->n_tables++] = t;
5567 lex_match (lexer, T_EQUALS);
5568 int expr_start = lex_ofs (lexer);
5569 if (!ctables_axis_parse (lexer, dataset_dict (ds), ct, t, PIVOT_AXIS_ROW))
5571 if (lex_match (lexer, T_BY))
5573 if (!ctables_axis_parse (lexer, dataset_dict (ds),
5574 ct, t, PIVOT_AXIS_COLUMN))
5577 if (lex_match (lexer, T_BY))
5579 if (!ctables_axis_parse (lexer, dataset_dict (ds),
5580 ct, t, PIVOT_AXIS_LAYER))
5584 int expr_end = lex_ofs (lexer);
5586 if (!t->axes[PIVOT_AXIS_ROW] && !t->axes[PIVOT_AXIS_COLUMN]
5587 && !t->axes[PIVOT_AXIS_LAYER])
5589 lex_error (lexer, _("At least one variable must be specified."));
5593 const struct ctables_axis *scales[PIVOT_N_AXES];
5594 size_t n_scales = 0;
5595 for (enum pivot_axis_type a = 0; a < PIVOT_N_AXES; a++)
5597 scales[a] = find_scale (t->axes[a]);
5603 msg (SE, _("Scale variables may appear only on one axis."));
5604 if (scales[PIVOT_AXIS_ROW])
5605 msg_at (SN, scales[PIVOT_AXIS_ROW]->loc,
5606 _("This scale variable appears on the rows axis."));
5607 if (scales[PIVOT_AXIS_COLUMN])
5608 msg_at (SN, scales[PIVOT_AXIS_COLUMN]->loc,
5609 _("This scale variable appears on the columns axis."));
5610 if (scales[PIVOT_AXIS_LAYER])
5611 msg_at (SN, scales[PIVOT_AXIS_LAYER]->loc,
5612 _("This scale variable appears on the layer axis."));
5616 const struct ctables_axis *summaries[PIVOT_N_AXES];
5617 size_t n_summaries = 0;
5618 for (enum pivot_axis_type a = 0; a < PIVOT_N_AXES; a++)
5620 summaries[a] = (scales[a]
5622 : find_categorical_summary_spec (t->axes[a]));
5626 if (n_summaries > 1)
5628 msg (SE, _("Summaries may appear only on one axis."));
5629 if (summaries[PIVOT_AXIS_ROW])
5630 msg_at (SN, summaries[PIVOT_AXIS_ROW]->loc,
5631 _("This variable on the rows axis has a summary."));
5632 if (summaries[PIVOT_AXIS_COLUMN])
5633 msg_at (SN, summaries[PIVOT_AXIS_COLUMN]->loc,
5634 _("This variable on the columns axis has a summary."));
5635 if (summaries[PIVOT_AXIS_LAYER])
5636 msg_at (SN, summaries[PIVOT_AXIS_LAYER]->loc,
5637 _("This variable on the layers axis has a summary."));
5640 for (enum pivot_axis_type a = 0; a < PIVOT_N_AXES; a++)
5641 if (n_summaries ? summaries[a] : t->axes[a])
5643 t->summary_axis = a;
5647 if (lex_token (lexer) == T_ENDCMD)
5649 if (!ctables_prepare_table (t))
5653 if (!lex_force_match (lexer, T_SLASH))
5656 while (!lex_match_id (lexer, "TABLE") && lex_token (lexer) != T_ENDCMD)
5658 if (lex_match_id (lexer, "SLABELS"))
5660 while (lex_token (lexer) != T_SLASH && lex_token (lexer) != T_ENDCMD)
5662 if (lex_match_id (lexer, "POSITION"))
5664 lex_match (lexer, T_EQUALS);
5665 if (lex_match_id (lexer, "COLUMN"))
5666 t->slabels_axis = PIVOT_AXIS_COLUMN;
5667 else if (lex_match_id (lexer, "ROW"))
5668 t->slabels_axis = PIVOT_AXIS_ROW;
5669 else if (lex_match_id (lexer, "LAYER"))
5670 t->slabels_axis = PIVOT_AXIS_LAYER;
5673 lex_error_expecting (lexer, "COLUMN", "ROW", "LAYER");
5677 else if (lex_match_id (lexer, "VISIBLE"))
5679 lex_match (lexer, T_EQUALS);
5680 if (!parse_bool (lexer, &t->slabels_visible))
5685 lex_error_expecting (lexer, "POSITION", "VISIBLE");
5690 else if (lex_match_id (lexer, "CLABELS"))
5692 while (lex_token (lexer) != T_SLASH && lex_token (lexer) != T_ENDCMD)
5694 if (lex_match_id (lexer, "AUTO"))
5696 t->label_axis[PIVOT_AXIS_ROW] = PIVOT_AXIS_ROW;
5697 t->label_axis[PIVOT_AXIS_COLUMN] = PIVOT_AXIS_COLUMN;
5699 else if (lex_match_id (lexer, "ROWLABELS"))
5701 lex_match (lexer, T_EQUALS);
5702 if (lex_match_id (lexer, "OPPOSITE"))
5703 t->label_axis[PIVOT_AXIS_ROW] = PIVOT_AXIS_COLUMN;
5704 else if (lex_match_id (lexer, "LAYER"))
5705 t->label_axis[PIVOT_AXIS_ROW] = PIVOT_AXIS_LAYER;
5708 lex_error_expecting (lexer, "OPPOSITE", "LAYER");
5712 else if (lex_match_id (lexer, "COLLABELS"))
5714 lex_match (lexer, T_EQUALS);
5715 if (lex_match_id (lexer, "OPPOSITE"))
5716 t->label_axis[PIVOT_AXIS_COLUMN] = PIVOT_AXIS_ROW;
5717 else if (lex_match_id (lexer, "LAYER"))
5718 t->label_axis[PIVOT_AXIS_COLUMN] = PIVOT_AXIS_LAYER;
5721 lex_error_expecting (lexer, "OPPOSITE", "LAYER");
5727 lex_error_expecting (lexer, "AUTO", "ROWLABELS",
5733 else if (lex_match_id (lexer, "CRITERIA"))
5735 if (!lex_force_match_id (lexer, "CILEVEL"))
5737 lex_match (lexer, T_EQUALS);
5739 if (!lex_force_num_range_halfopen (lexer, "CILEVEL", 0, 100))
5741 t->cilevel = lex_number (lexer);
5744 else if (lex_match_id (lexer, "CATEGORIES"))
5746 if (!ctables_table_parse_categories (lexer, dataset_dict (ds),
5750 else if (lex_match_id (lexer, "TITLES"))
5755 if (lex_match_id (lexer, "CAPTION"))
5756 textp = &t->caption;
5757 else if (lex_match_id (lexer, "CORNER"))
5759 else if (lex_match_id (lexer, "TITLE"))
5763 lex_error_expecting (lexer, "CAPTION", "CORNER", "TITLE");
5766 lex_match (lexer, T_EQUALS);
5768 struct string s = DS_EMPTY_INITIALIZER;
5769 while (lex_is_string (lexer))
5771 if (!ds_is_empty (&s))
5772 ds_put_byte (&s, ' ');
5773 put_title_text (&s, lex_tokss (lexer), now,
5774 lexer, dataset_dict (ds),
5775 expr_start, expr_end);
5779 *textp = ds_steal_cstr (&s);
5781 while (lex_token (lexer) != T_SLASH
5782 && lex_token (lexer) != T_ENDCMD);
5784 else if (lex_match_id (lexer, "SIGTEST"))
5788 t->chisq = xmalloc (sizeof *t->chisq);
5789 *t->chisq = (struct ctables_chisq) {
5791 .include_mrsets = true,
5792 .all_visible = true,
5798 if (lex_match_id (lexer, "TYPE"))
5800 lex_match (lexer, T_EQUALS);
5801 if (!lex_force_match_id (lexer, "CHISQUARE"))
5804 else if (lex_match_id (lexer, "ALPHA"))
5806 lex_match (lexer, T_EQUALS);
5807 if (!lex_force_num_range_halfopen (lexer, "ALPHA", 0, 1))
5809 t->chisq->alpha = lex_number (lexer);
5812 else if (lex_match_id (lexer, "INCLUDEMRSETS"))
5814 lex_match (lexer, T_EQUALS);
5815 if (parse_bool (lexer, &t->chisq->include_mrsets))
5818 else if (lex_match_id (lexer, "CATEGORIES"))
5820 lex_match (lexer, T_EQUALS);
5821 if (lex_match_id (lexer, "ALLVISIBLE"))
5822 t->chisq->all_visible = true;
5823 else if (lex_match_id (lexer, "SUBTOTALS"))
5824 t->chisq->all_visible = false;
5827 lex_error_expecting (lexer,
5828 "ALLVISIBLE", "SUBTOTALS");
5834 lex_error_expecting (lexer, "TYPE", "ALPHA",
5835 "INCLUDEMRSETS", "CATEGORIES");
5839 while (lex_token (lexer) != T_SLASH
5840 && lex_token (lexer) != T_ENDCMD);
5842 else if (lex_match_id (lexer, "COMPARETEST"))
5846 t->pairwise = xmalloc (sizeof *t->pairwise);
5847 *t->pairwise = (struct ctables_pairwise) {
5849 .alpha = { .05, .05 },
5850 .adjust = BONFERRONI,
5851 .include_mrsets = true,
5852 .meansvariance_allcats = true,
5853 .all_visible = true,
5862 if (lex_match_id (lexer, "TYPE"))
5864 lex_match (lexer, T_EQUALS);
5865 if (lex_match_id (lexer, "PROP"))
5866 t->pairwise->type = PROP;
5867 else if (lex_match_id (lexer, "MEAN"))
5868 t->pairwise->type = MEAN;
5871 lex_error_expecting (lexer, "PROP", "MEAN");
5875 else if (lex_match_id (lexer, "ALPHA"))
5877 lex_match (lexer, T_EQUALS);
5879 if (!lex_force_num_range_open (lexer, "ALPHA", 0, 1))
5881 double a0 = lex_number (lexer);
5884 lex_match (lexer, T_COMMA);
5885 if (lex_is_number (lexer))
5887 if (!lex_force_num_range_open (lexer, "ALPHA", 0, 1))
5889 double a1 = lex_number (lexer);
5892 t->pairwise->alpha[0] = MIN (a0, a1);
5893 t->pairwise->alpha[1] = MAX (a0, a1);
5896 t->pairwise->alpha[0] = t->pairwise->alpha[1] = a0;
5898 else if (lex_match_id (lexer, "ADJUST"))
5900 lex_match (lexer, T_EQUALS);
5901 if (lex_match_id (lexer, "BONFERRONI"))
5902 t->pairwise->adjust = BONFERRONI;
5903 else if (lex_match_id (lexer, "BH"))
5904 t->pairwise->adjust = BH;
5905 else if (lex_match_id (lexer, "NONE"))
5906 t->pairwise->adjust = 0;
5909 lex_error_expecting (lexer, "BONFERRONI", "BH",
5914 else if (lex_match_id (lexer, "INCLUDEMRSETS"))
5916 lex_match (lexer, T_EQUALS);
5917 if (!parse_bool (lexer, &t->pairwise->include_mrsets))
5920 else if (lex_match_id (lexer, "MEANSVARIANCE"))
5922 lex_match (lexer, T_EQUALS);
5923 if (lex_match_id (lexer, "ALLCATS"))
5924 t->pairwise->meansvariance_allcats = true;
5925 else if (lex_match_id (lexer, "TESTEDCATS"))
5926 t->pairwise->meansvariance_allcats = false;
5929 lex_error_expecting (lexer, "ALLCATS", "TESTEDCATS");
5933 else if (lex_match_id (lexer, "CATEGORIES"))
5935 lex_match (lexer, T_EQUALS);
5936 if (lex_match_id (lexer, "ALLVISIBLE"))
5937 t->pairwise->all_visible = true;
5938 else if (lex_match_id (lexer, "SUBTOTALS"))
5939 t->pairwise->all_visible = false;
5942 lex_error_expecting (lexer, "ALLVISIBLE",
5947 else if (lex_match_id (lexer, "MERGE"))
5949 lex_match (lexer, T_EQUALS);
5950 if (!parse_bool (lexer, &t->pairwise->merge))
5953 else if (lex_match_id (lexer, "STYLE"))
5955 lex_match (lexer, T_EQUALS);
5956 if (lex_match_id (lexer, "APA"))
5957 t->pairwise->apa_style = true;
5958 else if (lex_match_id (lexer, "SIMPLE"))
5959 t->pairwise->apa_style = false;
5962 lex_error_expecting (lexer, "APA", "SIMPLE");
5966 else if (lex_match_id (lexer, "SHOWSIG"))
5968 lex_match (lexer, T_EQUALS);
5969 if (!parse_bool (lexer, &t->pairwise->show_sig))
5974 lex_error_expecting (lexer, "TYPE", "ALPHA", "ADJUST",
5975 "INCLUDEMRSETS", "MEANSVARIANCE",
5976 "CATEGORIES", "MERGE", "STYLE",
5981 while (lex_token (lexer) != T_SLASH
5982 && lex_token (lexer) != T_ENDCMD);
5986 lex_error_expecting (lexer, "TABLE", "SLABELS", "CLABELS",
5987 "CRITERIA", "CATEGORIES", "TITLES",
5988 "SIGTEST", "COMPARETEST");
5992 if (!lex_match (lexer, T_SLASH))
5996 if (t->label_axis[PIVOT_AXIS_ROW] != PIVOT_AXIS_ROW
5997 && t->label_axis[PIVOT_AXIS_COLUMN] != PIVOT_AXIS_COLUMN)
5999 msg (SE, _("ROWLABELS and COLLABELS may not both be specified."));
6003 if (!ctables_prepare_table (t))
6006 while (lex_token (lexer) != T_ENDCMD);
6008 bool ok = ctables_execute (ds, ct);
6009 ctables_destroy (ct);
6010 return ok ? CMD_SUCCESS : CMD_FAILURE;
6013 ctables_destroy (ct);