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-out.h"
25 #include "data/dataset.h"
26 #include "data/dictionary.h"
27 #include "data/mrset.h"
28 #include "data/subcase.h"
29 #include "data/value-labels.h"
30 #include "language/command.h"
31 #include "language/lexer/format-parser.h"
32 #include "language/lexer/lexer.h"
33 #include "language/lexer/variable-parser.h"
34 #include "libpspp/array.h"
35 #include "libpspp/assertion.h"
36 #include "libpspp/hash-functions.h"
37 #include "libpspp/hmap.h"
38 #include "libpspp/i18n.h"
39 #include "libpspp/message.h"
40 #include "libpspp/string-array.h"
41 #include "math/mode.h"
42 #include "math/moments.h"
43 #include "math/percentiles.h"
44 #include "math/sort.h"
45 #include "output/pivot-table.h"
47 #include "gl/minmax.h"
48 #include "gl/xalloc.h"
51 #define _(msgid) gettext (msgid)
52 #define N_(msgid) (msgid)
56 CTVL_NONE = SETTINGS_VALUE_SHOW_DEFAULT,
57 CTVL_NAME = SETTINGS_VALUE_SHOW_VALUE,
58 CTVL_LABEL = SETTINGS_VALUE_SHOW_LABEL,
59 CTVL_BOTH = SETTINGS_VALUE_SHOW_BOTH,
63 - unweighted summaries (U*)
64 - lower confidence limits (*.LCL)
65 - upper confidence limits (*.UCL)
66 - standard error (*.SE)
69 /* All variables. */ \
70 S(CTSF_COUNT, "COUNT", N_("Count"), CTF_COUNT, CTFA_ALL) \
71 S(CTSF_ECOUNT, "ECOUNT", N_("Adjusted Count"), CTF_COUNT, CTFA_ALL) \
72 S(CTSF_ROWPCT_COUNT, "ROWPCT.COUNT", N_("Row %"), CTF_PERCENT, CTFA_ALL) \
73 S(CTSF_COLPCT_COUNT, "COLPCT.COUNT", N_("Column %"), CTF_PERCENT, CTFA_ALL) \
74 S(CTSF_TABLEPCT_COUNT, "TABLEPCT.COUNT", N_("Table %"), CTF_PERCENT, CTFA_ALL) \
75 S(CTSF_SUBTABLEPCT_COUNT, "SUBTABLEPCT.COUNT", N_("Subtable %"), CTF_PERCENT, CTFA_ALL) \
76 S(CTSF_LAYERPCT_COUNT, "LAYERPCT.COUNT", N_("Layer %"), CTF_PERCENT, CTFA_ALL) \
77 S(CTSF_LAYERROWPCT_COUNT, "LAYERROWPCT.COUNT", N_("Layer Row %"), CTF_PERCENT, CTFA_ALL) \
78 S(CTSF_LAYERCOLPCT_COUNT, "LAYERCOLPCT.COUNT", N_("Layer Column %"), CTF_PERCENT, CTFA_ALL) \
79 S(CTSF_ROWPCT_VALIDN, "ROWPCT.VALIDN", N_("Row Valid N %"), CTF_PERCENT, CTFA_ALL) \
80 S(CTSF_COLPCT_VALIDN, "COLPCT.VALIDN", N_("Column Valid N %"), CTF_PERCENT, CTFA_ALL) \
81 S(CTSF_TABLEPCT_VALIDN, "TABLEPCT.VALIDN", N_("Table Valid N %"), CTF_PERCENT, CTFA_ALL) \
82 S(CTSF_SUBTABLEPCT_VALIDN, "SUBTABLEPCT.VALIDN", N_("Subtable Valid N %"), CTF_PERCENT, CTFA_ALL) \
83 S(CTSF_LAYERPCT_VALIDN, "LAYERPCT.VALIDN", N_("Layer Valid N %"), CTF_PERCENT, CTFA_ALL) \
84 S(CTSF_LAYERROWPCT_VALIDN, "LAYERROWPCT.VALIDN", N_("Layer Row Valid N %"), CTF_PERCENT, CTFA_ALL) \
85 S(CTSF_LAYERCOLPCT_VALIDN, "LAYERCOLPCT.VALIDN", N_("Layer Column Valid N %"), CTF_PERCENT, CTFA_ALL) \
86 S(CTSF_ROWPCT_TOTALN, "ROWPCT.TOTALN", N_("Row Total N %"), CTF_PERCENT, CTFA_ALL) \
87 S(CTSF_COLPCT_TOTALN, "COLPCT.TOTALN", N_("Column Total N %"), CTF_PERCENT, CTFA_ALL) \
88 S(CTSF_TABLEPCT_TOTALN, "TABLEPCT.TOTALN", N_("Table Total N %"), CTF_PERCENT, CTFA_ALL) \
89 S(CTSF_SUBTABLEPCT_TOTALN, "SUBTABLEPCT.TOTALN", N_("Subtable Total N %"), CTF_PERCENT, CTFA_ALL) \
90 S(CTSF_LAYERPCT_TOTALN, "LAYERPCT.TOTALN", N_("Layer Total N %"), CTF_PERCENT, CTFA_ALL) \
91 S(CTSF_LAYERROWPCT_TOTALN, "LAYERROWPCT.TOTALN", N_("Layer Row Total N %"), CTF_PERCENT, CTFA_ALL) \
92 S(CTSF_LAYERCOLPCT_TOTALN, "LAYERCOLPCT.TOTALN", N_("Layer Column Total N %"), CTF_PERCENT, CTFA_ALL) \
94 /* Scale variables, totals, and subtotals. */ \
95 S(CTSF_MAXIMUM, "MAXIMUM", N_("Maximum"), CTF_GENERAL, CTFA_SCALE) \
96 S(CTSF_MEAN, "MEAN", N_("Mean"), CTF_GENERAL, CTFA_SCALE) \
97 S(CTSF_MEDIAN, "MEDIAN", N_("Median"), CTF_GENERAL, CTFA_SCALE) \
98 S(CTSF_MINIMUM, "MINIMUM", N_("Minimum"), CTF_GENERAL, CTFA_SCALE) \
99 S(CTSF_MISSING, "MISSING", N_("Missing"), CTF_GENERAL, CTFA_SCALE) \
100 S(CTSF_MODE, "MODE", N_("Mode"), CTF_GENERAL, CTFA_SCALE) \
101 S(CTSF_PTILE, "PTILE", N_("Percentile"), CTF_GENERAL, CTFA_SCALE) \
102 S(CTSF_RANGE, "RANGE", N_("Range"), CTF_GENERAL, CTFA_SCALE) \
103 S(CTSF_SEMEAN, "SEMEAN", N_("Std Error of Mean"), CTF_GENERAL, CTFA_SCALE) \
104 S(CTSF_STDDEV, "STDDEV", N_("Std Deviation"), CTF_GENERAL, CTFA_SCALE) \
105 S(CTSF_SUM, "SUM", N_("Sum"), CTF_GENERAL, CTFA_SCALE) \
106 S(CSTF_TOTALN, "TOTALN", N_("Total N"), CTF_COUNT, CTFA_SCALE) \
107 S(CTSF_ETOTALN, "ETOTALN", N_("Adjusted Total N"), CTF_COUNT, CTFA_SCALE) \
108 S(CTSF_VALIDN, "VALIDN", N_("Valid N"), CTF_COUNT, CTFA_SCALE) \
109 S(CTSF_EVALIDN, "EVALIDN", N_("Adjusted Valid N"), CTF_COUNT, CTFA_SCALE) \
110 S(CTSF_VARIANCE, "VARIANCE", N_("Variance"), CTF_GENERAL, CTFA_SCALE) \
111 S(CTSF_ROWPCT_SUM, "ROWPCT.SUM", N_("Row Sum %"), CTF_PERCENT, CTFA_SCALE) \
112 S(CTSF_COLPCT_SUM, "COLPCT.SUM", N_("Column Sum %"), CTF_PERCENT, CTFA_SCALE) \
113 S(CTSF_TABLEPCT_SUM, "TABLEPCT.SUM", N_("Table Sum %"), CTF_PERCENT, CTFA_SCALE) \
114 S(CTSF_SUBTABLEPCT_SUM, "SUBTABLEPCT.SUM", N_("Subtable Sum %"), CTF_PERCENT, CTFA_SCALE) \
115 S(CTSF_LAYERPCT_SUM, "LAYERPCT.SUM", N_("Layer Sum %"), CTF_PERCENT, CTFA_SCALE) \
116 S(CTSF_LAYERROWPCT_SUM, "LAYERROWPCT.SUM", N_("Layer Row Sum %"), CTF_PERCENT, CTFA_SCALE) \
117 S(CTSF_LAYERCOLPCT_SUM, "LAYERCOLPCT.SUM", N_("Layer Column Sum %"), CTF_PERCENT, CTFA_SCALE) \
119 #if 0 /* Multiple response sets not yet implemented. */
120 S(CTSF_RESPONSES, "RESPONSES", N_("Responses"), CTF_COUNT, CTFA_MRSETS) \
121 S(CTSF_ROWPCT_RESPONSES, "ROWPCT.RESPONSES", N_("Row Responses %"), CTF_PERCENT, CTFA_MRSETS) \
122 S(CTSF_COLPCT_RESPONSES, "COLPCT.RESPONSES", N_("Column Responses %"), CTF_PERCENT, CTFA_MRSETS) \
123 S(CTSF_TABLEPCT_RESPONSES, "TABLEPCT.RESPONSES", N_("Table Responses %"), CTF_PERCENT, CTFA_MRSETS) \
124 S(CTSF_SUBTABLEPCT_RESPONSES, "SUBTABLEPCT.RESPONSES", N_("Subtable Responses %"), CTF_PERCENT, CTFA_MRSETS) \
125 S(CTSF_LAYERPCT_RESPONSES, "LAYERPCT.RESPONSES", N_("Layer Responses %"), CTF_PERCENT, CTFA_MRSETS) \
126 S(CTSF_LAYERROWPCT_RESPONSES, "LAYERROWPCT.RESPONSES", N_("Layer Row Responses %"), CTF_PERCENT, CTFA_MRSETS) \
127 S(CTSF_LAYERCOLPCT_RESPONSES, "LAYERCOLPCT.RESPONSES", N_("Layer Column Responses %"), CTF_PERCENT, CTFA_MRSETS) \
128 S(CTSF_ROWPCT_RESPONSES_COUNT, "ROWPCT.RESPONSES.COUNT", N_("Row Responses % (Base: Count)"), CTF_PERCENT, CTFA_MRSETS) \
129 S(CTSF_COLPCT_RESPONSES_COUNT, "COLPCT.RESPONSES.COUNT", N_("Column Responses % (Base: Count)"), CTF_PERCENT, CTFA_MRSETS) \
130 S(CTSF_TABLEPCT_RESPONSES_COUNT, "TABLEPCT.RESPONSES.COUNT", N_("Table Responses % (Base: Count)"), CTF_PERCENT, CTFA_MRSETS) \
131 S(CTSF_SUBTABLEPCT_RESPONSES_COUNT, "SUBTABLEPCT.RESPONSES.COUNT", N_("Subtable Responses % (Base: Count)"), CTF_PERCENT, CTFA_MRSETS) \
132 S(CTSF_LAYERPCT_RESPONSES_COUNT, "LAYERPCT.RESPONSES.COUNT", N_("Layer Responses % (Base: Count)"), CTF_PERCENT, CTFA_MRSETS) \
133 S(CTSF_LAYERROWPCT_RESPONSES_COUNT, "LAYERROWPCT.RESPONSES.COUNT", N_("Layer Row Responses % (Base: Count)"), CTF_PERCENT, CTFA_MRSETS) \
134 S(CTSF_LAYERCOLPCT_RESPONSES_COUNT, "LAYERCOLPCT.RESPONSES.COUNT", N_("Layer Column Responses % (Base: Count)"), CTF_PERCENT, CTFA_MRSETS) \
135 S(CTSF_ROWPCT_COUNT_RESPONSES, "ROWPCT.COUNT.RESPONSES", N_("Row Count % (Base: Responses)"), CTF_PERCENT, CTFA_MRSETS) \
136 S(CTSF_COLPCT_COUNT_RESPONSES, "COLPCT.COUNT.RESPONSES", N_("Column Count % (Base: Responses)"), CTF_PERCENT, CTFA_MRSETS) \
137 S(CTSF_TABLEPCT_COUNT_RESPONSES, "TABLEPCT.COUNT.RESPONSES", N_("Table Count % (Base: Responses)"), CTF_PERCENT, CTFA_MRSETS) \
138 S(CTSF_SUBTABLEPCT_COUNT_RESPONSES, "SUBTABLEPCT.COUNT.RESPONSES", N_("Subtable Count % (Base: Responses)"), CTF_PERCENT, CTFA_MRSETS) \
139 S(CTSF_LAYERPCT_COUNT_RESPONSES, "LAYERPCT.COUNT.RESPONSES", N_("Layer Count % (Base: Responses)"), CTF_PERCENT, CTFA_MRSETS) \
140 S(CTSF_LAYERROWPCT_COUNT_RESPONSES, "LAYERROWPCT.COUNT.RESPONSES", N_("Layer Row Count % (Base: Responses)"), CTF_PERCENT, CTFA_MRSETS) \
141 S(CTSF_LAYERCOLPCT_COUNT_RESPONSES, "LAYERCOLPCT.RESPONSES.COUNT", N_("Layer Column Count % (Base: Responses)"), CTF_PERCENT, CTFA_MRSETS)
144 enum ctables_summary_function
146 #define S(ENUM, NAME, LABEL, FORMAT, AVAILABILITY) ENUM,
152 #define S(ENUM, NAME, LABEL, FORMAT, AVAILABILITY) +1
153 N_CTSF_FUNCTIONS = SUMMARIES
157 static bool ctables_summary_function_is_count (enum ctables_summary_function);
159 enum ctables_domain_type
161 /* Within a section, where stacked variables divide one section from
163 CTDT_TABLE, /* All layers of a whole section. */
164 CTDT_LAYER, /* One layer within a section. */
165 CTDT_LAYERROW, /* Row in one layer within a section. */
166 CTDT_LAYERCOL, /* Column in one layer within a section. */
168 /* Within a subtable, where a subtable pairs an innermost row variable with
169 an innermost column variable within a single layer. */
170 CTDT_SUBTABLE, /* Whole subtable. */
171 CTDT_ROW, /* Row within a subtable. */
172 CTDT_COL, /* Column within a subtable. */
176 struct ctables_domain
178 struct hmap_node node;
180 const struct ctables_cell *example;
182 double d_valid; /* Dictionary weight. */
184 double e_valid; /* Effective weight */
188 enum ctables_summary_variant
197 /* In struct ctables_section's 'cells' hmap. Indexed by all the values in
198 all the axes (except the scalar variable, if any). */
199 struct hmap_node node;
201 /* The domains that contain this cell. */
202 bool contributes_to_domains;
203 struct ctables_domain *domains[N_CTDTS];
207 enum ctables_summary_variant sv;
209 struct ctables_cell_axis
211 struct ctables_cell_value
213 const struct ctables_category *category;
221 union ctables_summary *summaries;
226 const struct dictionary *dict;
227 struct pivot_table_look *look;
229 /* CTABLES has a number of extra formats that we implement via custom
230 currency specifications on an alternate fmt_settings. */
231 #define CTEF_NEGPAREN FMT_CCA
232 #define CTEF_NEQUAL FMT_CCB
233 #define CTEF_PAREN FMT_CCC
234 #define CTEF_PCTPAREN FMT_CCD
235 struct fmt_settings ctables_formats;
237 /* If this is NULL, zeros are displayed using the normal print format.
238 Otherwise, this string is displayed. */
241 /* If this is NULL, missing values are displayed using the normal print
242 format. Otherwise, this string is displayed. */
245 /* Indexed by variable dictionary index. */
246 enum ctables_vlabel *vlabels;
248 struct hmap postcomputes; /* Contains "struct ctables_postcompute"s. */
250 bool mrsets_count_duplicates; /* MRSETS. */
251 bool smissing_listwise; /* SMISSING. */
252 struct variable *e_weight; /* WEIGHT. */
253 int hide_threshold; /* HIDESMALLCOUNTS. */
255 struct ctables_table **tables;
259 static struct ctables_postcompute *ctables_find_postcompute (struct ctables *,
262 struct ctables_postcompute
264 struct hmap_node hmap_node; /* In struct ctables's 'pcompute' hmap. */
265 char *name; /* Name, without leading &. */
267 struct msg_location *location; /* Location of definition. */
268 struct ctables_pcexpr *expr;
270 struct ctables_summary_spec_set *specs;
271 bool hide_source_cats;
274 struct ctables_pcexpr
284 enum ctables_postcompute_op
287 CTPO_CONSTANT, /* 5 */
288 CTPO_CAT_NUMBER, /* [5] */
289 CTPO_CAT_STRING, /* ["STRING"] */
290 CTPO_CAT_RANGE, /* [LO THRU 5] */
291 CTPO_CAT_MISSING, /* MISSING */
292 CTPO_CAT_OTHERNM, /* OTHERNM */
293 CTPO_CAT_SUBTOTAL, /* SUBTOTAL */
294 CTPO_CAT_TOTAL, /* TOTAL */
308 /* CTPO_CAT_NUMBER. */
311 /* CTPO_CAT_STRING. */
314 /* CTPO_CAT_RANGE. */
317 /* CTPO_CAT_SUBTOTAL. */
318 size_t subtotal_index;
320 /* Two elements: CTPO_ADD, CTPO_SUB, CTPO_MUL, CTPO_DIV, CTPO_POW.
321 One element: CTPO_NEG. */
322 struct ctables_pcexpr *subs[2];
325 /* Source location. */
326 struct msg_location *location;
329 static void ctables_pcexpr_destroy (struct ctables_pcexpr *);
330 static struct ctables_pcexpr *ctables_pcexpr_allocate_binary (
331 enum ctables_postcompute_op, struct ctables_pcexpr *sub0,
332 struct ctables_pcexpr *sub1);
334 struct ctables_summary_spec_set
336 struct ctables_summary_spec *specs;
340 struct variable *scale_var;
343 static void ctables_summary_spec_set_clone (struct ctables_summary_spec_set *,
344 const struct ctables_summary_spec_set *);
345 static void ctables_summary_spec_set_uninit (struct ctables_summary_spec_set *);
347 /* A nested sequence of variables, e.g. a > b > c. */
350 struct variable **vars;
353 size_t *domains[N_CTDTS];
354 size_t n_domains[N_CTDTS];
356 struct ctables_summary_spec_set specs[N_CSVS];
359 /* A stack of nestings, e.g. nest1 + nest2 + ... + nestN. */
362 struct ctables_nest *nests;
368 struct hmap_node node;
373 struct ctables_occurrence
375 struct hmap_node node;
379 struct ctables_section
381 struct ctables_table *table;
382 struct ctables_nest *nests[PIVOT_N_AXES];
383 struct hmap *occurrences[PIVOT_N_AXES];
384 struct hmap cells; /* Contains "struct ctable_cell"s. */
385 struct hmap domains[N_CTDTS]; /* Contains "struct ctable_domain"s. */
390 struct ctables *ctables;
391 struct ctables_axis *axes[PIVOT_N_AXES];
392 struct ctables_stack stacks[PIVOT_N_AXES];
393 struct ctables_section *sections;
395 enum pivot_axis_type summary_axis;
396 struct ctables_summary_spec_set summary_specs;
398 const struct variable *clabels_example;
399 struct hmap clabels_values_map;
400 struct ctables_value **clabels_values;
401 size_t n_clabels_values;
403 enum pivot_axis_type slabels_axis;
404 bool slabels_visible;
406 /* The innermost category labels for axis 'a' appear on axis label_axis[a].
408 Most commonly, label_axis[a] == a, and in particular we always have
409 label_axis{PIVOT_AXIS_LAYER] == PIVOT_AXIS_LAYER.
411 If ROWLABELS or COLLABELS is specified, then one of
412 label_axis[PIVOT_AXIS_ROW] or label_axis[PIVOT_AXIS_COLUMN] can be the
413 opposite axis or PIVOT_AXIS_LAYER. Only one of them will differ.
415 enum pivot_axis_type label_axis[PIVOT_N_AXES];
416 enum pivot_axis_type clabels_from_axis;
418 /* Indexed by variable dictionary index. */
419 struct ctables_categories **categories;
428 struct ctables_chisq *chisq;
429 struct ctables_pairwise *pairwise;
437 struct variable *var;
438 const struct mrset *mrset;
442 static const struct fmt_spec *
443 ctables_var_get_print_format (const struct ctables_var *var)
445 return (var->is_mrset
446 ? var_get_print_format (var->mrset->vars[0])
447 : var_get_print_format (var->var));
451 ctables_var_name (const struct ctables_var *var)
453 return var->is_mrset ? var->mrset->name : var_get_name (var->var);
456 struct ctables_categories
459 struct ctables_category *cats;
464 struct ctables_category
466 enum ctables_category_type
468 /* Explicit category lists. */
476 /* Totals and subtotals. */
480 /* Implicit category lists. */
487 struct ctables_category *subtotal;
493 double number; /* CCT_NUMBER. */
494 char *string; /* CCT_STRING. */
495 double range[2]; /* CCT_RANGE. */
499 char *total_label; /* CCT_SUBTOTAL, CCT_TOTAL. */
500 bool hide_subcategories; /* CCT_SUBTOTAL. */
503 const struct ctables_postcompute *pc; /* CCT_POSTCOMPUTE. */
505 /* CCT_VALUE, CCT_LABEL, CCT_FUNCTION. */
508 bool include_missing;
512 enum ctables_summary_function sort_function;
513 struct variable *sort_var;
518 /* Source location. This is null for CCT_TOTAL, CCT_VALUE, CCT_LABEL,
520 struct msg_location *location;
524 ctables_category_uninit (struct ctables_category *cat)
535 case CCT_POSTCOMPUTE:
544 free (cat->total_label);
555 ctables_category_equal (const struct ctables_category *a,
556 const struct ctables_category *b)
558 if (a->type != b->type)
564 return a->number == b->number;
567 return strcmp (a->string, b->string);
570 return a->range[0] == b->range[0] && a->range[1] == b->range[1];
576 case CCT_POSTCOMPUTE:
577 return a->pc == b->pc;
581 return !strcmp (a->total_label, b->total_label);
586 return (a->include_missing == b->include_missing
587 && a->sort_ascending == b->sort_ascending
588 && a->sort_function == b->sort_function
589 && a->sort_var == b->sort_var
590 && a->percentile == b->percentile);
597 ctables_categories_unref (struct ctables_categories *c)
602 assert (c->n_refs > 0);
606 for (size_t i = 0; i < c->n_cats; i++)
607 ctables_category_uninit (&c->cats[i]);
613 ctables_categories_equal (const struct ctables_categories *a,
614 const struct ctables_categories *b)
616 if (a->n_cats != b->n_cats || a->show_empty != b->show_empty)
619 for (size_t i = 0; i < a->n_cats; i++)
620 if (!ctables_category_equal (&a->cats[i], &b->cats[i]))
626 /* Chi-square test (SIGTEST). */
634 /* Pairwise comparison test (COMPARETEST). */
635 struct ctables_pairwise
637 enum { PROP, MEAN } type;
640 bool meansvariance_allcats;
642 enum { BONFERRONI = 1, BH } adjust;
666 struct ctables_var var;
668 struct ctables_summary_spec_set specs[N_CSVS];
672 struct ctables_axis *subs[2];
675 struct msg_location *loc;
678 static void ctables_axis_destroy (struct ctables_axis *);
687 enum ctables_function_availability
689 CTFA_ALL, /* Any variables. */
690 CTFA_SCALE, /* Only scale variables, totals, and subtotals. */
691 CTFA_MRSETS, /* Only multiple-response sets */
694 struct ctables_summary_spec
696 enum ctables_summary_function function;
697 double percentile; /* CTSF_PTILE only. */
700 struct fmt_spec format;
701 bool is_ctables_format; /* Is 'format' one of CTEF_*? */
707 ctables_summary_spec_clone (struct ctables_summary_spec *dst,
708 const struct ctables_summary_spec *src)
711 dst->label = xstrdup (src->label);
715 ctables_summary_spec_uninit (struct ctables_summary_spec *s)
722 ctables_summary_spec_set_clone (struct ctables_summary_spec_set *dst,
723 const struct ctables_summary_spec_set *src)
725 struct ctables_summary_spec *specs = xnmalloc (src->n, sizeof *specs);
726 for (size_t i = 0; i < src->n; i++)
727 ctables_summary_spec_clone (&specs[i], &src->specs[i]);
729 *dst = (struct ctables_summary_spec_set) {
733 .scale_var = src->scale_var
738 ctables_summary_spec_set_uninit (struct ctables_summary_spec_set *set)
740 for (size_t i = 0; i < set->n; i++)
741 ctables_summary_spec_uninit (&set->specs[i]);
746 parse_col_width (struct lexer *lexer, const char *name, double *width)
748 lex_match (lexer, T_EQUALS);
749 if (lex_match_id (lexer, "DEFAULT"))
751 else if (lex_force_num_range_closed (lexer, name, 0, DBL_MAX))
753 *width = lex_number (lexer);
763 parse_bool (struct lexer *lexer, bool *b)
765 if (lex_match_id (lexer, "NO"))
767 else if (lex_match_id (lexer, "YES"))
771 lex_error_expecting (lexer, "YES", "NO");
777 static enum ctables_function_availability
778 ctables_function_availability (enum ctables_summary_function f)
780 static enum ctables_function_availability availability[] = {
781 #define S(ENUM, NAME, LABEL, FORMAT, AVAILABILITY) [ENUM] = AVAILABILITY,
786 return availability[f];
790 ctables_summary_function_is_count (enum ctables_summary_function f)
796 case CTSF_ROWPCT_COUNT:
797 case CTSF_COLPCT_COUNT:
798 case CTSF_TABLEPCT_COUNT:
799 case CTSF_SUBTABLEPCT_COUNT:
800 case CTSF_LAYERPCT_COUNT:
801 case CTSF_LAYERROWPCT_COUNT:
802 case CTSF_LAYERCOLPCT_COUNT:
805 case CTSF_ROWPCT_VALIDN:
806 case CTSF_COLPCT_VALIDN:
807 case CTSF_TABLEPCT_VALIDN:
808 case CTSF_SUBTABLEPCT_VALIDN:
809 case CTSF_LAYERPCT_VALIDN:
810 case CTSF_LAYERROWPCT_VALIDN:
811 case CTSF_LAYERCOLPCT_VALIDN:
812 case CTSF_ROWPCT_TOTALN:
813 case CTSF_COLPCT_TOTALN:
814 case CTSF_TABLEPCT_TOTALN:
815 case CTSF_SUBTABLEPCT_TOTALN:
816 case CTSF_LAYERPCT_TOTALN:
817 case CTSF_LAYERROWPCT_TOTALN:
818 case CTSF_LAYERCOLPCT_TOTALN:
835 case CTSF_ROWPCT_SUM:
836 case CTSF_COLPCT_SUM:
837 case CTSF_TABLEPCT_SUM:
838 case CTSF_SUBTABLEPCT_SUM:
839 case CTSF_LAYERPCT_SUM:
840 case CTSF_LAYERROWPCT_SUM:
841 case CTSF_LAYERCOLPCT_SUM:
849 parse_ctables_summary_function (struct lexer *lexer,
850 enum ctables_summary_function *f)
854 enum ctables_summary_function function;
855 struct substring name;
857 static struct pair names[] = {
858 #define S(ENUM, NAME, LABEL, FORMAT, AVAILABILITY) \
859 { ENUM, SS_LITERAL_INITIALIZER (NAME) },
862 /* The .COUNT suffix may be omitted. */
863 S(CTSF_ROWPCT_COUNT, "ROWPCT", _, _, _)
864 S(CTSF_COLPCT_COUNT, "COLPCT", _, _, _)
865 S(CTSF_TABLEPCT_COUNT, "TABLEPCT", _, _, _)
866 S(CTSF_SUBTABLEPCT_COUNT, "SUBTABLEPCT", _, _, _)
867 S(CTSF_LAYERPCT_COUNT, "LAYERPCT", _, _, _)
868 S(CTSF_LAYERROWPCT_COUNT, "LAYERROWPCT", _, _, _)
869 S(CTSF_LAYERCOLPCT_COUNT, "LAYERCOLPCT", _, _, _)
873 if (!lex_force_id (lexer))
876 for (size_t i = 0; i < sizeof names / sizeof *names; i++)
877 if (ss_equals_case (names[i].name, lex_tokss (lexer)))
879 *f = names[i].function;
884 lex_error (lexer, _("Expecting summary function name."));
889 ctables_axis_destroy (struct ctables_axis *axis)
897 for (size_t i = 0; i < N_CSVS; i++)
898 ctables_summary_spec_set_uninit (&axis->specs[i]);
903 ctables_axis_destroy (axis->subs[0]);
904 ctables_axis_destroy (axis->subs[1]);
907 msg_location_destroy (axis->loc);
911 static struct ctables_axis *
912 ctables_axis_new_nonterminal (enum ctables_axis_op op,
913 struct ctables_axis *sub0,
914 struct ctables_axis *sub1,
915 struct lexer *lexer, int start_ofs)
917 struct ctables_axis *axis = xmalloc (sizeof *axis);
918 *axis = (struct ctables_axis) {
920 .subs = { sub0, sub1 },
921 .loc = lex_ofs_location (lexer, start_ofs, lex_ofs (lexer) - 1),
926 struct ctables_axis_parse_ctx
929 struct dictionary *dict;
931 struct ctables_table *t;
934 static struct fmt_spec
935 ctables_summary_default_format (enum ctables_summary_function function,
936 const struct ctables_var *var)
938 static const enum ctables_format default_formats[] = {
939 #define S(ENUM, NAME, LABEL, FORMAT, AVAILABILITY) [ENUM] = FORMAT,
943 switch (default_formats[function])
946 return (struct fmt_spec) { .type = FMT_F, .w = 40 };
949 return (struct fmt_spec) { .type = FMT_PCT, .w = 40, .d = 1 };
952 return *ctables_var_get_print_format (var);
960 ctables_summary_default_label (enum ctables_summary_function function,
963 static const char *default_labels[] = {
964 #define S(ENUM, NAME, LABEL, FORMAT, AVAILABILITY) [ENUM] = LABEL,
969 return (function == CTSF_PTILE
970 ? xasprintf (_("Percentile %.2f"), percentile)
971 : xstrdup (gettext (default_labels[function])));
975 ctables_summary_function_name (enum ctables_summary_function function)
977 static const char *names[] = {
978 #define S(ENUM, NAME, LABEL, FORMAT, AVAILABILITY) [ENUM] = NAME,
982 return names[function];
986 add_summary_spec (struct ctables_axis *axis,
987 enum ctables_summary_function function, double percentile,
988 const char *label, const struct fmt_spec *format,
989 bool is_ctables_format, const struct msg_location *loc,
990 enum ctables_summary_variant sv)
992 if (axis->op == CTAO_VAR)
994 const char *function_name = ctables_summary_function_name (function);
995 const char *var_name = ctables_var_name (&axis->var);
996 switch (ctables_function_availability (function))
999 if (!axis->var.is_mrset)
1001 msg_at (SE, loc, _("Summary function %s applies only to multiple "
1002 "response sets."), function_name);
1003 msg_at (SN, axis->loc, _("'%s' is not a multiple response set."),
1013 _("Summary function %s applies only to scale variables."),
1015 msg_at (SN, axis->loc, _("'%s' is not a scale variable."),
1025 struct ctables_summary_spec_set *set = &axis->specs[sv];
1026 if (set->n >= set->allocated)
1027 set->specs = x2nrealloc (set->specs, &set->allocated,
1028 sizeof *set->specs);
1030 struct ctables_summary_spec *dst = &set->specs[set->n++];
1031 *dst = (struct ctables_summary_spec) {
1032 .function = function,
1033 .percentile = percentile,
1034 .label = xstrdup (label),
1035 .format = (format ? *format
1036 : ctables_summary_default_format (function, &axis->var)),
1037 .is_ctables_format = is_ctables_format,
1043 for (size_t i = 0; i < 2; i++)
1044 if (!add_summary_spec (axis->subs[i], function, percentile, label,
1045 format, is_ctables_format, loc, sv))
1051 static struct ctables_axis *ctables_axis_parse_stack (
1052 struct ctables_axis_parse_ctx *);
1055 ctables_var_parse (struct lexer *lexer, struct dictionary *dict,
1056 struct ctables_var *var)
1058 if (ss_starts_with (lex_tokss (lexer), ss_cstr ("$")))
1060 *var = (struct ctables_var) {
1062 .mrset = dict_lookup_mrset (dict, lex_tokcstr (lexer))
1066 lex_error (lexer, _("'%s' does not name a multiple-response set "
1067 "in the active file dictionary."),
1068 lex_tokcstr (lexer));
1076 *var = (struct ctables_var) {
1078 .var = parse_variable (lexer, dict),
1080 return var->var != NULL;
1084 static struct ctables_axis *
1085 ctables_axis_parse_primary (struct ctables_axis_parse_ctx *ctx)
1087 if (lex_match (ctx->lexer, T_LPAREN))
1089 struct ctables_axis *sub = ctables_axis_parse_stack (ctx);
1090 if (!sub || !lex_force_match (ctx->lexer, T_RPAREN))
1092 ctables_axis_destroy (sub);
1098 if (!lex_force_id (ctx->lexer))
1101 int start_ofs = lex_ofs (ctx->lexer);
1102 struct ctables_var var;
1103 if (!ctables_var_parse (ctx->lexer, ctx->dict, &var))
1106 struct ctables_axis *axis = xmalloc (sizeof *axis);
1107 *axis = (struct ctables_axis) { .op = CTAO_VAR, .var = var };
1109 /* XXX should figure out default measures by reading data */
1110 axis->scale = (var.is_mrset ? false
1111 : lex_match_phrase (ctx->lexer, "[S]") ? true
1112 : lex_match_phrase (ctx->lexer, "[C]") ? false
1113 : var_get_measure (var.var) == MEASURE_SCALE);
1114 axis->loc = lex_ofs_location (ctx->lexer, start_ofs,
1115 lex_ofs (ctx->lexer) - 1);
1120 has_digit (const char *s)
1122 return s[strcspn (s, "0123456789")] != '\0';
1126 parse_ctables_format_specifier (struct lexer *lexer, struct fmt_spec *format,
1127 bool *is_ctables_format)
1129 char type[FMT_TYPE_LEN_MAX + 1];
1130 if (!parse_abstract_format_specifier__ (lexer, type, &format->w, &format->d))
1133 if (!strcasecmp (type, "NEGPAREN"))
1134 format->type = CTEF_NEGPAREN;
1135 else if (!strcasecmp (type, "NEQUAL"))
1136 format->type = CTEF_NEQUAL;
1137 else if (!strcasecmp (type, "PAREN"))
1138 format->type = CTEF_PAREN;
1139 else if (!strcasecmp (type, "PCTPAREN"))
1140 format->type = CTEF_PCTPAREN;
1143 *is_ctables_format = false;
1144 return (parse_format_specifier (lexer, format)
1145 && fmt_check_output (format)
1146 && fmt_check_type_compat (format, VAL_NUMERIC));
1151 msg (SE, _("Output format %s requires width 2 or greater."), type);
1154 else if (format->d > format->w - 1)
1156 msg (SE, _("Output format %s requires width greater than decimals."),
1162 *is_ctables_format = true;
1167 static struct ctables_axis *
1168 ctables_axis_parse_postfix (struct ctables_axis_parse_ctx *ctx)
1170 struct ctables_axis *sub = ctables_axis_parse_primary (ctx);
1171 if (!sub || !lex_match (ctx->lexer, T_LBRACK))
1174 enum ctables_summary_variant sv = CSV_CELL;
1177 int start_ofs = lex_ofs (ctx->lexer);
1179 /* Parse function. */
1180 enum ctables_summary_function function;
1181 if (!parse_ctables_summary_function (ctx->lexer, &function))
1184 /* Parse percentile. */
1185 double percentile = 0;
1186 if (function == CTSF_PTILE)
1188 if (!lex_force_num_range_closed (ctx->lexer, "PTILE", 0, 100))
1190 percentile = lex_number (ctx->lexer);
1191 lex_get (ctx->lexer);
1196 if (lex_is_string (ctx->lexer))
1198 label = ss_xstrdup (lex_tokss (ctx->lexer));
1199 lex_get (ctx->lexer);
1202 label = ctables_summary_default_label (function, percentile);
1205 struct fmt_spec format;
1206 const struct fmt_spec *formatp;
1207 bool is_ctables_format = false;
1208 if (lex_token (ctx->lexer) == T_ID
1209 && has_digit (lex_tokcstr (ctx->lexer)))
1211 if (!parse_ctables_format_specifier (ctx->lexer, &format,
1212 &is_ctables_format))
1222 struct msg_location *loc = lex_ofs_location (ctx->lexer, start_ofs,
1223 lex_ofs (ctx->lexer) - 1);
1224 add_summary_spec (sub, function, percentile, label, formatp,
1225 is_ctables_format, loc, sv);
1227 msg_location_destroy (loc);
1229 lex_match (ctx->lexer, T_COMMA);
1230 if (sv == CSV_CELL && lex_match_id (ctx->lexer, "TOTALS"))
1232 if (!lex_force_match (ctx->lexer, T_LBRACK))
1236 else if (lex_match (ctx->lexer, T_RBRACK))
1238 if (sv == CSV_TOTAL && !lex_force_match (ctx->lexer, T_RBRACK))
1245 ctables_axis_destroy (sub);
1249 static const struct ctables_axis *
1250 find_scale (const struct ctables_axis *axis)
1254 else if (axis->op == CTAO_VAR)
1258 assert (!axis->var.is_mrset);
1266 for (size_t i = 0; i < 2; i++)
1268 const struct ctables_axis *scale = find_scale (axis->subs[i]);
1276 static const struct ctables_axis *
1277 find_categorical_summary_spec (const struct ctables_axis *axis)
1281 else if (axis->op == CTAO_VAR)
1282 return !axis->scale && axis->specs[CSV_CELL].n ? axis : NULL;
1285 for (size_t i = 0; i < 2; i++)
1287 const struct ctables_axis *sum
1288 = find_categorical_summary_spec (axis->subs[i]);
1296 static struct ctables_axis *
1297 ctables_axis_parse_nest (struct ctables_axis_parse_ctx *ctx)
1299 int start_ofs = lex_ofs (ctx->lexer);
1300 struct ctables_axis *lhs = ctables_axis_parse_postfix (ctx);
1304 while (lex_match (ctx->lexer, T_GT))
1306 struct ctables_axis *rhs = ctables_axis_parse_postfix (ctx);
1310 struct ctables_axis *nest = ctables_axis_new_nonterminal (
1311 CTAO_NEST, lhs, rhs, ctx->lexer, start_ofs);
1313 const struct ctables_axis *outer_scale = find_scale (lhs);
1314 const struct ctables_axis *inner_scale = find_scale (rhs);
1315 if (outer_scale && inner_scale)
1317 msg_at (SE, nest->loc, _("Cannot nest scale variables."));
1318 msg_at (SN, outer_scale->loc, _("This is an outer scale variable."));
1319 msg_at (SN, inner_scale->loc, _("This is an inner scale variable."));
1320 ctables_axis_destroy (nest);
1324 const struct ctables_axis *outer_sum = find_categorical_summary_spec (lhs);
1327 msg_at (SE, nest->loc,
1328 _("Summaries may only be requested for categorical variables "
1329 "at the innermost nesting level."));
1330 msg_at (SN, outer_sum->loc,
1331 _("This outer categorical variable has a summary."));
1332 ctables_axis_destroy (nest);
1342 static struct ctables_axis *
1343 ctables_axis_parse_stack (struct ctables_axis_parse_ctx *ctx)
1345 int start_ofs = lex_ofs (ctx->lexer);
1346 struct ctables_axis *lhs = ctables_axis_parse_nest (ctx);
1350 while (lex_match (ctx->lexer, T_PLUS))
1352 struct ctables_axis *rhs = ctables_axis_parse_nest (ctx);
1356 lhs = ctables_axis_new_nonterminal (CTAO_STACK, lhs, rhs,
1357 ctx->lexer, start_ofs);
1364 ctables_axis_parse (struct lexer *lexer, struct dictionary *dict,
1365 struct ctables *ct, struct ctables_table *t,
1366 enum pivot_axis_type a)
1368 if (lex_token (lexer) == T_BY
1369 || lex_token (lexer) == T_SLASH
1370 || lex_token (lexer) == T_ENDCMD)
1373 struct ctables_axis_parse_ctx ctx = {
1379 t->axes[a] = ctables_axis_parse_stack (&ctx);
1380 return t->axes[a] != NULL;
1384 ctables_chisq_destroy (struct ctables_chisq *chisq)
1390 ctables_pairwise_destroy (struct ctables_pairwise *pairwise)
1396 ctables_table_destroy (struct ctables_table *t)
1401 for (size_t i = 0; i < t->n_categories; i++)
1402 ctables_categories_unref (t->categories[i]);
1403 free (t->categories);
1405 ctables_axis_destroy (t->axes[PIVOT_AXIS_COLUMN]);
1406 ctables_axis_destroy (t->axes[PIVOT_AXIS_ROW]);
1407 ctables_axis_destroy (t->axes[PIVOT_AXIS_LAYER]);
1411 ctables_chisq_destroy (t->chisq);
1412 ctables_pairwise_destroy (t->pairwise);
1417 ctables_destroy (struct ctables *ct)
1422 pivot_table_look_unref (ct->look);
1426 for (size_t i = 0; i < ct->n_tables; i++)
1427 ctables_table_destroy (ct->tables[i]);
1432 static struct ctables_category
1433 cct_range (double low, double high)
1435 return (struct ctables_category) {
1437 .range = { low, high }
1442 ctables_table_parse_subtotal (struct lexer *lexer, bool hide_subcategories,
1443 struct ctables_category *cat)
1446 if (lex_match (lexer, T_EQUALS))
1448 if (!lex_force_string (lexer))
1451 total_label = ss_xstrdup (lex_tokss (lexer));
1455 total_label = xstrdup (_("Subtotal"));
1457 *cat = (struct ctables_category) {
1458 .type = CCT_SUBTOTAL,
1459 .hide_subcategories = hide_subcategories,
1460 .total_label = total_label
1466 ctables_table_parse_explicit_category (struct lexer *lexer, struct ctables *ct,
1467 struct ctables_category *cat)
1469 if (lex_match_id (lexer, "OTHERNM"))
1470 *cat = (struct ctables_category) { .type = CCT_OTHERNM };
1471 else if (lex_match_id (lexer, "MISSING"))
1472 *cat = (struct ctables_category) { .type = CCT_MISSING };
1473 else if (lex_match_id (lexer, "SUBTOTAL"))
1474 return ctables_table_parse_subtotal (lexer, false, cat);
1475 else if (lex_match_id (lexer, "HSUBTOTAL"))
1476 return ctables_table_parse_subtotal (lexer, true, cat);
1477 else if (lex_match_id (lexer, "LO"))
1479 if (!lex_force_match_id (lexer, "THRU") || lex_force_num (lexer))
1481 *cat = cct_range (-DBL_MAX, lex_number (lexer));
1484 else if (lex_is_number (lexer))
1486 double number = lex_number (lexer);
1488 if (lex_match_id (lexer, "THRU"))
1490 if (lex_match_id (lexer, "HI"))
1491 *cat = cct_range (number, DBL_MAX);
1494 if (!lex_force_num (lexer))
1496 *cat = cct_range (number, lex_number (lexer));
1501 *cat = (struct ctables_category) {
1506 else if (lex_is_string (lexer))
1508 *cat = (struct ctables_category) {
1510 .string = ss_xstrdup (lex_tokss (lexer)),
1514 else if (lex_match (lexer, T_AND))
1516 if (!lex_force_id (lexer))
1518 struct ctables_postcompute *pc = ctables_find_postcompute (
1519 ct, lex_tokcstr (lexer));
1522 struct msg_location *loc = lex_get_location (lexer, -1, 0);
1523 msg_at (SE, loc, _("Unknown postcompute &%s."),
1524 lex_tokcstr (lexer));
1525 msg_location_destroy (loc);
1530 *cat = (struct ctables_category) { .type = CCT_POSTCOMPUTE, .pc = pc };
1534 lex_error (lexer, NULL);
1541 static struct ctables_category *
1542 ctables_find_category_for_postcompute (const struct ctables_categories *cats,
1543 const struct ctables_pcexpr *e)
1545 struct ctables_category *best = NULL;
1546 size_t n_subtotals = 0;
1547 for (size_t i = 0; i < cats->n_cats; i++)
1549 struct ctables_category *cat = &cats->cats[i];
1552 case CTPO_CAT_NUMBER:
1553 if (cat->type == CCT_NUMBER && cat->number == e->number)
1557 case CTPO_CAT_STRING:
1558 if (cat->type == CCT_STRING && !strcmp (cat->string, e->string))
1562 case CTPO_CAT_RANGE:
1563 if (cat->type == CCT_RANGE
1564 && cat->range[0] == e->range[0]
1565 && cat->range[1] == e->range[1])
1569 case CTPO_CAT_MISSING:
1570 if (cat->type == CCT_MISSING)
1574 case CTPO_CAT_OTHERNM:
1575 if (cat->type == CCT_OTHERNM)
1579 case CTPO_CAT_SUBTOTAL:
1580 if (cat->type == CCT_SUBTOTAL)
1583 if (e->subtotal_index == n_subtotals)
1585 else if (e->subtotal_index == 0)
1590 case CTPO_CAT_TOTAL:
1591 if (cat->type == CCT_TOTAL)
1605 if (e->op == CTPO_CAT_SUBTOTAL && e->subtotal_index == 0 && n_subtotals > 1)
1611 ctables_recursive_check_postcompute (const struct ctables_pcexpr *e,
1612 struct ctables_category *pc_cat,
1613 const struct ctables_categories *cats,
1614 const struct msg_location *cats_location)
1618 case CTPO_CAT_NUMBER:
1619 case CTPO_CAT_STRING:
1620 case CTPO_CAT_RANGE:
1621 case CTPO_CAT_MISSING:
1622 case CTPO_CAT_OTHERNM:
1623 case CTPO_CAT_SUBTOTAL:
1624 case CTPO_CAT_TOTAL:
1626 struct ctables_category *cat = ctables_find_category_for_postcompute (
1630 if (e->op == CTPO_CAT_SUBTOTAL && e->subtotal_index == 0)
1632 size_t n_subtotals = 0;
1633 for (size_t i = 0; i < cats->n_cats; i++)
1634 n_subtotals += cats->cats[i].type == CCT_SUBTOTAL;
1635 if (n_subtotals > 1)
1637 msg_at (SE, cats_location,
1638 ngettext ("These categories include %zu instance "
1639 "of SUBTOTAL or HSUBTOTAL, so references "
1640 "from computed categories must refer to "
1641 "subtotals by position.",
1642 "These categories include %zu instances "
1643 "of SUBTOTAL or HSUBTOTAL, so references "
1644 "from computed categories must refer to "
1645 "subtotals by position.",
1648 msg_at (SN, e->location,
1649 _("This is the reference that lacks a position."));
1654 msg_at (SE, pc_cat->location,
1655 _("Computed category &%s references a category not included "
1656 "in the category list."),
1658 msg_at (SN, e->location, _("This is the missing category."));
1659 msg_at (SN, cats_location,
1660 _("To fix the problem, add the missing category to the "
1661 "list of categories here."));
1664 if (pc_cat->pc->hide_source_cats)
1678 for (size_t i = 0; i < 2; i++)
1679 if (e->subs[i] && !ctables_recursive_check_postcompute (
1680 e->subs[i], pc_cat, cats, cats_location))
1690 ctables_table_parse_categories (struct lexer *lexer, struct dictionary *dict,
1691 struct ctables *ct, struct ctables_table *t)
1693 if (!lex_match_id (lexer, "VARIABLES"))
1695 lex_match (lexer, T_EQUALS);
1697 struct variable **vars;
1699 if (!parse_variables (lexer, dict, &vars, &n_vars, PV_NO_SCRATCH))
1702 struct ctables_categories *c = xmalloc (sizeof *c);
1703 *c = (struct ctables_categories) { .n_refs = n_vars, .show_empty = true };
1704 for (size_t i = 0; i < n_vars; i++)
1706 struct ctables_categories **cp
1707 = &t->categories[var_get_dict_index (vars[i])];
1708 ctables_categories_unref (*cp);
1713 size_t allocated_cats = 0;
1714 if (lex_match (lexer, T_LBRACK))
1716 int cats_start_ofs = lex_ofs (lexer);
1719 if (c->n_cats >= allocated_cats)
1720 c->cats = x2nrealloc (c->cats, &allocated_cats, sizeof *c->cats);
1722 int start_ofs = lex_ofs (lexer);
1723 struct ctables_category *cat = &c->cats[c->n_cats];
1724 if (!ctables_table_parse_explicit_category (lexer, ct, cat))
1726 cat->location = lex_ofs_location (lexer, start_ofs, lex_ofs (lexer) - 1);
1729 lex_match (lexer, T_COMMA);
1731 while (!lex_match (lexer, T_RBRACK));
1733 struct msg_location *cats_location
1734 = lex_ofs_location (lexer, cats_start_ofs, lex_ofs (lexer) - 1);
1735 for (size_t i = 0; i < c->n_cats; i++)
1737 struct ctables_category *cat = &c->cats[i];
1738 if (cat->type == CCT_POSTCOMPUTE
1739 && !ctables_recursive_check_postcompute (cat->pc->expr, cat,
1745 struct ctables_category cat = {
1747 .include_missing = false,
1748 .sort_ascending = true,
1750 bool show_totals = false;
1751 char *total_label = NULL;
1752 bool totals_before = false;
1753 while (lex_token (lexer) != T_SLASH && lex_token (lexer) != T_ENDCMD)
1755 if (!c->n_cats && lex_match_id (lexer, "ORDER"))
1757 lex_match (lexer, T_EQUALS);
1758 if (lex_match_id (lexer, "A"))
1759 cat.sort_ascending = true;
1760 else if (lex_match_id (lexer, "D"))
1761 cat.sort_ascending = false;
1764 lex_error_expecting (lexer, "A", "D");
1768 else if (!c->n_cats && lex_match_id (lexer, "KEY"))
1770 lex_match (lexer, T_EQUALS);
1771 if (lex_match_id (lexer, "VALUE"))
1772 cat.type = CCT_VALUE;
1773 else if (lex_match_id (lexer, "LABEL"))
1774 cat.type = CCT_LABEL;
1777 cat.type = CCT_FUNCTION;
1778 if (!parse_ctables_summary_function (lexer, &cat.sort_function))
1781 if (lex_match (lexer, T_LPAREN))
1783 cat.sort_var = parse_variable (lexer, dict);
1787 if (cat.sort_function == CTSF_PTILE)
1789 lex_match (lexer, T_COMMA);
1790 if (!lex_force_num_range_closed (lexer, "PTILE", 0, 100))
1792 cat.percentile = lex_number (lexer);
1796 if (!lex_force_match (lexer, T_RPAREN))
1799 else if (ctables_function_availability (cat.sort_function)
1802 bool UNUSED b = lex_force_match (lexer, T_LPAREN);
1807 else if (!c->n_cats && lex_match_id (lexer, "MISSING"))
1809 lex_match (lexer, T_EQUALS);
1810 if (lex_match_id (lexer, "INCLUDE"))
1811 cat.include_missing = true;
1812 else if (lex_match_id (lexer, "EXCLUDE"))
1813 cat.include_missing = false;
1816 lex_error_expecting (lexer, "INCLUDE", "EXCLUDE");
1820 else if (lex_match_id (lexer, "TOTAL"))
1822 lex_match (lexer, T_EQUALS);
1823 if (!parse_bool (lexer, &show_totals))
1826 else if (lex_match_id (lexer, "LABEL"))
1828 lex_match (lexer, T_EQUALS);
1829 if (!lex_force_string (lexer))
1832 total_label = ss_xstrdup (lex_tokss (lexer));
1835 else if (lex_match_id (lexer, "POSITION"))
1837 lex_match (lexer, T_EQUALS);
1838 if (lex_match_id (lexer, "BEFORE"))
1839 totals_before = true;
1840 else if (lex_match_id (lexer, "AFTER"))
1841 totals_before = false;
1844 lex_error_expecting (lexer, "BEFORE", "AFTER");
1848 else if (lex_match_id (lexer, "EMPTY"))
1850 lex_match (lexer, T_EQUALS);
1851 if (lex_match_id (lexer, "INCLUDE"))
1852 c->show_empty = true;
1853 else if (lex_match_id (lexer, "EXCLUDE"))
1854 c->show_empty = false;
1857 lex_error_expecting (lexer, "INCLUDE", "EXCLUDE");
1864 lex_error_expecting (lexer, "ORDER", "KEY", "MISSING",
1865 "TOTAL", "LABEL", "POSITION", "EMPTY");
1867 lex_error_expecting (lexer, "TOTAL", "LABEL", "POSITION", "EMPTY");
1874 if (c->n_cats >= allocated_cats)
1875 c->cats = x2nrealloc (c->cats, &allocated_cats, sizeof *c->cats);
1876 c->cats[c->n_cats++] = cat;
1881 if (c->n_cats >= allocated_cats)
1882 c->cats = x2nrealloc (c->cats, &allocated_cats, sizeof *c->cats);
1884 struct ctables_category *totals;
1887 insert_element (c->cats, c->n_cats, sizeof *c->cats, 0);
1888 totals = &c->cats[0];
1891 totals = &c->cats[c->n_cats];
1894 *totals = (struct ctables_category) {
1896 .total_label = total_label ? total_label : xstrdup (_("Total")),
1900 struct ctables_category *subtotal = NULL;
1901 for (size_t i = totals_before ? 0 : c->n_cats;
1902 totals_before ? i < c->n_cats : i-- > 0;
1903 totals_before ? i++ : 0)
1905 struct ctables_category *cat = &c->cats[i];
1913 cat->subtotal = subtotal;
1916 case CCT_POSTCOMPUTE:
1935 ctables_nest_uninit (struct ctables_nest *nest)
1942 ctables_stack_uninit (struct ctables_stack *stack)
1946 for (size_t i = 0; i < stack->n; i++)
1947 ctables_nest_uninit (&stack->nests[i]);
1948 free (stack->nests);
1952 static struct ctables_stack
1953 nest_fts (struct ctables_stack s0, struct ctables_stack s1)
1960 struct ctables_stack stack = { .nests = xnmalloc (s0.n, s1.n * sizeof *stack.nests) };
1961 for (size_t i = 0; i < s0.n; i++)
1962 for (size_t j = 0; j < s1.n; j++)
1964 const struct ctables_nest *a = &s0.nests[i];
1965 const struct ctables_nest *b = &s1.nests[j];
1967 size_t allocate = a->n + b->n;
1968 struct variable **vars = xnmalloc (allocate, sizeof *vars);
1969 enum pivot_axis_type *axes = xnmalloc (allocate, sizeof *axes);
1971 for (size_t k = 0; k < a->n; k++)
1972 vars[n++] = a->vars[k];
1973 for (size_t k = 0; k < b->n; k++)
1974 vars[n++] = b->vars[k];
1975 assert (n == allocate);
1977 const struct ctables_nest *summary_src;
1978 if (!a->specs[CSV_CELL].n && !a->specs[CSV_CELL].scale_var)
1980 else if (!b->specs[CSV_CELL].n && !b->specs[CSV_CELL].scale_var)
1985 struct ctables_nest *new = &stack.nests[stack.n++];
1986 *new = (struct ctables_nest) {
1988 .scale_idx = (a->scale_idx != SIZE_MAX ? a->scale_idx
1989 : b->scale_idx != SIZE_MAX ? a->n + b->scale_idx
1993 for (enum ctables_summary_variant sv = 0; sv < N_CSVS; sv++)
1994 ctables_summary_spec_set_clone (&new->specs[sv], &summary_src->specs[sv]);
1996 ctables_stack_uninit (&s0);
1997 ctables_stack_uninit (&s1);
2001 static struct ctables_stack
2002 stack_fts (struct ctables_stack s0, struct ctables_stack s1)
2004 struct ctables_stack stack = { .nests = xnmalloc (s0.n + s1.n, sizeof *stack.nests) };
2005 for (size_t i = 0; i < s0.n; i++)
2006 stack.nests[stack.n++] = s0.nests[i];
2007 for (size_t i = 0; i < s1.n; i++)
2008 stack.nests[stack.n++] = s1.nests[i];
2009 assert (stack.n == s0.n + s1.n);
2015 static struct ctables_stack
2016 enumerate_fts (enum pivot_axis_type axis_type, const struct ctables_axis *a)
2019 return (struct ctables_stack) { .n = 0 };
2024 assert (!a->var.is_mrset);
2026 struct variable **vars = xmalloc (sizeof *vars);
2029 struct ctables_nest *nest = xmalloc (sizeof *nest);
2030 *nest = (struct ctables_nest) {
2033 .scale_idx = a->scale ? 0 : SIZE_MAX,
2035 if (a->specs[CSV_CELL].n || a->scale)
2036 for (enum ctables_summary_variant sv = 0; sv < N_CSVS; sv++)
2038 ctables_summary_spec_set_clone (&nest->specs[sv], &a->specs[sv]);
2039 nest->specs[sv].scale_var = a->var.var;
2041 return (struct ctables_stack) { .nests = nest, .n = 1 };
2044 return stack_fts (enumerate_fts (axis_type, a->subs[0]),
2045 enumerate_fts (axis_type, a->subs[1]));
2048 return nest_fts (enumerate_fts (axis_type, a->subs[0]),
2049 enumerate_fts (axis_type, a->subs[1]));
2055 union ctables_summary
2057 /* COUNT, VALIDN, TOTALN. */
2064 /* MINIMUM, MAXIMUM, RANGE. */
2071 /* MEAN, SEMEAN, STDDEV, SUM, VARIANCE, *.SUM. */
2072 struct moments1 *moments;
2074 /* MEDIAN, MODE, PTILE. */
2077 struct casewriter *writer;
2082 /* XXX multiple response */
2086 ctables_summary_init (union ctables_summary *s,
2087 const struct ctables_summary_spec *ss)
2089 switch (ss->function)
2093 case CTSF_ROWPCT_COUNT:
2094 case CTSF_COLPCT_COUNT:
2095 case CTSF_TABLEPCT_COUNT:
2096 case CTSF_SUBTABLEPCT_COUNT:
2097 case CTSF_LAYERPCT_COUNT:
2098 case CTSF_LAYERROWPCT_COUNT:
2099 case CTSF_LAYERCOLPCT_COUNT:
2100 case CTSF_ROWPCT_VALIDN:
2101 case CTSF_COLPCT_VALIDN:
2102 case CTSF_TABLEPCT_VALIDN:
2103 case CTSF_SUBTABLEPCT_VALIDN:
2104 case CTSF_LAYERPCT_VALIDN:
2105 case CTSF_LAYERROWPCT_VALIDN:
2106 case CTSF_LAYERCOLPCT_VALIDN:
2107 case CTSF_ROWPCT_TOTALN:
2108 case CTSF_COLPCT_TOTALN:
2109 case CTSF_TABLEPCT_TOTALN:
2110 case CTSF_SUBTABLEPCT_TOTALN:
2111 case CTSF_LAYERPCT_TOTALN:
2112 case CTSF_LAYERROWPCT_TOTALN:
2113 case CTSF_LAYERCOLPCT_TOTALN:
2119 s->missing = s->valid = 0;
2125 s->min = s->max = SYSMIS;
2133 case CTSF_ROWPCT_SUM:
2134 case CTSF_COLPCT_SUM:
2135 case CTSF_TABLEPCT_SUM:
2136 case CTSF_SUBTABLEPCT_SUM:
2137 case CTSF_LAYERPCT_SUM:
2138 case CTSF_LAYERROWPCT_SUM:
2139 case CTSF_LAYERCOLPCT_SUM:
2140 s->moments = moments1_create (MOMENT_VARIANCE);
2147 struct caseproto *proto = caseproto_create ();
2148 proto = caseproto_add_width (proto, 0);
2149 proto = caseproto_add_width (proto, 0);
2151 struct subcase ordering;
2152 subcase_init (&ordering, 0, 0, SC_ASCEND);
2153 s->writer = sort_create_writer (&ordering, proto);
2154 subcase_uninit (&ordering);
2155 caseproto_unref (proto);
2165 ctables_summary_uninit (union ctables_summary *s,
2166 const struct ctables_summary_spec *ss)
2168 switch (ss->function)
2172 case CTSF_ROWPCT_COUNT:
2173 case CTSF_COLPCT_COUNT:
2174 case CTSF_TABLEPCT_COUNT:
2175 case CTSF_SUBTABLEPCT_COUNT:
2176 case CTSF_LAYERPCT_COUNT:
2177 case CTSF_LAYERROWPCT_COUNT:
2178 case CTSF_LAYERCOLPCT_COUNT:
2179 case CTSF_ROWPCT_VALIDN:
2180 case CTSF_COLPCT_VALIDN:
2181 case CTSF_TABLEPCT_VALIDN:
2182 case CTSF_SUBTABLEPCT_VALIDN:
2183 case CTSF_LAYERPCT_VALIDN:
2184 case CTSF_LAYERROWPCT_VALIDN:
2185 case CTSF_LAYERCOLPCT_VALIDN:
2186 case CTSF_ROWPCT_TOTALN:
2187 case CTSF_COLPCT_TOTALN:
2188 case CTSF_TABLEPCT_TOTALN:
2189 case CTSF_SUBTABLEPCT_TOTALN:
2190 case CTSF_LAYERPCT_TOTALN:
2191 case CTSF_LAYERROWPCT_TOTALN:
2192 case CTSF_LAYERCOLPCT_TOTALN:
2210 case CTSF_ROWPCT_SUM:
2211 case CTSF_COLPCT_SUM:
2212 case CTSF_TABLEPCT_SUM:
2213 case CTSF_SUBTABLEPCT_SUM:
2214 case CTSF_LAYERPCT_SUM:
2215 case CTSF_LAYERROWPCT_SUM:
2216 case CTSF_LAYERCOLPCT_SUM:
2217 moments1_destroy (s->moments);
2223 casewriter_destroy (s->writer);
2229 ctables_summary_add (union ctables_summary *s,
2230 const struct ctables_summary_spec *ss,
2231 const struct variable *scale_var, const union value *value,
2232 double d_weight, double e_weight)
2234 /* To determine whether a case is included in a given table for a particular
2235 kind of summary, consider the following charts for each variable in the
2236 table. Only if "yes" appears for every variable for the summary is the
2239 Categorical variables: VALIDN COUNT TOTALN
2240 Valid values in included categories yes yes yes
2241 Missing values in included categories --- yes yes
2242 Missing values in excluded categories --- --- yes
2243 Valid values in excluded categories --- --- ---
2245 Scale variables: VALIDN COUNT TOTALN
2246 Valid value yes yes yes
2247 Missing value --- yes yes
2249 Missing values include both user- and system-missing. (The system-missing
2250 value is always in an excluded category.)
2252 switch (ss->function)
2258 if (scale_var && var_is_value_missing (scale_var, value))
2259 s->missing += d_weight;
2261 s->valid += d_weight;
2265 case CTSF_ROWPCT_COUNT:
2266 case CTSF_COLPCT_COUNT:
2267 case CTSF_TABLEPCT_COUNT:
2268 case CTSF_SUBTABLEPCT_COUNT:
2269 case CTSF_LAYERPCT_COUNT:
2270 case CTSF_LAYERROWPCT_COUNT:
2271 case CTSF_LAYERCOLPCT_COUNT:
2272 case CTSF_ROWPCT_VALIDN:
2273 case CTSF_COLPCT_VALIDN:
2274 case CTSF_TABLEPCT_VALIDN:
2275 case CTSF_SUBTABLEPCT_VALIDN:
2276 case CTSF_LAYERPCT_VALIDN:
2277 case CTSF_LAYERROWPCT_VALIDN:
2278 case CTSF_LAYERCOLPCT_VALIDN:
2279 case CTSF_ROWPCT_TOTALN:
2280 case CTSF_COLPCT_TOTALN:
2281 case CTSF_TABLEPCT_TOTALN:
2282 case CTSF_SUBTABLEPCT_TOTALN:
2283 case CTSF_LAYERPCT_TOTALN:
2284 case CTSF_LAYERROWPCT_TOTALN:
2285 case CTSF_LAYERCOLPCT_TOTALN:
2289 if (scale_var && var_is_value_missing (scale_var, value))
2290 s->missing += e_weight;
2292 s->valid += e_weight;
2298 if (!var_is_value_missing (scale_var, value))
2300 assert (!var_is_alpha (scale_var)); /* XXX? */
2301 if (s->min == SYSMIS || value->f < s->min)
2303 if (s->max == SYSMIS || value->f > s->max)
2313 case CTSF_ROWPCT_SUM:
2314 case CTSF_COLPCT_SUM:
2315 case CTSF_TABLEPCT_SUM:
2316 case CTSF_SUBTABLEPCT_SUM:
2317 case CTSF_LAYERPCT_SUM:
2318 case CTSF_LAYERROWPCT_SUM:
2319 case CTSF_LAYERCOLPCT_SUM:
2320 if (!var_is_value_missing (scale_var, value))
2321 moments1_add (s->moments, value->f, e_weight);
2327 if (var_is_value_missing (scale_var, value))
2329 s->ovalid += e_weight;
2331 struct ccase *c = case_create (casewriter_get_proto (s->writer));
2332 *case_num_rw_idx (c, 0) = value->f;
2333 *case_num_rw_idx (c, 1) = e_weight;
2334 casewriter_write (s->writer, c);
2340 static enum ctables_domain_type
2341 ctables_function_domain (enum ctables_summary_function function)
2365 case CTSF_COLPCT_COUNT:
2366 case CTSF_COLPCT_SUM:
2367 case CTSF_COLPCT_TOTALN:
2368 case CTSF_COLPCT_VALIDN:
2371 case CTSF_LAYERCOLPCT_COUNT:
2372 case CTSF_LAYERCOLPCT_SUM:
2373 case CTSF_LAYERCOLPCT_TOTALN:
2374 case CTSF_LAYERCOLPCT_VALIDN:
2375 return CTDT_LAYERCOL;
2377 case CTSF_LAYERPCT_COUNT:
2378 case CTSF_LAYERPCT_SUM:
2379 case CTSF_LAYERPCT_TOTALN:
2380 case CTSF_LAYERPCT_VALIDN:
2383 case CTSF_LAYERROWPCT_COUNT:
2384 case CTSF_LAYERROWPCT_SUM:
2385 case CTSF_LAYERROWPCT_TOTALN:
2386 case CTSF_LAYERROWPCT_VALIDN:
2387 return CTDT_LAYERROW;
2389 case CTSF_ROWPCT_COUNT:
2390 case CTSF_ROWPCT_SUM:
2391 case CTSF_ROWPCT_TOTALN:
2392 case CTSF_ROWPCT_VALIDN:
2395 case CTSF_SUBTABLEPCT_COUNT:
2396 case CTSF_SUBTABLEPCT_SUM:
2397 case CTSF_SUBTABLEPCT_TOTALN:
2398 case CTSF_SUBTABLEPCT_VALIDN:
2399 return CTDT_SUBTABLE;
2401 case CTSF_TABLEPCT_COUNT:
2402 case CTSF_TABLEPCT_SUM:
2403 case CTSF_TABLEPCT_TOTALN:
2404 case CTSF_TABLEPCT_VALIDN:
2412 ctables_summary_value (const struct ctables_cell *cell,
2413 union ctables_summary *s,
2414 const struct ctables_summary_spec *ss)
2416 switch (ss->function)
2422 case CTSF_ROWPCT_COUNT:
2423 case CTSF_COLPCT_COUNT:
2424 case CTSF_TABLEPCT_COUNT:
2425 case CTSF_SUBTABLEPCT_COUNT:
2426 case CTSF_LAYERPCT_COUNT:
2427 case CTSF_LAYERROWPCT_COUNT:
2428 case CTSF_LAYERCOLPCT_COUNT:
2430 enum ctables_domain_type d = ctables_function_domain (ss->function);
2431 return (cell->domains[d]->e_valid
2432 ? s->valid / cell->domains[d]->e_valid * 100
2436 case CTSF_ROWPCT_VALIDN:
2437 case CTSF_COLPCT_VALIDN:
2438 case CTSF_TABLEPCT_VALIDN:
2439 case CTSF_SUBTABLEPCT_VALIDN:
2440 case CTSF_LAYERPCT_VALIDN:
2441 case CTSF_LAYERROWPCT_VALIDN:
2442 case CTSF_LAYERCOLPCT_VALIDN:
2443 case CTSF_ROWPCT_TOTALN:
2444 case CTSF_COLPCT_TOTALN:
2445 case CTSF_TABLEPCT_TOTALN:
2446 case CTSF_SUBTABLEPCT_TOTALN:
2447 case CTSF_LAYERPCT_TOTALN:
2448 case CTSF_LAYERROWPCT_TOTALN:
2449 case CTSF_LAYERCOLPCT_TOTALN:
2457 return s->valid + s->missing;
2470 return s->max != SYSMIS && s->min != SYSMIS ? s->max - s->min : SYSMIS;
2475 moments1_calculate (s->moments, NULL, &mean, NULL, NULL, NULL);
2481 double weight, variance;
2482 moments1_calculate (s->moments, &weight, NULL, &variance, NULL, NULL);
2483 return calc_semean (variance, weight);
2489 moments1_calculate (s->moments, NULL, NULL, &variance, NULL, NULL);
2490 return variance != SYSMIS ? sqrt (variance) : SYSMIS;
2495 double weight, mean;
2496 moments1_calculate (s->moments, &weight, &mean, NULL, NULL, NULL);
2497 return weight != SYSMIS && mean != SYSMIS ? weight * mean : SYSMIS;
2503 moments1_calculate (s->moments, NULL, NULL, &variance, NULL, NULL);
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:
2520 struct casereader *reader = casewriter_make_reader (s->writer);
2523 struct percentile *ptile = percentile_create (
2524 ss->function == CTSF_PTILE ? ss->percentile : 0.5, s->ovalid);
2525 struct order_stats *os = &ptile->parent;
2526 order_stats_accumulate_idx (&os, 1, reader, 1, 0);
2527 s->ovalue = percentile_calculate (ptile, PC_HAVERAGE);
2528 statistic_destroy (&ptile->parent.parent);
2535 struct casereader *reader = casewriter_make_reader (s->writer);
2538 struct mode *mode = mode_create ();
2539 struct order_stats *os = &mode->parent;
2540 order_stats_accumulate_idx (&os, 1, reader, 1, 0);
2541 s->ovalue = mode->mode;
2542 statistic_destroy (&mode->parent.parent);
2550 struct ctables_cell_sort_aux
2552 const struct ctables_nest *nest;
2553 enum pivot_axis_type a;
2557 ctables_cell_compare_3way (const void *a_, const void *b_, const void *aux_)
2559 const struct ctables_cell_sort_aux *aux = aux_;
2560 struct ctables_cell *const *ap = a_;
2561 struct ctables_cell *const *bp = b_;
2562 const struct ctables_cell *a = *ap;
2563 const struct ctables_cell *b = *bp;
2565 const struct ctables_nest *nest = aux->nest;
2566 for (size_t i = 0; i < nest->n; i++)
2567 if (i != nest->scale_idx)
2569 const struct variable *var = nest->vars[i];
2570 const struct ctables_cell_value *a_cv = &a->axes[aux->a].cvs[i];
2571 const struct ctables_cell_value *b_cv = &b->axes[aux->a].cvs[i];
2572 if (a_cv->category != b_cv->category)
2573 return a_cv->category > b_cv->category ? 1 : -1;
2575 const union value *a_val = &a_cv->value;
2576 const union value *b_val = &b_cv->value;
2577 switch (a_cv->category->type)
2583 case CCT_POSTCOMPUTE:
2584 /* Must be equal. */
2591 int cmp = value_compare_3way (a_val, b_val, var_get_width (var));
2599 int cmp = value_compare_3way (a_val, b_val, var_get_width (var));
2601 return a_cv->category->sort_ascending ? cmp : -cmp;
2607 const char *a_label = var_lookup_value_label (var, a_val);
2608 const char *b_label = var_lookup_value_label (var, b_val);
2610 ? (b_label ? strcmp (a_label, b_label) : 1)
2611 : (b_label ? -1 : value_compare_3way (
2612 a_val, b_val, var_get_width (var))));
2614 return a_cv->category->sort_ascending ? cmp : -cmp;
2628 For each ctables_table:
2629 For each combination of row vars:
2630 For each combination of column vars:
2631 For each combination of layer vars:
2633 Make a table of row values:
2634 Sort entries by row values
2635 Assign a 0-based index to each actual value
2636 Construct a dimension
2637 Make a table of column values
2638 Make a table of layer values
2640 Fill the table entry using the indexes from before.
2643 static struct ctables_domain *
2644 ctables_domain_insert (struct ctables_section *s, struct ctables_cell *cell,
2645 enum ctables_domain_type domain)
2648 for (enum pivot_axis_type a = 0; a < PIVOT_N_AXES; a++)
2650 const struct ctables_nest *nest = s->nests[a];
2651 for (size_t i = 0; i < nest->n_domains[domain]; i++)
2653 size_t v_idx = nest->domains[domain][i];
2654 hash = value_hash (&cell->axes[a].cvs[v_idx].value,
2655 var_get_width (nest->vars[v_idx]), hash);
2659 struct ctables_domain *d;
2660 HMAP_FOR_EACH_WITH_HASH (d, struct ctables_domain, node, hash, &s->domains[domain])
2662 const struct ctables_cell *df = d->example;
2663 for (enum pivot_axis_type a = 0; a < PIVOT_N_AXES; a++)
2665 const struct ctables_nest *nest = s->nests[a];
2666 for (size_t i = 0; i < nest->n_domains[domain]; i++)
2668 size_t v_idx = nest->domains[domain][i];
2669 if (!value_equal (&df->axes[a].cvs[v_idx].value,
2670 &cell->axes[a].cvs[v_idx].value,
2671 var_get_width (nest->vars[v_idx])))
2680 d = xmalloc (sizeof *d);
2681 *d = (struct ctables_domain) { .example = cell };
2682 hmap_insert (&s->domains[domain], &d->node, hash);
2686 static const struct ctables_category *
2687 ctables_categories_match (const struct ctables_categories *c,
2688 const union value *v, const struct variable *var)
2690 const struct ctables_category *othernm = NULL;
2691 for (size_t i = c->n_cats; i-- > 0; )
2693 const struct ctables_category *cat = &c->cats[i];
2697 if (cat->number == v->f)
2705 if ((cat->range[0] == -DBL_MAX || v->f >= cat->range[0])
2706 && (cat->range[1] == DBL_MAX || v->f <= cat->range[1]))
2711 if (var_is_value_missing (var, v))
2715 case CCT_POSTCOMPUTE:
2730 return (cat->include_missing || !var_is_value_missing (var, v) ? cat
2735 return var_is_value_missing (var, v) ? NULL : othernm;
2738 static const struct ctables_category *
2739 ctables_categories_total (const struct ctables_categories *c)
2741 const struct ctables_category *first = &c->cats[0];
2742 const struct ctables_category *last = &c->cats[c->n_cats - 1];
2743 return (first->type == CCT_TOTAL ? first
2744 : last->type == CCT_TOTAL ? last
2748 static struct ctables_cell *
2749 ctables_cell_insert__ (struct ctables_section *s, const struct ccase *c,
2750 const struct ctables_category *cats[PIVOT_N_AXES][10])
2753 enum ctables_summary_variant sv = CSV_CELL;
2754 for (enum pivot_axis_type a = 0; a < PIVOT_N_AXES; a++)
2756 const struct ctables_nest *nest = s->nests[a];
2757 for (size_t i = 0; i < nest->n; i++)
2758 if (i != nest->scale_idx)
2760 hash = hash_pointer (cats[a][i], hash);
2761 if (cats[a][i]->type != CCT_TOTAL
2762 && cats[a][i]->type != CCT_SUBTOTAL
2763 && cats[a][i]->type != CCT_POSTCOMPUTE)
2764 hash = value_hash (case_data (c, nest->vars[i]),
2765 var_get_width (nest->vars[i]), hash);
2771 struct ctables_cell *cell;
2772 HMAP_FOR_EACH_WITH_HASH (cell, struct ctables_cell, node, hash, &s->cells)
2774 for (enum pivot_axis_type a = 0; a < PIVOT_N_AXES; a++)
2776 const struct ctables_nest *nest = s->nests[a];
2777 for (size_t i = 0; i < nest->n; i++)
2778 if (i != nest->scale_idx
2779 && (cats[a][i] != cell->axes[a].cvs[i].category
2780 || (cats[a][i]->type != CCT_TOTAL
2781 && cats[a][i]->type != CCT_SUBTOTAL
2782 && cats[a][i]->type != CCT_POSTCOMPUTE
2783 && !value_equal (case_data (c, nest->vars[i]),
2784 &cell->axes[a].cvs[i].value,
2785 var_get_width (nest->vars[i])))))
2794 cell = xmalloc (sizeof *cell);
2797 cell->contributes_to_domains = true;
2798 cell->postcompute = false;
2799 for (enum pivot_axis_type a = 0; a < PIVOT_N_AXES; a++)
2801 const struct ctables_nest *nest = s->nests[a];
2802 cell->axes[a].cvs = (nest->n
2803 ? xnmalloc (nest->n, sizeof *cell->axes[a].cvs)
2805 for (size_t i = 0; i < nest->n; i++)
2807 const struct ctables_category *cat = cats[a][i];
2808 if (i != nest->scale_idx)
2810 const struct ctables_category *subtotal = cat->subtotal;
2811 if (cat->hide || (subtotal && subtotal->hide_subcategories))
2814 if (cat->type == CCT_TOTAL
2815 || cat->type == CCT_SUBTOTAL
2816 || cat->type == CCT_POSTCOMPUTE)
2817 cell->contributes_to_domains = false;
2818 if (cat->type == CCT_POSTCOMPUTE)
2819 cell->postcompute = true;
2822 cell->axes[a].cvs[i].category = cat;
2823 value_clone (&cell->axes[a].cvs[i].value, case_data (c, nest->vars[i]),
2824 var_get_width (nest->vars[i]));
2828 const struct ctables_nest *ss = s->nests[s->table->summary_axis];
2829 const struct ctables_summary_spec_set *specs = &ss->specs[cell->sv];
2830 cell->summaries = xmalloc (specs->n * sizeof *cell->summaries);
2831 for (size_t i = 0; i < specs->n; i++)
2832 ctables_summary_init (&cell->summaries[i], &specs->specs[i]);
2833 for (enum ctables_domain_type dt = 0; dt < N_CTDTS; dt++)
2834 cell->domains[dt] = ctables_domain_insert (s, cell, dt);
2835 hmap_insert (&s->cells, &cell->node, hash);
2840 ctables_cell_add__ (struct ctables_section *s, const struct ccase *c,
2841 const struct ctables_category *cats[PIVOT_N_AXES][10],
2842 double d_weight, double e_weight)
2844 struct ctables_cell *cell = ctables_cell_insert__ (s, c, cats);
2845 const struct ctables_nest *ss = s->nests[s->table->summary_axis];
2847 const struct ctables_summary_spec_set *specs = &ss->specs[cell->sv];
2848 for (size_t i = 0; i < specs->n; i++)
2850 const struct variable *scale_var = specs->scale_var;
2851 const union value *value = scale_var ? case_data (c, scale_var) : NULL;
2852 ctables_summary_add (&cell->summaries[i], &specs->specs[i],
2853 scale_var, value, d_weight, e_weight);
2855 if (cell->contributes_to_domains)
2857 for (enum ctables_domain_type dt = 0; dt < N_CTDTS; dt++)
2859 cell->domains[dt]->d_valid += d_weight;
2860 cell->domains[dt]->e_valid += e_weight;
2866 recurse_totals (struct ctables_section *s, const struct ccase *c,
2867 const struct ctables_category *cats[PIVOT_N_AXES][10],
2868 double d_weight, double e_weight,
2869 enum pivot_axis_type start_axis, size_t start_nest)
2871 for (enum pivot_axis_type a = start_axis; a < PIVOT_N_AXES; a++)
2873 const struct ctables_nest *nest = s->nests[a];
2874 for (size_t i = start_nest; i < nest->n; i++)
2876 if (i == nest->scale_idx)
2879 const struct variable *var = nest->vars[i];
2881 const struct ctables_category *total = ctables_categories_total (
2882 s->table->categories[var_get_dict_index (var)]);
2885 const struct ctables_category *save = cats[a][i];
2887 ctables_cell_add__ (s, c, cats, d_weight, e_weight);
2888 recurse_totals (s, c, cats, d_weight, e_weight, a, i + 1);
2897 recurse_subtotals (struct ctables_section *s, const struct ccase *c,
2898 const struct ctables_category *cats[PIVOT_N_AXES][10],
2899 double d_weight, double e_weight,
2900 enum pivot_axis_type start_axis, size_t start_nest)
2902 for (enum pivot_axis_type a = start_axis; a < PIVOT_N_AXES; a++)
2904 const struct ctables_nest *nest = s->nests[a];
2905 for (size_t i = start_nest; i < nest->n; i++)
2907 if (i == nest->scale_idx)
2910 const struct ctables_category *save = cats[a][i];
2913 cats[a][i] = save->subtotal;
2914 ctables_cell_add__ (s, c, cats, d_weight, e_weight);
2915 recurse_subtotals (s, c, cats, d_weight, e_weight, a, i + 1);
2924 ctables_add_occurrence (const struct variable *var,
2925 const union value *value,
2926 struct hmap *occurrences)
2928 int width = var_get_width (var);
2929 unsigned int hash = value_hash (value, width, 0);
2931 struct ctables_occurrence *o;
2932 HMAP_FOR_EACH_WITH_HASH (o, struct ctables_occurrence, node, hash,
2934 if (value_equal (value, &o->value, width))
2937 o = xmalloc (sizeof *o);
2938 value_clone (&o->value, value, width);
2939 hmap_insert (occurrences, &o->node, hash);
2943 ctables_cell_insert (struct ctables_section *s,
2944 const struct ccase *c,
2945 double d_weight, double e_weight)
2947 const struct ctables_category *cats[PIVOT_N_AXES][10]; /* XXX */
2948 for (enum pivot_axis_type a = 0; a < PIVOT_N_AXES; a++)
2950 const struct ctables_nest *nest = s->nests[a];
2951 for (size_t i = 0; i < nest->n; i++)
2953 if (i == nest->scale_idx)
2956 const struct variable *var = nest->vars[i];
2957 const union value *value = case_data (c, var);
2959 if (var_is_numeric (var) && value->f == SYSMIS)
2962 cats[a][i] = ctables_categories_match (
2963 s->table->categories[var_get_dict_index (var)], value, var);
2969 for (enum pivot_axis_type a = 0; a < PIVOT_N_AXES; a++)
2971 const struct ctables_nest *nest = s->nests[a];
2972 for (size_t i = 0; i < nest->n; i++)
2973 if (i != nest->scale_idx)
2975 const struct variable *var = nest->vars[i];
2976 const union value *value = case_data (c, var);
2977 ctables_add_occurrence (var, value, &s->occurrences[a][i]);
2981 ctables_cell_add__ (s, c, cats, d_weight, e_weight);
2983 recurse_totals (s, c, cats, d_weight, e_weight, 0, 0);
2984 recurse_subtotals (s, c, cats, d_weight, e_weight, 0, 0);
2989 const struct ctables_summary_spec_set *set;
2994 merge_item_compare_3way (const struct merge_item *a, const struct merge_item *b)
2996 const struct ctables_summary_spec *as = &a->set->specs[a->ofs];
2997 const struct ctables_summary_spec *bs = &b->set->specs[b->ofs];
2998 if (as->function != bs->function)
2999 return as->function > bs->function ? 1 : -1;
3000 else if (as->percentile != bs->percentile)
3001 return as->percentile < bs->percentile ? 1 : -1;
3002 return strcmp (as->label, bs->label);
3005 static struct pivot_value *
3006 ctables_category_create_label (const struct ctables_category *cat,
3007 const struct variable *var,
3008 const union value *value)
3010 return (cat->type == CCT_TOTAL || cat->type == CCT_SUBTOTAL
3011 ? pivot_value_new_user_text (cat->total_label, SIZE_MAX)
3012 : cat->type == CCT_POSTCOMPUTE && cat->pc->label
3013 ? pivot_value_new_user_text (cat->pc->label, SIZE_MAX)
3014 : pivot_value_new_var_value (var, value));
3017 static struct ctables_value *
3018 ctables_value_find__ (struct ctables_table *t, const union value *value,
3019 int width, unsigned int hash)
3021 struct ctables_value *clv;
3022 HMAP_FOR_EACH_WITH_HASH (clv, struct ctables_value, node,
3023 hash, &t->clabels_values_map)
3024 if (value_equal (value, &clv->value, width))
3030 ctables_value_insert (struct ctables_table *t, const union value *value,
3033 unsigned int hash = value_hash (value, width, 0);
3034 struct ctables_value *clv = ctables_value_find__ (t, value, width, hash);
3037 clv = xmalloc (sizeof *clv);
3038 value_clone (&clv->value, value, width);
3039 hmap_insert (&t->clabels_values_map, &clv->node, hash);
3043 static struct ctables_value *
3044 ctables_value_find (struct ctables_table *t,
3045 const union value *value, int width)
3047 return ctables_value_find__ (t, value, width,
3048 value_hash (value, width, 0));
3052 ctables_table_add_section (struct ctables_table *t, enum pivot_axis_type a,
3053 size_t ix[PIVOT_N_AXES])
3055 if (a < PIVOT_N_AXES)
3057 size_t limit = MAX (t->stacks[a].n, 1);
3058 for (ix[a] = 0; ix[a] < limit; ix[a]++)
3059 ctables_table_add_section (t, a + 1, ix);
3063 struct ctables_section *s = &t->sections[t->n_sections++];
3064 *s = (struct ctables_section) {
3066 .cells = HMAP_INITIALIZER (s->cells),
3068 for (a = 0; a < PIVOT_N_AXES; a++)
3071 struct ctables_nest *nest = &t->stacks[a].nests[ix[a]];
3073 s->occurrences[a] = xnmalloc (nest->n, sizeof *s->occurrences[a]);
3074 for (size_t i = 0; i < nest->n; i++)
3075 hmap_init (&s->occurrences[a][i]);
3077 for (size_t i = 0; i < N_CTDTS; i++)
3078 hmap_init (&s->domains[i]);
3083 ctpo_add (double a, double b)
3089 ctpo_sub (double a, double b)
3095 ctpo_mul (double a, double b)
3101 ctpo_div (double a, double b)
3103 return b ? a / b : SYSMIS;
3107 ctpo_pow (double a, double b)
3109 int save_errno = errno;
3111 double result = pow (a, b);
3119 ctpo_neg (double a, double b UNUSED)
3124 struct ctables_pcexpr_evaluate_ctx
3126 const struct ctables_cell *cell;
3127 const struct ctables_section *section;
3128 const struct ctables_categories *cats;
3129 enum pivot_axis_type pc_a;
3133 static double ctables_pcexpr_evaluate (
3134 const struct ctables_pcexpr_evaluate_ctx *, const struct ctables_pcexpr *);
3137 ctables_pcexpr_evaluate_nonterminal (
3138 const struct ctables_pcexpr_evaluate_ctx *ctx,
3139 const struct ctables_pcexpr *e, size_t n_args,
3140 double evaluate (double, double))
3142 double args[2] = { 0, 0 };
3143 for (size_t i = 0; i < n_args; i++)
3145 args[i] = ctables_pcexpr_evaluate (ctx, e->subs[i]);
3146 if (!isfinite (args[i]) || args[i] == SYSMIS)
3149 return evaluate (args[0], args[1]);
3153 ctables_pcexpr_evaluate_category (const struct ctables_pcexpr_evaluate_ctx *ctx,
3154 const struct ctables_cell_value *pc_cv)
3156 const struct ctables_section *s = ctx->section;
3159 for (enum pivot_axis_type a = 0; a < PIVOT_N_AXES; a++)
3161 const struct ctables_nest *nest = s->nests[a];
3162 for (size_t i = 0; i < nest->n; i++)
3163 if (i != nest->scale_idx)
3165 const struct ctables_cell_value *cv
3166 = (a == ctx->pc_a && i == ctx->pc_a_idx ? pc_cv
3167 : &ctx->cell->axes[a].cvs[i]);
3168 hash = hash_pointer (cv->category, hash);
3169 if (cv->category->type != CCT_TOTAL
3170 && cv->category->type != CCT_SUBTOTAL
3171 && cv->category->type != CCT_POSTCOMPUTE)
3172 hash = value_hash (&cv->value,
3173 var_get_width (nest->vars[i]), hash);
3177 struct ctables_cell *tc;
3178 HMAP_FOR_EACH_WITH_HASH (tc, struct ctables_cell, node, hash, &s->cells)
3180 for (enum pivot_axis_type a = 0; a < PIVOT_N_AXES; a++)
3182 const struct ctables_nest *nest = s->nests[a];
3183 for (size_t i = 0; i < nest->n; i++)
3184 if (i != nest->scale_idx)
3186 const struct ctables_cell_value *p_cv
3187 = (a == ctx->pc_a && i == ctx->pc_a_idx ? pc_cv
3188 : &ctx->cell->axes[a].cvs[i]);
3189 const struct ctables_cell_value *t_cv = &tc->axes[a].cvs[i];
3190 if (p_cv->category != t_cv->category
3191 || (p_cv->category->type != CCT_TOTAL
3192 && p_cv->category->type != CCT_SUBTOTAL
3193 && p_cv->category->type != CCT_POSTCOMPUTE
3194 && !value_equal (&p_cv->value,
3196 var_get_width (nest->vars[i]))))
3208 const struct ctables_table *t = s->table;
3209 const struct ctables_nest *specs_nest = s->nests[t->summary_axis];
3210 const struct ctables_summary_spec_set *specs = &specs_nest->specs[tc->sv];
3211 size_t j = 0 /* XXX */;
3212 return ctables_summary_value (tc, &tc->summaries[j], &specs->specs[j]);
3216 ctables_pcexpr_evaluate (const struct ctables_pcexpr_evaluate_ctx *ctx,
3217 const struct ctables_pcexpr *e)
3224 case CTPO_CAT_RANGE:
3226 struct ctables_cell_value cv = {
3227 .category = ctables_find_category_for_postcompute (ctx->cats, e)
3229 assert (cv.category != NULL);
3231 struct hmap *occurrences = &ctx->section->occurrences[ctx->pc_a][ctx->pc_a_idx];
3232 const struct ctables_occurrence *o;
3235 const struct variable *var = ctx->section->nests[ctx->pc_a]->vars[ctx->pc_a_idx];
3236 HMAP_FOR_EACH (o, struct ctables_occurrence, node, occurrences)
3237 if (ctables_categories_match (ctx->cats, &o->value, var) == cv.category)
3239 cv.value = o->value;
3240 sum += ctables_pcexpr_evaluate_category (ctx, &cv);
3245 case CTPO_CAT_NUMBER:
3246 case CTPO_CAT_STRING:
3247 case CTPO_CAT_MISSING:
3248 case CTPO_CAT_OTHERNM:
3249 case CTPO_CAT_SUBTOTAL:
3250 case CTPO_CAT_TOTAL:
3252 struct ctables_cell_value cv = {
3253 .category = ctables_find_category_for_postcompute (ctx->cats, e),
3254 .value = { .f = e->number },
3256 assert (cv.category != NULL);
3257 return ctables_pcexpr_evaluate_category (ctx, &cv);
3261 return ctables_pcexpr_evaluate_nonterminal (ctx, e, 2, ctpo_add);
3264 return ctables_pcexpr_evaluate_nonterminal (ctx, e, 2, ctpo_sub);
3267 return ctables_pcexpr_evaluate_nonterminal (ctx, e, 2, ctpo_mul);
3270 return ctables_pcexpr_evaluate_nonterminal (ctx, e, 2, ctpo_div);
3273 return ctables_pcexpr_evaluate_nonterminal (ctx, e, 2, ctpo_pow);
3276 return ctables_pcexpr_evaluate_nonterminal (ctx, e, 1, ctpo_neg);
3283 ctables_cell_calculate_postcompute (const struct ctables_section *s,
3284 const struct ctables_cell *cell)
3286 enum pivot_axis_type pc_a;
3288 const struct ctables_postcompute *pc;
3289 for (pc_a = 0; ; pc_a++)
3291 assert (pc_a < PIVOT_N_AXES);
3292 for (pc_a_idx = 0; pc_a_idx < s->nests[pc_a]->n; pc_a_idx++)
3294 const struct ctables_cell_value *cv = &cell->axes[pc_a].cvs[pc_a_idx];
3295 if (cv->category->type == CCT_POSTCOMPUTE)
3297 pc = cv->category->pc;
3304 const struct variable *var = s->nests[pc_a]->vars[pc_a_idx];
3305 const struct ctables_categories *cats = s->table->categories[
3306 var_get_dict_index (var)];
3307 struct ctables_pcexpr_evaluate_ctx ctx = {
3312 .pc_a_idx = pc_a_idx,
3314 return ctables_pcexpr_evaluate (&ctx, pc->expr);
3318 ctables_table_output (struct ctables *ct, struct ctables_table *t)
3320 struct pivot_table *pt = pivot_table_create__ (
3322 ? pivot_value_new_user_text (t->title, SIZE_MAX)
3323 : pivot_value_new_text (N_("Custom Tables"))),
3326 pivot_table_set_caption (
3327 pt, pivot_value_new_user_text (t->caption, SIZE_MAX));
3329 pivot_table_set_caption (
3330 pt, pivot_value_new_user_text (t->corner, SIZE_MAX));
3332 bool summary_dimension = (t->summary_axis != t->slabels_axis
3333 || (!t->slabels_visible
3334 && t->summary_specs.n > 1));
3335 if (summary_dimension)
3337 struct pivot_dimension *d = pivot_dimension_create (
3338 pt, t->slabels_axis, N_("Statistics"));
3339 const struct ctables_summary_spec_set *specs = &t->summary_specs;
3340 if (!t->slabels_visible)
3341 d->hide_all_labels = true;
3342 for (size_t i = 0; i < specs->n; i++)
3343 pivot_category_create_leaf (
3344 d->root, pivot_value_new_text (specs->specs[i].label));
3347 bool categories_dimension = t->clabels_example != NULL;
3348 if (categories_dimension)
3350 struct pivot_dimension *d = pivot_dimension_create (
3351 pt, t->label_axis[t->clabels_from_axis],
3352 t->clabels_from_axis == PIVOT_AXIS_ROW
3353 ? N_("Row Categories")
3354 : N_("Column Categories"));
3355 const struct variable *var = t->clabels_example;
3356 const struct ctables_categories *c = t->categories[var_get_dict_index (var)];
3357 for (size_t i = 0; i < t->n_clabels_values; i++)
3359 const struct ctables_value *value = t->clabels_values[i];
3360 const struct ctables_category *cat = ctables_categories_match (c, &value->value, var);
3361 assert (cat != NULL);
3362 pivot_category_create_leaf (d->root, ctables_category_create_label (
3363 cat, t->clabels_example, &value->value));
3367 pivot_table_set_look (pt, ct->look);
3368 struct pivot_dimension *d[PIVOT_N_AXES];
3369 for (enum pivot_axis_type a = 0; a < PIVOT_N_AXES; a++)
3371 static const char *names[] = {
3372 [PIVOT_AXIS_ROW] = N_("Rows"),
3373 [PIVOT_AXIS_COLUMN] = N_("Columns"),
3374 [PIVOT_AXIS_LAYER] = N_("Layers"),
3376 d[a] = (t->axes[a] || a == t->summary_axis
3377 ? pivot_dimension_create (pt, a, names[a])
3382 assert (t->axes[a]);
3384 for (size_t i = 0; i < t->stacks[a].n; i++)
3386 struct ctables_nest *nest = &t->stacks[a].nests[i];
3387 struct ctables_section **sections = xnmalloc (t->n_sections,
3389 size_t n_sections = 0;
3391 size_t n_total_cells = 0;
3392 size_t max_depth = 0;
3393 for (size_t j = 0; j < t->n_sections; j++)
3394 if (t->sections[j].nests[a] == nest)
3396 struct ctables_section *s = &t->sections[j];
3397 sections[n_sections++] = s;
3398 n_total_cells += s->cells.count;
3400 size_t depth = s->nests[a]->n;
3401 max_depth = MAX (depth, max_depth);
3404 struct ctables_cell **sorted = xnmalloc (n_total_cells,
3406 size_t n_sorted = 0;
3408 for (size_t j = 0; j < n_sections; j++)
3410 struct ctables_section *s = sections[j];
3412 struct ctables_cell *cell;
3413 HMAP_FOR_EACH (cell, struct ctables_cell, node, &s->cells)
3415 sorted[n_sorted++] = cell;
3416 assert (n_sorted <= n_total_cells);
3419 struct ctables_cell_sort_aux aux = { .nest = nest, .a = a };
3420 sort (sorted, n_sorted, sizeof *sorted, ctables_cell_compare_3way, &aux);
3422 struct ctables_level
3424 enum ctables_level_type
3426 CTL_VAR, /* Variable label for nest->vars[var_idx]. */
3427 CTL_CATEGORY, /* Category for nest->vars[var_idx]. */
3428 CTL_SUMMARY, /* Summary functions. */
3432 enum settings_value_show vlabel; /* CTL_VAR only. */
3435 struct ctables_level *levels = xnmalloc (1 + 2 * max_depth, sizeof *levels);
3436 size_t n_levels = 0;
3437 for (size_t k = 0; k < nest->n; k++)
3439 enum ctables_vlabel vlabel = ct->vlabels[var_get_dict_index (nest->vars[k])];
3440 if (vlabel != CTVL_NONE)
3442 levels[n_levels++] = (struct ctables_level) {
3444 .vlabel = (enum settings_value_show) vlabel,
3449 if (nest->scale_idx != k
3450 && (k != nest->n - 1 || t->label_axis[a] == a))
3452 levels[n_levels++] = (struct ctables_level) {
3453 .type = CTL_CATEGORY,
3459 if (!summary_dimension && a == t->slabels_axis)
3461 levels[n_levels++] = (struct ctables_level) {
3462 .type = CTL_SUMMARY,
3463 .var_idx = SIZE_MAX,
3467 /* Pivot categories:
3469 - variable label for nest->vars[0], if vlabel != CTVL_NONE
3470 - category for nest->vars[0], if nest->scale_idx != 0
3471 - variable label for nest->vars[1], if vlabel != CTVL_NONE
3472 - category for nest->vars[1], if nest->scale_idx != 1
3474 - variable label for nest->vars[n - 1], if vlabel != CTVL_NONE
3475 - category for nest->vars[n - 1], if t->label_axis[a] == a && nest->scale_idx != n - 1.
3476 - summary function, if 'a == t->slabels_axis && a ==
3479 Additional dimensions:
3481 - If 'a == t->slabels_axis && a != t->summary_axis', add a summary
3483 - If 't->label_axis[b] == a' for some 'b != a', add a category
3488 struct pivot_category **groups = xnmalloc (1 + 2 * max_depth, sizeof *groups);
3490 for (size_t j = 0; j < n_sorted; j++)
3492 struct ctables_cell *cell = sorted[j];
3493 struct ctables_cell *prev = j > 0 ? sorted[j - 1] : NULL;
3495 size_t n_common = 0;
3498 for (; n_common < n_levels; n_common++)
3500 const struct ctables_level *level = &levels[n_common];
3501 if (level->type == CTL_CATEGORY)
3503 size_t var_idx = level->var_idx;
3504 const struct ctables_category *c = cell->axes[a].cvs[var_idx].category;
3505 if (prev->axes[a].cvs[var_idx].category != c)
3507 else if (c->type != CCT_SUBTOTAL
3508 && c->type != CCT_TOTAL
3509 && c->type != CCT_POSTCOMPUTE
3510 && !value_equal (&prev->axes[a].cvs[var_idx].value,
3511 &cell->axes[a].cvs[var_idx].value,
3512 var_get_type (nest->vars[var_idx])))
3518 for (size_t k = n_common; k < n_levels; k++)
3520 const struct ctables_level *level = &levels[k];
3521 struct pivot_category *parent = k ? groups[k - 1] : d[a]->root;
3522 if (level->type == CTL_SUMMARY)
3524 assert (k == n_levels - 1);
3526 const struct ctables_summary_spec_set *specs = &t->summary_specs;
3527 for (size_t m = 0; m < specs->n; m++)
3529 int leaf = pivot_category_create_leaf (
3530 parent, pivot_value_new_text (specs->specs[m].label));
3537 const struct variable *var = nest->vars[level->var_idx];
3538 struct pivot_value *label;
3539 if (level->type == CTL_VAR)
3541 label = pivot_value_new_variable (var);
3542 label->variable.show = level->vlabel;
3544 else if (level->type == CTL_CATEGORY)
3546 const struct ctables_cell_value *cv = &cell->axes[a].cvs[level->var_idx];
3547 label = ctables_category_create_label (cv->category,
3553 if (k == n_levels - 1)
3554 prev_leaf = pivot_category_create_leaf (parent, label);
3556 groups[k] = pivot_category_create_group__ (parent, label);
3560 cell->axes[a].leaf = prev_leaf;
3567 for (size_t i = 0; i < t->n_sections; i++)
3569 struct ctables_section *s = &t->sections[i];
3571 struct ctables_cell *cell;
3572 HMAP_FOR_EACH (cell, struct ctables_cell, node, &s->cells)
3577 const struct ctables_nest *specs_nest = s->nests[t->summary_axis];
3578 const struct ctables_summary_spec_set *specs = &specs_nest->specs[cell->sv];
3579 for (size_t j = 0; j < specs->n; j++)
3582 size_t n_dindexes = 0;
3584 if (summary_dimension)
3585 dindexes[n_dindexes++] = specs->specs[j].axis_idx;
3587 if (categories_dimension)
3589 const struct ctables_nest *clabels_nest = s->nests[t->clabels_from_axis];
3590 const struct variable *var = clabels_nest->vars[clabels_nest->n - 1];
3591 const union value *value = &cell->axes[t->clabels_from_axis].cvs[clabels_nest->n - 1].value;
3592 const struct ctables_value *ctv = ctables_value_find (t, value, var_get_width (var));
3595 dindexes[n_dindexes++] = ctv->leaf;
3598 for (enum pivot_axis_type a = 0; a < PIVOT_N_AXES; a++)
3601 int leaf = cell->axes[a].leaf;
3602 if (a == t->summary_axis && !summary_dimension)
3604 dindexes[n_dindexes++] = leaf;
3607 const struct ctables_summary_spec *ss = &specs->specs[j];
3609 double d = (cell->postcompute
3610 ? ctables_cell_calculate_postcompute (s, cell)
3611 : ctables_summary_value (cell, &cell->summaries[j], ss));
3612 struct pivot_value *value;
3613 if (ct->hide_threshold != 0
3614 && d < ct->hide_threshold
3615 && (cell->postcompute
3617 : ctables_summary_function_is_count (ss->function)))
3619 value = pivot_value_new_user_text_nocopy (
3620 xasprintf ("<%d", ct->hide_threshold));
3622 else if (d == 0 && ct->zero)
3623 value = pivot_value_new_user_text (ct->zero, SIZE_MAX);
3624 else if (d == SYSMIS && ct->missing)
3625 value = pivot_value_new_user_text (ct->missing, SIZE_MAX);
3626 else if (specs->specs[j].is_ctables_format)
3628 char *s = data_out_stretchy (&(union value) { .f = d },
3630 &specs->specs[j].format,
3631 &ct->ctables_formats, NULL);
3632 value = pivot_value_new_user_text_nocopy (s);
3636 value = pivot_value_new_number (d);
3637 value->numeric.format = specs->specs[j].format;
3639 pivot_table_put (pt, dindexes, n_dindexes, value);
3644 pivot_table_submit (pt);
3648 ctables_check_label_position (struct ctables_table *t, enum pivot_axis_type a)
3650 enum pivot_axis_type label_pos = t->label_axis[a];
3654 t->clabels_from_axis = a;
3656 const char *subcommand_name = a == PIVOT_AXIS_ROW ? "ROWLABELS" : "COLLABELS";
3657 const char *pos_name = label_pos == PIVOT_AXIS_LAYER ? "LAYER" : "OPPOSITE";
3659 const struct ctables_stack *stack = &t->stacks[a];
3663 const struct ctables_nest *n0 = &stack->nests[0];
3665 const struct variable *v0 = n0->vars[n0->n - 1];
3666 struct ctables_categories *c0 = t->categories[var_get_dict_index (v0)];
3667 t->clabels_example = v0;
3669 for (size_t i = 0; i < c0->n_cats; i++)
3670 if (c0->cats[i].type == CCT_FUNCTION)
3672 msg (SE, _("%s=%s is not allowed with sorting based "
3673 "on a summary function."),
3674 subcommand_name, pos_name);
3677 if (n0->n - 1 == n0->scale_idx)
3679 msg (SE, _("%s=%s requires the variables to be moved to be categorical, "
3680 "but %s is a scale variable."),
3681 subcommand_name, pos_name, var_get_name (v0));
3685 for (size_t i = 1; i < stack->n; i++)
3687 const struct ctables_nest *ni = &stack->nests[i];
3689 const struct variable *vi = ni->vars[ni->n - 1];
3690 struct ctables_categories *ci = t->categories[var_get_dict_index (vi)];
3692 if (ni->n - 1 == ni->scale_idx)
3694 msg (SE, _("%s=%s requires the variables to be moved to be "
3695 "categorical, but %s is a scale variable."),
3696 subcommand_name, pos_name, var_get_name (vi));
3699 if (var_get_width (v0) != var_get_width (vi))
3701 msg (SE, _("%s=%s requires the variables to be "
3702 "moved to have the same width, but %s has "
3703 "width %d and %s has width %d."),
3704 subcommand_name, pos_name,
3705 var_get_name (v0), var_get_width (v0),
3706 var_get_name (vi), var_get_width (vi));
3709 if (!val_labs_equal (var_get_value_labels (v0),
3710 var_get_value_labels (vi)))
3712 msg (SE, _("%s=%s requires the variables to be "
3713 "moved to have the same value labels, but %s "
3714 "and %s have different value labels."),
3715 subcommand_name, pos_name,
3716 var_get_name (v0), var_get_name (vi));
3719 if (!ctables_categories_equal (c0, ci))
3721 msg (SE, _("%s=%s requires the variables to be "
3722 "moved to have the same category "
3723 "specifications, but %s and %s have different "
3724 "category specifications."),
3725 subcommand_name, pos_name,
3726 var_get_name (v0), var_get_name (vi));
3735 ctables_prepare_table (struct ctables_table *t)
3737 for (enum pivot_axis_type a = 0; a < PIVOT_N_AXES; a++)
3740 t->stacks[a] = enumerate_fts (a, t->axes[a]);
3742 for (size_t j = 0; j < t->stacks[a].n; j++)
3744 struct ctables_nest *nest = &t->stacks[a].nests[j];
3745 for (enum ctables_domain_type dt = 0; dt < N_CTDTS; dt++)
3747 nest->domains[dt] = xmalloc (nest->n * sizeof *nest->domains[dt]);
3748 nest->n_domains[dt] = 0;
3750 for (size_t k = 0; k < nest->n; k++)
3752 if (k == nest->scale_idx)
3761 if (a != PIVOT_AXIS_LAYER)
3768 if (dt == CTDT_SUBTABLE ? a != PIVOT_AXIS_LAYER
3769 : dt == CTDT_ROW ? a == PIVOT_AXIS_COLUMN
3770 : a == PIVOT_AXIS_ROW)
3772 if (k == nest->n - 1
3773 || (nest->scale_idx == nest->n - 1
3774 && k == nest->n - 2))
3780 if (a == PIVOT_AXIS_COLUMN)
3785 if (a == PIVOT_AXIS_ROW)
3790 nest->domains[dt][nest->n_domains[dt]++] = k;
3797 struct ctables_nest *nest = xmalloc (sizeof *nest);
3798 *nest = (struct ctables_nest) { .n = 0 };
3799 t->stacks[a] = (struct ctables_stack) { .nests = nest, .n = 1 };
3802 struct ctables_stack *stack = &t->stacks[t->summary_axis];
3803 for (size_t i = 0; i < stack->n; i++)
3805 struct ctables_nest *nest = &stack->nests[i];
3806 if (!nest->specs[CSV_CELL].n)
3808 struct ctables_summary_spec_set *specs = &nest->specs[CSV_CELL];
3809 specs->specs = xmalloc (sizeof *specs->specs);
3812 enum ctables_summary_function function
3813 = specs->scale_var ? CTSF_MEAN : CTSF_COUNT;
3814 struct ctables_var var = { .var = specs->scale_var };
3816 *specs->specs = (struct ctables_summary_spec) {
3817 .function = function,
3818 .format = ctables_summary_default_format (function, &var),
3819 .label = ctables_summary_default_label (function, 0),
3822 ctables_summary_spec_set_clone (&nest->specs[CSV_TOTAL],
3823 &nest->specs[CSV_CELL]);
3825 else if (!nest->specs[CSV_TOTAL].n)
3826 ctables_summary_spec_set_clone (&nest->specs[CSV_TOTAL],
3827 &nest->specs[CSV_CELL]);
3830 struct ctables_summary_spec_set *merged = &t->summary_specs;
3831 struct merge_item *items = xnmalloc (2 * stack->n, sizeof *items);
3833 for (size_t j = 0; j < stack->n; j++)
3835 const struct ctables_nest *nest = &stack->nests[j];
3837 for (enum ctables_summary_variant sv = 0; sv < N_CSVS; sv++)
3838 items[n_left++] = (struct merge_item) { .set = &nest->specs[sv] };
3843 struct merge_item min = items[0];
3844 for (size_t j = 1; j < n_left; j++)
3845 if (merge_item_compare_3way (&items[j], &min) < 0)
3848 if (merged->n >= merged->allocated)
3849 merged->specs = x2nrealloc (merged->specs, &merged->allocated,
3850 sizeof *merged->specs);
3851 merged->specs[merged->n++] = min.set->specs[min.ofs];
3853 for (size_t j = 0; j < n_left; )
3855 if (merge_item_compare_3way (&items[j], &min) == 0)
3857 struct merge_item *item = &items[j];
3858 item->set->specs[item->ofs].axis_idx = merged->n - 1;
3859 if (++item->ofs >= item->set->n)
3861 items[j] = items[--n_left];
3870 for (size_t j = 0; j < merged->n; j++)
3871 printf ("%s\n", ctables_summary_function_name (merged->specs[j].function));
3873 for (size_t j = 0; j < stack->n; j++)
3875 const struct ctables_nest *nest = &stack->nests[j];
3876 for (enum ctables_summary_variant sv = 0; sv < N_CSVS; sv++)
3878 const struct ctables_summary_spec_set *specs = &nest->specs[sv];
3879 for (size_t k = 0; k < specs->n; k++)
3880 printf ("(%s, %zu) ", ctables_summary_function_name (specs->specs[k].function),
3881 specs->specs[k].axis_idx);
3887 return (ctables_check_label_position (t, PIVOT_AXIS_ROW)
3888 && ctables_check_label_position (t, PIVOT_AXIS_COLUMN));
3892 ctables_insert_clabels_values (struct ctables_table *t, const struct ccase *c,
3893 enum pivot_axis_type a)
3895 struct ctables_stack *stack = &t->stacks[a];
3896 for (size_t i = 0; i < stack->n; i++)
3898 const struct ctables_nest *nest = &stack->nests[i];
3899 const struct variable *var = nest->vars[nest->n - 1];
3900 const union value *value = case_data (c, var);
3902 if (var_is_numeric (var) && value->f == SYSMIS)
3905 if (ctables_categories_match (t->categories [var_get_dict_index (var)],
3907 ctables_value_insert (t, value, var_get_width (var));
3912 compare_clabels_values_3way (const void *a_, const void *b_, const void *width_)
3914 const struct ctables_value *const *ap = a_;
3915 const struct ctables_value *const *bp = b_;
3916 const struct ctables_value *a = *ap;
3917 const struct ctables_value *b = *bp;
3918 const int *width = width_;
3919 return value_compare_3way (&a->value, &b->value, *width);
3923 ctables_sort_clabels_values (struct ctables_table *t)
3925 const struct variable *v0 = t->clabels_example;
3926 int width = var_get_width (v0);
3928 struct ctables_categories *c0 = t->categories[var_get_dict_index (v0)];
3931 const struct val_labs *val_labs = var_get_value_labels (v0);
3932 for (const struct val_lab *vl = val_labs_first (val_labs); vl;
3933 vl = val_labs_next (val_labs, vl))
3934 if (ctables_categories_match (c0, &vl->value, v0))
3935 ctables_value_insert (t, &vl->value, width);
3938 size_t n = hmap_count (&t->clabels_values_map);
3939 t->clabels_values = xnmalloc (n, sizeof *t->clabels_values);
3941 struct ctables_value *clv;
3943 HMAP_FOR_EACH (clv, struct ctables_value, node, &t->clabels_values_map)
3944 t->clabels_values[i++] = clv;
3945 t->n_clabels_values = n;
3948 sort (t->clabels_values, n, sizeof *t->clabels_values,
3949 compare_clabels_values_3way, &width);
3951 for (size_t i = 0; i < n; i++)
3952 t->clabels_values[i]->leaf = i;
3956 ctables_add_category_occurrences (const struct variable *var,
3957 struct hmap *occurrences,
3958 const struct ctables_categories *cats)
3960 const struct val_labs *val_labs = var_get_value_labels (var);
3962 for (size_t i = 0; i < cats->n_cats; i++)
3964 const struct ctables_category *c = &cats->cats[i];
3968 ctables_add_occurrence (var, &(const union value) { .f = c->number },
3976 assert (var_is_numeric (var));
3977 for (const struct val_lab *vl = val_labs_first (val_labs); vl;
3978 vl = val_labs_next (val_labs, vl))
3979 if (vl->value.f >= c->range[0] && vl->value.f <= c->range[1])
3980 ctables_add_occurrence (var, &vl->value, occurrences);
3984 for (const struct val_lab *vl = val_labs_first (val_labs); vl;
3985 vl = val_labs_next (val_labs, vl))
3986 if (var_is_value_missing (var, &vl->value))
3987 ctables_add_occurrence (var, &vl->value, occurrences);
3991 for (const struct val_lab *vl = val_labs_first (val_labs); vl;
3992 vl = val_labs_next (val_labs, vl))
3993 ctables_add_occurrence (var, &vl->value, occurrences);
3996 case CCT_POSTCOMPUTE:
4006 for (const struct val_lab *vl = val_labs_first (val_labs); vl;
4007 vl = val_labs_next (val_labs, vl))
4008 if (c->include_missing || !var_is_value_missing (var, &vl->value))
4009 ctables_add_occurrence (var, &vl->value, occurrences);
4016 ctables_section_recurse_add_empty_categories (
4017 struct ctables_section *s,
4018 const struct ctables_category *cats[PIVOT_N_AXES][10], struct ccase *c,
4019 enum pivot_axis_type a, size_t a_idx)
4021 if (a >= PIVOT_N_AXES)
4022 ctables_cell_insert__ (s, c, cats);
4023 else if (!s->nests[a] || a_idx >= s->nests[a]->n)
4024 ctables_section_recurse_add_empty_categories (s, cats, c, a + 1, 0);
4027 const struct variable *var = s->nests[a]->vars[a_idx];
4028 const struct ctables_categories *categories = s->table->categories[
4029 var_get_dict_index (var)];
4030 int width = var_get_width (var);
4031 const struct hmap *occurrences = &s->occurrences[a][a_idx];
4032 const struct ctables_occurrence *o;
4033 HMAP_FOR_EACH (o, struct ctables_occurrence, node, occurrences)
4035 union value *value = case_data_rw (c, var);
4036 value_destroy (value, width);
4037 value_clone (value, &o->value, width);
4038 cats[a][a_idx] = ctables_categories_match (categories, value, var);
4039 assert (cats[a][a_idx] != NULL);
4040 ctables_section_recurse_add_empty_categories (s, cats, c, a, a_idx + 1);
4043 for (size_t i = 0; i < categories->n_cats; i++)
4045 const struct ctables_category *cat = &categories->cats[i];
4046 if (cat->type == CCT_POSTCOMPUTE)
4048 cats[a][a_idx] = cat;
4049 ctables_section_recurse_add_empty_categories (s, cats, c, a, a_idx + 1);
4056 ctables_section_add_empty_categories (struct ctables_section *s)
4058 bool show_empty = false;
4059 for (size_t a = 0; a < PIVOT_N_AXES; a++)
4061 for (size_t k = 0; k < s->nests[a]->n; k++)
4062 if (k != s->nests[a]->scale_idx)
4064 const struct variable *var = s->nests[a]->vars[k];
4065 const struct ctables_categories *cats = s->table->categories[
4066 var_get_dict_index (var)];
4067 if (cats->show_empty)
4070 ctables_add_category_occurrences (var, &s->occurrences[a][k], cats);
4076 const struct ctables_category *cats[PIVOT_N_AXES][10]; /* XXX */
4077 struct ccase *c = case_create (dict_get_proto (s->table->ctables->dict));
4078 ctables_section_recurse_add_empty_categories (s, cats, c, 0, 0);
4083 ctables_execute (struct dataset *ds, struct ctables *ct)
4085 for (size_t i = 0; i < ct->n_tables; i++)
4087 struct ctables_table *t = ct->tables[i];
4088 t->sections = xnmalloc (MAX (1, t->stacks[PIVOT_AXIS_ROW].n) *
4089 MAX (1, t->stacks[PIVOT_AXIS_COLUMN].n) *
4090 MAX (1, t->stacks[PIVOT_AXIS_LAYER].n),
4091 sizeof *t->sections);
4092 size_t ix[PIVOT_N_AXES];
4093 ctables_table_add_section (t, 0, ix);
4096 struct casereader *input = proc_open (ds);
4097 bool warn_on_invalid = true;
4098 for (struct ccase *c = casereader_read (input); c;
4099 case_unref (c), c = casereader_read (input))
4101 double d_weight = dict_get_case_weight (dataset_dict (ds), c,
4103 double e_weight = (ct->e_weight
4104 ? var_force_valid_weight (ct->e_weight,
4105 case_num (c, ct->e_weight),
4109 for (size_t i = 0; i < ct->n_tables; i++)
4111 struct ctables_table *t = ct->tables[i];
4113 for (size_t j = 0; j < t->n_sections; j++)
4114 ctables_cell_insert (&t->sections[j], c, d_weight, e_weight);
4116 for (enum pivot_axis_type a = 0; a < PIVOT_N_AXES; a++)
4117 if (t->label_axis[a] != a)
4118 ctables_insert_clabels_values (t, c, a);
4121 casereader_destroy (input);
4123 for (size_t i = 0; i < ct->n_tables; i++)
4125 struct ctables_table *t = ct->tables[i];
4127 if (t->clabels_example)
4128 ctables_sort_clabels_values (t);
4130 for (size_t j = 0; j < t->n_sections; j++)
4131 ctables_section_add_empty_categories (&t->sections[j]);
4133 ctables_table_output (ct, ct->tables[i]);
4135 return proc_commit (ds);
4140 typedef struct ctables_pcexpr *parse_recursively_func (struct lexer *);
4143 ctables_pcexpr_destroy (struct ctables_pcexpr *e)
4149 case CTPO_CAT_STRING:
4159 for (size_t i = 0; i < 2; i++)
4160 ctables_pcexpr_destroy (e->subs[i]);
4164 case CTPO_CAT_NUMBER:
4165 case CTPO_CAT_RANGE:
4166 case CTPO_CAT_MISSING:
4167 case CTPO_CAT_OTHERNM:
4168 case CTPO_CAT_SUBTOTAL:
4169 case CTPO_CAT_TOTAL:
4173 msg_location_destroy (e->location);
4178 static struct ctables_pcexpr *
4179 ctables_pcexpr_allocate_binary (enum ctables_postcompute_op op,
4180 struct ctables_pcexpr *sub0,
4181 struct ctables_pcexpr *sub1)
4183 struct ctables_pcexpr *e = xmalloc (sizeof *e);
4184 *e = (struct ctables_pcexpr) {
4186 .subs = { sub0, sub1 },
4187 .location = msg_location_merged (sub0->location, sub1->location),
4192 /* How to parse an operator. */
4195 enum token_type token;
4196 enum ctables_postcompute_op op;
4199 static const struct operator *
4200 match_operator (struct lexer *lexer, const struct operator ops[], size_t n_ops)
4202 for (const struct operator *op = ops; op < ops + n_ops; op++)
4203 if (lex_token (lexer) == op->token)
4205 if (op->token != T_NEG_NUM)
4214 static struct ctables_pcexpr *
4215 parse_binary_operators__ (struct lexer *lexer,
4216 const struct operator ops[], size_t n_ops,
4217 parse_recursively_func *parse_next_level,
4218 const char *chain_warning,
4219 struct ctables_pcexpr *lhs)
4221 for (int op_count = 0; ; op_count++)
4223 const struct operator *op = match_operator (lexer, ops, n_ops);
4226 if (op_count > 1 && chain_warning)
4227 msg_at (SW, lhs->location, "%s", chain_warning);
4232 struct ctables_pcexpr *rhs = parse_next_level (lexer);
4235 ctables_pcexpr_destroy (lhs);
4239 lhs = ctables_pcexpr_allocate_binary (op->op, lhs, rhs);
4243 static struct ctables_pcexpr *
4244 parse_binary_operators (struct lexer *lexer,
4245 const struct operator ops[], size_t n_ops,
4246 parse_recursively_func *parse_next_level,
4247 const char *chain_warning)
4249 struct ctables_pcexpr *lhs = parse_next_level (lexer);
4253 return parse_binary_operators__ (lexer, ops, n_ops, parse_next_level,
4254 chain_warning, lhs);
4257 static struct ctables_pcexpr *parse_add (struct lexer *);
4259 static struct ctables_pcexpr
4260 ctpo_cat_range (double low, double high)
4262 return (struct ctables_pcexpr) {
4263 .op = CTPO_CAT_RANGE,
4264 .range = { low, high },
4268 static struct ctables_pcexpr *
4269 parse_primary (struct lexer *lexer)
4271 int start_ofs = lex_ofs (lexer);
4272 struct ctables_pcexpr e;
4273 if (lex_is_number (lexer))
4275 e = (struct ctables_pcexpr) { .op = CTPO_CONSTANT,
4276 .number = lex_number (lexer) };
4279 else if (lex_match_id (lexer, "MISSING"))
4280 e = (struct ctables_pcexpr) { .op = CTPO_CAT_MISSING };
4281 else if (lex_match_id (lexer, "OTHERNM"))
4282 e = (struct ctables_pcexpr) { .op = CTPO_CAT_OTHERNM };
4283 else if (lex_match_id (lexer, "TOTAL"))
4284 e = (struct ctables_pcexpr) { .op = CTPO_CAT_TOTAL };
4285 else if (lex_match_id (lexer, "SUBTOTAL"))
4287 size_t subtotal_index = 0;
4288 if (lex_match (lexer, T_LBRACK))
4290 if (!lex_force_int_range (lexer, "SUBTOTAL", 1, LONG_MAX))
4292 subtotal_index = lex_integer (lexer);
4294 if (!lex_force_match (lexer, T_RBRACK))
4297 e = (struct ctables_pcexpr) { .op = CTPO_CAT_SUBTOTAL,
4298 .subtotal_index = subtotal_index };
4300 else if (lex_match (lexer, T_LBRACK))
4302 if (lex_match_id (lexer, "LO"))
4304 if (!lex_force_match_id (lexer, "THRU") || lex_force_num (lexer))
4306 e = ctpo_cat_range (-DBL_MAX, lex_number (lexer));
4309 else if (lex_is_number (lexer))
4311 double number = lex_number (lexer);
4313 if (lex_match_id (lexer, "THRU"))
4315 if (lex_match_id (lexer, "HI"))
4316 e = ctpo_cat_range (number, DBL_MAX);
4319 if (!lex_force_num (lexer))
4321 e = ctpo_cat_range (number, lex_number (lexer));
4326 e = (struct ctables_pcexpr) { .op = CTPO_CAT_NUMBER,
4329 else if (lex_is_string (lexer))
4331 e = (struct ctables_pcexpr) {
4332 .op = CTPO_CAT_STRING,
4333 .string = ss_xstrdup (lex_tokss (lexer)),
4339 lex_error (lexer, NULL);
4343 if (!lex_force_match (lexer, T_RBRACK))
4345 if (e.op == CTPO_CAT_STRING)
4350 else if (lex_match (lexer, T_LPAREN))
4352 struct ctables_pcexpr *ep = parse_add (lexer);
4355 if (!lex_force_match (lexer, T_RPAREN))
4357 ctables_pcexpr_destroy (ep);
4364 lex_error (lexer, NULL);
4368 e.location = lex_ofs_location (lexer, start_ofs, lex_ofs (lexer) - 1);
4369 return xmemdup (&e, sizeof e);
4372 static struct ctables_pcexpr *
4373 ctables_pcexpr_allocate_neg (struct ctables_pcexpr *sub,
4374 struct lexer *lexer, int start_ofs)
4376 struct ctables_pcexpr *e = xmalloc (sizeof *e);
4377 *e = (struct ctables_pcexpr) {
4380 .location = lex_ofs_location (lexer, start_ofs, lex_ofs (lexer) - 1),
4385 static struct ctables_pcexpr *
4386 parse_exp (struct lexer *lexer)
4388 static const struct operator op = { T_EXP, CTPO_POW };
4390 const char *chain_warning =
4391 _("The exponentiation operator (`**') is left-associative: "
4392 "`a**b**c' equals `(a**b)**c', not `a**(b**c)'. "
4393 "To disable this warning, insert parentheses.");
4395 if (lex_token (lexer) != T_NEG_NUM || lex_next_token (lexer, 1) != T_EXP)
4396 return parse_binary_operators (lexer, &op, 1,
4397 parse_primary, chain_warning);
4399 /* Special case for situations like "-5**6", which must be parsed as
4402 int start_ofs = lex_ofs (lexer);
4403 struct ctables_pcexpr *lhs = xmalloc (sizeof *lhs);
4404 *lhs = (struct ctables_pcexpr) {
4405 .op = CTPO_CONSTANT,
4406 .number = -lex_tokval (lexer),
4407 .location = lex_ofs_location (lexer, start_ofs, lex_ofs (lexer)),
4411 struct ctables_pcexpr *node = parse_binary_operators__ (
4412 lexer, &op, 1, parse_primary, chain_warning, lhs);
4416 return ctables_pcexpr_allocate_neg (node, lexer, start_ofs);
4419 /* Parses the unary minus level. */
4420 static struct ctables_pcexpr *
4421 parse_neg (struct lexer *lexer)
4423 int start_ofs = lex_ofs (lexer);
4424 if (!lex_match (lexer, T_DASH))
4425 return parse_exp (lexer);
4427 struct ctables_pcexpr *inner = parse_neg (lexer);
4431 return ctables_pcexpr_allocate_neg (inner, lexer, start_ofs);
4434 /* Parses the multiplication and division level. */
4435 static struct ctables_pcexpr *
4436 parse_mul (struct lexer *lexer)
4438 static const struct operator ops[] =
4440 { T_ASTERISK, CTPO_MUL },
4441 { T_SLASH, CTPO_DIV },
4444 return parse_binary_operators (lexer, ops, sizeof ops / sizeof *ops,
4448 /* Parses the addition and subtraction level. */
4449 static struct ctables_pcexpr *
4450 parse_add (struct lexer *lexer)
4452 static const struct operator ops[] =
4454 { T_PLUS, CTPO_ADD },
4455 { T_DASH, CTPO_SUB },
4456 { T_NEG_NUM, CTPO_ADD },
4459 return parse_binary_operators (lexer, ops, sizeof ops / sizeof *ops,
4463 static struct ctables_postcompute *
4464 ctables_find_postcompute (struct ctables *ct, const char *name)
4466 struct ctables_postcompute *pc;
4467 HMAP_FOR_EACH_WITH_HASH (pc, struct ctables_postcompute, hmap_node,
4468 utf8_hash_case_string (name, 0), &ct->postcomputes)
4469 if (!utf8_strcasecmp (pc->name, name))
4475 ctables_parse_pcompute (struct lexer *lexer, struct ctables *ct)
4477 int pcompute_start = lex_ofs (lexer) - 1;
4479 if (!lex_force_match (lexer, T_AND) || !lex_force_id (lexer))
4482 char *name = ss_xstrdup (lex_tokss (lexer));
4485 if (!lex_force_match (lexer, T_EQUALS)
4486 || !lex_force_match_id (lexer, "EXPR")
4487 || !lex_force_match (lexer, T_LPAREN))
4493 int expr_start = lex_ofs (lexer);
4494 struct ctables_pcexpr *expr = parse_add (lexer);
4495 int expr_end = lex_ofs (lexer) - 1;
4496 if (!expr || !lex_force_match (lexer, T_RPAREN))
4501 int pcompute_end = lex_ofs (lexer) - 1;
4503 struct msg_location *location = lex_ofs_location (lexer, pcompute_start,
4506 struct ctables_postcompute *pc = ctables_find_postcompute (ct, name);
4509 msg_at (SW, location, _("New definition of &%s will override the "
4510 "previous definition."),
4512 msg_at (SN, pc->location, _("This is the previous definition."));
4514 ctables_pcexpr_destroy (pc->expr);
4515 msg_location_destroy (pc->location);
4520 pc = xmalloc (sizeof *pc);
4521 *pc = (struct ctables_postcompute) { .name = name };
4522 hmap_insert (&ct->postcomputes, &pc->hmap_node,
4523 utf8_hash_case_string (pc->name, 0));
4526 pc->location = location;
4528 pc->label = lex_ofs_representation (lexer, expr_start, expr_end);
4533 ctables_parse_pproperties_format (struct lexer *lexer,
4534 struct ctables_summary_spec_set *sss)
4536 *sss = (struct ctables_summary_spec_set) { .n = 0 };
4538 while (lex_token (lexer) != T_ENDCMD && lex_token (lexer) != T_SLASH
4539 && !(lex_token (lexer) == T_ID
4540 && (lex_id_match (ss_cstr ("LABEL"), lex_tokss (lexer))
4541 || lex_id_match (ss_cstr ("HIDESOURCECATS"),
4542 lex_tokss (lexer)))))
4544 /* Parse function. */
4545 enum ctables_summary_function function;
4546 if (!parse_ctables_summary_function (lexer, &function))
4549 /* Parse percentile. */
4550 double percentile = 0;
4551 if (function == CTSF_PTILE)
4553 if (!lex_force_num_range_closed (lexer, "PTILE", 0, 100))
4555 percentile = lex_number (lexer);
4560 struct fmt_spec format;
4561 if (!parse_format_specifier (lexer, &format)
4562 || !fmt_check_output (&format)
4563 || !fmt_check_type_compat (&format, VAL_NUMERIC))
4566 if (sss->n >= sss->allocated)
4567 sss->specs = x2nrealloc (sss->specs, &sss->allocated,
4568 sizeof *sss->specs);
4569 sss->specs[sss->n++] = (struct ctables_summary_spec) {
4570 .function = function,
4571 .percentile = percentile,
4578 ctables_summary_spec_set_uninit (sss);
4583 ctables_parse_pproperties (struct lexer *lexer, struct ctables *ct)
4585 struct ctables_postcompute **pcs = NULL;
4587 size_t allocated_pcs = 0;
4589 while (lex_match (lexer, T_AND))
4591 if (!lex_force_id (lexer))
4593 struct ctables_postcompute *pc
4594 = ctables_find_postcompute (ct, lex_tokcstr (lexer));
4597 msg (SE, _("Unknown computed category &%s."), lex_tokcstr (lexer));
4602 if (n_pcs >= allocated_pcs)
4603 pcs = x2nrealloc (pcs, &allocated_pcs, sizeof *pcs);
4607 while (lex_token (lexer) != T_SLASH && lex_token (lexer) != T_ENDCMD)
4609 if (lex_match_id (lexer, "LABEL"))
4611 lex_match (lexer, T_EQUALS);
4612 if (!lex_force_string (lexer))
4615 for (size_t i = 0; i < n_pcs; i++)
4617 free (pcs[i]->label);
4618 pcs[i]->label = ss_xstrdup (lex_tokss (lexer));
4623 else if (lex_match_id (lexer, "FORMAT"))
4625 lex_match (lexer, T_EQUALS);
4627 struct ctables_summary_spec_set sss;
4628 if (!ctables_parse_pproperties_format (lexer, &sss))
4631 for (size_t i = 0; i < n_pcs; i++)
4634 ctables_summary_spec_set_uninit (pcs[i]->specs);
4636 pcs[i]->specs = xmalloc (sizeof *pcs[i]->specs);
4637 ctables_summary_spec_set_clone (pcs[i]->specs, &sss);
4639 ctables_summary_spec_set_uninit (&sss);
4641 else if (lex_match_id (lexer, "HIDESOURCECATS"))
4643 lex_match (lexer, T_EQUALS);
4644 bool hide_source_cats;
4645 if (!parse_bool (lexer, &hide_source_cats))
4647 for (size_t i = 0; i < n_pcs; i++)
4648 pcs[i]->hide_source_cats = hide_source_cats;
4652 lex_error_expecting (lexer, "LABEL", "FORMAT", "HIDESOURCECATS");
4665 cmd_ctables (struct lexer *lexer, struct dataset *ds)
4667 size_t n_vars = dict_get_n_vars (dataset_dict (ds));
4668 enum ctables_vlabel *vlabels = xnmalloc (n_vars, sizeof *vlabels);
4669 enum settings_value_show tvars = settings_get_show_variables ();
4670 for (size_t i = 0; i < n_vars; i++)
4671 vlabels[i] = (enum ctables_vlabel) tvars;
4673 struct pivot_table_look *look = pivot_table_look_unshare (
4674 pivot_table_look_ref (pivot_table_look_get_default ()));
4675 look->omit_empty = false;
4677 struct ctables *ct = xmalloc (sizeof *ct);
4678 *ct = (struct ctables) {
4679 .dict = dataset_dict (ds),
4681 .ctables_formats = FMT_SETTINGS_INIT,
4683 .postcomputes = HMAP_INITIALIZER (ct->postcomputes),
4689 const char *dot_string;
4690 const char *comma_string;
4692 static const struct ctf ctfs[4] = {
4693 { CTEF_NEGPAREN, "(,,,)", "(...)" },
4694 { CTEF_NEQUAL, "-,N=,,", "-.N=.." },
4695 { CTEF_PAREN, "-,(,),", "-.(.)." },
4696 { CTEF_PCTPAREN, "-,(,%),", "-.(.%)." },
4698 bool is_dot = settings_get_fmt_settings ()->decimal == '.';
4699 for (size_t i = 0; i < 4; i++)
4701 const char *s = is_dot ? ctfs[i].dot_string : ctfs[i].comma_string;
4702 fmt_settings_set_cc (&ct->ctables_formats, ctfs[i].type,
4703 fmt_number_style_from_string (s));
4706 if (!lex_force_match (lexer, T_SLASH))
4709 while (!lex_match_id (lexer, "TABLE"))
4711 if (lex_match_id (lexer, "FORMAT"))
4713 double widths[2] = { SYSMIS, SYSMIS };
4714 double units_per_inch = 72.0;
4716 while (lex_token (lexer) != T_SLASH)
4718 if (lex_match_id (lexer, "MINCOLWIDTH"))
4720 if (!parse_col_width (lexer, "MINCOLWIDTH", &widths[0]))
4723 else if (lex_match_id (lexer, "MAXCOLWIDTH"))
4725 if (!parse_col_width (lexer, "MAXCOLWIDTH", &widths[1]))
4728 else if (lex_match_id (lexer, "UNITS"))
4730 lex_match (lexer, T_EQUALS);
4731 if (lex_match_id (lexer, "POINTS"))
4732 units_per_inch = 72.0;
4733 else if (lex_match_id (lexer, "INCHES"))
4734 units_per_inch = 1.0;
4735 else if (lex_match_id (lexer, "CM"))
4736 units_per_inch = 2.54;
4739 lex_error_expecting (lexer, "POINTS", "INCHES", "CM");
4743 else if (lex_match_id (lexer, "EMPTY"))
4748 lex_match (lexer, T_EQUALS);
4749 if (lex_match_id (lexer, "ZERO"))
4751 /* Nothing to do. */
4753 else if (lex_match_id (lexer, "BLANK"))
4754 ct->zero = xstrdup ("");
4755 else if (lex_force_string (lexer))
4757 ct->zero = ss_xstrdup (lex_tokss (lexer));
4763 else if (lex_match_id (lexer, "MISSING"))
4765 lex_match (lexer, T_EQUALS);
4766 if (!lex_force_string (lexer))
4770 ct->missing = (strcmp (lex_tokcstr (lexer), ".")
4771 ? ss_xstrdup (lex_tokss (lexer))
4777 lex_error_expecting (lexer, "MINCOLWIDTH", "MAXCOLWIDTH",
4778 "UNITS", "EMPTY", "MISSING");
4783 if (widths[0] != SYSMIS && widths[1] != SYSMIS
4784 && widths[0] > widths[1])
4786 msg (SE, _("MINCOLWIDTH must not be greater than MAXCOLWIDTH."));
4790 for (size_t i = 0; i < 2; i++)
4791 if (widths[i] != SYSMIS)
4793 int *wr = ct->look->width_ranges[TABLE_HORZ];
4794 wr[i] = widths[i] / units_per_inch * 96.0;
4799 else if (lex_match_id (lexer, "VLABELS"))
4801 if (!lex_force_match_id (lexer, "VARIABLES"))
4803 lex_match (lexer, T_EQUALS);
4805 struct variable **vars;
4807 if (!parse_variables (lexer, dataset_dict (ds), &vars, &n_vars,
4811 if (!lex_force_match_id (lexer, "DISPLAY"))
4816 lex_match (lexer, T_EQUALS);
4818 enum ctables_vlabel vlabel;
4819 if (lex_match_id (lexer, "DEFAULT"))
4820 vlabel = (enum ctables_vlabel) settings_get_show_variables ();
4821 else if (lex_match_id (lexer, "NAME"))
4823 else if (lex_match_id (lexer, "LABEL"))
4824 vlabel = CTVL_LABEL;
4825 else if (lex_match_id (lexer, "BOTH"))
4827 else if (lex_match_id (lexer, "NONE"))
4831 lex_error_expecting (lexer, "DEFAULT", "NAME", "LABEL",
4837 for (size_t i = 0; i < n_vars; i++)
4838 ct->vlabels[var_get_dict_index (vars[i])] = vlabel;
4841 else if (lex_match_id (lexer, "MRSETS"))
4843 if (!lex_force_match_id (lexer, "COUNTDUPLICATES"))
4845 lex_match (lexer, T_EQUALS);
4846 if (!parse_bool (lexer, &ct->mrsets_count_duplicates))
4849 else if (lex_match_id (lexer, "SMISSING"))
4851 if (lex_match_id (lexer, "VARIABLE"))
4852 ct->smissing_listwise = false;
4853 else if (lex_match_id (lexer, "LISTWISE"))
4854 ct->smissing_listwise = true;
4857 lex_error_expecting (lexer, "VARIABLE", "LISTWISE");
4861 else if (lex_match_id (lexer, "PCOMPUTE"))
4863 if (!ctables_parse_pcompute (lexer, ct))
4866 else if (lex_match_id (lexer, "PPROPERTIES"))
4868 if (!ctables_parse_pproperties (lexer, ct))
4871 else if (lex_match_id (lexer, "WEIGHT"))
4873 if (!lex_force_match_id (lexer, "VARIABLE"))
4875 lex_match (lexer, T_EQUALS);
4876 ct->e_weight = parse_variable (lexer, dataset_dict (ds));
4880 else if (lex_match_id (lexer, " HIDESMALLCOUNTS"))
4882 if (lex_match_id (lexer, "COUNT"))
4884 lex_match (lexer, T_EQUALS);
4885 if (!lex_force_int_range (lexer, "HIDESMALLCOUNTS COUNT",
4888 ct->hide_threshold = lex_integer (lexer);
4891 else if (ct->hide_threshold == 0)
4892 ct->hide_threshold = 5;
4896 lex_error_expecting (lexer, "FORMAT", "VLABELS", "MRSETS",
4897 "SMISSING", "PCOMPUTE", "PPROPERTIES",
4898 "WEIGHT", "HIDESMALLCOUNTS", "TABLE");
4902 if (!lex_force_match (lexer, T_SLASH))
4906 size_t allocated_tables = 0;
4909 if (ct->n_tables >= allocated_tables)
4910 ct->tables = x2nrealloc (ct->tables, &allocated_tables,
4911 sizeof *ct->tables);
4913 struct ctables_category *cat = xmalloc (sizeof *cat);
4914 *cat = (struct ctables_category) {
4916 .include_missing = false,
4917 .sort_ascending = true,
4920 struct ctables_categories *c = xmalloc (sizeof *c);
4921 size_t n_vars = dict_get_n_vars (dataset_dict (ds));
4922 *c = (struct ctables_categories) {
4929 struct ctables_categories **categories = xnmalloc (n_vars,
4930 sizeof *categories);
4931 for (size_t i = 0; i < n_vars; i++)
4934 struct ctables_table *t = xmalloc (sizeof *t);
4935 *t = (struct ctables_table) {
4937 .slabels_axis = PIVOT_AXIS_COLUMN,
4938 .slabels_visible = true,
4939 .clabels_values_map = HMAP_INITIALIZER (t->clabels_values_map),
4941 [PIVOT_AXIS_ROW] = PIVOT_AXIS_ROW,
4942 [PIVOT_AXIS_COLUMN] = PIVOT_AXIS_COLUMN,
4943 [PIVOT_AXIS_LAYER] = PIVOT_AXIS_LAYER,
4945 .clabels_from_axis = PIVOT_AXIS_LAYER,
4946 .categories = categories,
4947 .n_categories = n_vars,
4950 ct->tables[ct->n_tables++] = t;
4952 lex_match (lexer, T_EQUALS);
4953 if (!ctables_axis_parse (lexer, dataset_dict (ds), ct, t, PIVOT_AXIS_ROW))
4955 if (lex_match (lexer, T_BY))
4957 if (!ctables_axis_parse (lexer, dataset_dict (ds),
4958 ct, t, PIVOT_AXIS_COLUMN))
4961 if (lex_match (lexer, T_BY))
4963 if (!ctables_axis_parse (lexer, dataset_dict (ds),
4964 ct, t, PIVOT_AXIS_LAYER))
4969 if (!t->axes[PIVOT_AXIS_ROW] && !t->axes[PIVOT_AXIS_COLUMN]
4970 && !t->axes[PIVOT_AXIS_LAYER])
4972 lex_error (lexer, _("At least one variable must be specified."));
4976 const struct ctables_axis *scales[PIVOT_N_AXES];
4977 size_t n_scales = 0;
4978 for (enum pivot_axis_type a = 0; a < PIVOT_N_AXES; a++)
4980 scales[a] = find_scale (t->axes[a]);
4986 msg (SE, _("Scale variables may appear only on one axis."));
4987 if (scales[PIVOT_AXIS_ROW])
4988 msg_at (SN, scales[PIVOT_AXIS_ROW]->loc,
4989 _("This scale variable appears on the rows axis."));
4990 if (scales[PIVOT_AXIS_COLUMN])
4991 msg_at (SN, scales[PIVOT_AXIS_COLUMN]->loc,
4992 _("This scale variable appears on the columns axis."));
4993 if (scales[PIVOT_AXIS_LAYER])
4994 msg_at (SN, scales[PIVOT_AXIS_LAYER]->loc,
4995 _("This scale variable appears on the layer axis."));
4999 const struct ctables_axis *summaries[PIVOT_N_AXES];
5000 size_t n_summaries = 0;
5001 for (enum pivot_axis_type a = 0; a < PIVOT_N_AXES; a++)
5003 summaries[a] = (scales[a]
5005 : find_categorical_summary_spec (t->axes[a]));
5009 if (n_summaries > 1)
5011 msg (SE, _("Summaries may appear only on one axis."));
5012 if (summaries[PIVOT_AXIS_ROW])
5013 msg_at (SN, summaries[PIVOT_AXIS_ROW]->loc,
5014 _("This variable on the rows axis has a summary."));
5015 if (summaries[PIVOT_AXIS_COLUMN])
5016 msg_at (SN, summaries[PIVOT_AXIS_COLUMN]->loc,
5017 _("This variable on the columns axis has a summary."));
5018 if (summaries[PIVOT_AXIS_LAYER])
5019 msg_at (SN, summaries[PIVOT_AXIS_LAYER]->loc,
5020 _("This variable on the layers axis has a summary."));
5023 for (enum pivot_axis_type a = 0; a < PIVOT_N_AXES; a++)
5024 if (n_summaries ? summaries[a] : t->axes[a])
5026 t->summary_axis = a;
5030 if (lex_token (lexer) == T_ENDCMD)
5032 if (!ctables_prepare_table (t))
5036 if (!lex_force_match (lexer, T_SLASH))
5039 while (!lex_match_id (lexer, "TABLE") && lex_token (lexer) != T_ENDCMD)
5041 if (lex_match_id (lexer, "SLABELS"))
5043 while (lex_token (lexer) != T_SLASH && lex_token (lexer) != T_ENDCMD)
5045 if (lex_match_id (lexer, "POSITION"))
5047 lex_match (lexer, T_EQUALS);
5048 if (lex_match_id (lexer, "COLUMN"))
5049 t->slabels_axis = PIVOT_AXIS_COLUMN;
5050 else if (lex_match_id (lexer, "ROW"))
5051 t->slabels_axis = PIVOT_AXIS_ROW;
5052 else if (lex_match_id (lexer, "LAYER"))
5053 t->slabels_axis = PIVOT_AXIS_LAYER;
5056 lex_error_expecting (lexer, "COLUMN", "ROW", "LAYER");
5060 else if (lex_match_id (lexer, "VISIBLE"))
5062 lex_match (lexer, T_EQUALS);
5063 if (!parse_bool (lexer, &t->slabels_visible))
5068 lex_error_expecting (lexer, "POSITION", "VISIBLE");
5073 else if (lex_match_id (lexer, "CLABELS"))
5075 while (lex_token (lexer) != T_SLASH && lex_token (lexer) != T_ENDCMD)
5077 if (lex_match_id (lexer, "AUTO"))
5079 t->label_axis[PIVOT_AXIS_ROW] = PIVOT_AXIS_ROW;
5080 t->label_axis[PIVOT_AXIS_COLUMN] = PIVOT_AXIS_COLUMN;
5082 else if (lex_match_id (lexer, "ROWLABELS"))
5084 lex_match (lexer, T_EQUALS);
5085 if (lex_match_id (lexer, "OPPOSITE"))
5086 t->label_axis[PIVOT_AXIS_ROW] = PIVOT_AXIS_COLUMN;
5087 else if (lex_match_id (lexer, "LAYER"))
5088 t->label_axis[PIVOT_AXIS_ROW] = PIVOT_AXIS_LAYER;
5091 lex_error_expecting (lexer, "OPPOSITE", "LAYER");
5095 else if (lex_match_id (lexer, "COLLABELS"))
5097 lex_match (lexer, T_EQUALS);
5098 if (lex_match_id (lexer, "OPPOSITE"))
5099 t->label_axis[PIVOT_AXIS_COLUMN] = PIVOT_AXIS_ROW;
5100 else if (lex_match_id (lexer, "LAYER"))
5101 t->label_axis[PIVOT_AXIS_COLUMN] = PIVOT_AXIS_LAYER;
5104 lex_error_expecting (lexer, "OPPOSITE", "LAYER");
5110 lex_error_expecting (lexer, "AUTO", "ROWLABELS",
5116 else if (lex_match_id (lexer, "CRITERIA"))
5118 if (!lex_force_match_id (lexer, "CILEVEL"))
5120 lex_match (lexer, T_EQUALS);
5122 if (!lex_force_num_range_halfopen (lexer, "CILEVEL", 0, 100))
5124 t->cilevel = lex_number (lexer);
5127 else if (lex_match_id (lexer, "CATEGORIES"))
5129 if (!ctables_table_parse_categories (lexer, dataset_dict (ds),
5133 else if (lex_match_id (lexer, "TITLES"))
5138 if (lex_match_id (lexer, "CAPTION"))
5139 textp = &t->caption;
5140 else if (lex_match_id (lexer, "CORNER"))
5142 else if (lex_match_id (lexer, "TITLE"))
5146 lex_error_expecting (lexer, "CAPTION", "CORNER", "TITLE");
5149 lex_match (lexer, T_EQUALS);
5151 struct string s = DS_EMPTY_INITIALIZER;
5152 while (lex_is_string (lexer))
5154 if (!ds_is_empty (&s))
5155 ds_put_byte (&s, ' ');
5156 ds_put_substring (&s, lex_tokss (lexer));
5160 *textp = ds_steal_cstr (&s);
5162 while (lex_token (lexer) != T_SLASH
5163 && lex_token (lexer) != T_ENDCMD);
5165 else if (lex_match_id (lexer, "SIGTEST"))
5169 t->chisq = xmalloc (sizeof *t->chisq);
5170 *t->chisq = (struct ctables_chisq) {
5172 .include_mrsets = true,
5173 .all_visible = true,
5179 if (lex_match_id (lexer, "TYPE"))
5181 lex_match (lexer, T_EQUALS);
5182 if (!lex_force_match_id (lexer, "CHISQUARE"))
5185 else if (lex_match_id (lexer, "ALPHA"))
5187 lex_match (lexer, T_EQUALS);
5188 if (!lex_force_num_range_halfopen (lexer, "ALPHA", 0, 1))
5190 t->chisq->alpha = lex_number (lexer);
5193 else if (lex_match_id (lexer, "INCLUDEMRSETS"))
5195 lex_match (lexer, T_EQUALS);
5196 if (parse_bool (lexer, &t->chisq->include_mrsets))
5199 else if (lex_match_id (lexer, "CATEGORIES"))
5201 lex_match (lexer, T_EQUALS);
5202 if (lex_match_id (lexer, "ALLVISIBLE"))
5203 t->chisq->all_visible = true;
5204 else if (lex_match_id (lexer, "SUBTOTALS"))
5205 t->chisq->all_visible = false;
5208 lex_error_expecting (lexer,
5209 "ALLVISIBLE", "SUBTOTALS");
5215 lex_error_expecting (lexer, "TYPE", "ALPHA",
5216 "INCLUDEMRSETS", "CATEGORIES");
5220 while (lex_token (lexer) != T_SLASH
5221 && lex_token (lexer) != T_ENDCMD);
5223 else if (lex_match_id (lexer, "COMPARETEST"))
5227 t->pairwise = xmalloc (sizeof *t->pairwise);
5228 *t->pairwise = (struct ctables_pairwise) {
5230 .alpha = { .05, .05 },
5231 .adjust = BONFERRONI,
5232 .include_mrsets = true,
5233 .meansvariance_allcats = true,
5234 .all_visible = true,
5243 if (lex_match_id (lexer, "TYPE"))
5245 lex_match (lexer, T_EQUALS);
5246 if (lex_match_id (lexer, "PROP"))
5247 t->pairwise->type = PROP;
5248 else if (lex_match_id (lexer, "MEAN"))
5249 t->pairwise->type = MEAN;
5252 lex_error_expecting (lexer, "PROP", "MEAN");
5256 else if (lex_match_id (lexer, "ALPHA"))
5258 lex_match (lexer, T_EQUALS);
5260 if (!lex_force_num_range_open (lexer, "ALPHA", 0, 1))
5262 double a0 = lex_number (lexer);
5265 lex_match (lexer, T_COMMA);
5266 if (lex_is_number (lexer))
5268 if (!lex_force_num_range_open (lexer, "ALPHA", 0, 1))
5270 double a1 = lex_number (lexer);
5273 t->pairwise->alpha[0] = MIN (a0, a1);
5274 t->pairwise->alpha[1] = MAX (a0, a1);
5277 t->pairwise->alpha[0] = t->pairwise->alpha[1] = a0;
5279 else if (lex_match_id (lexer, "ADJUST"))
5281 lex_match (lexer, T_EQUALS);
5282 if (lex_match_id (lexer, "BONFERRONI"))
5283 t->pairwise->adjust = BONFERRONI;
5284 else if (lex_match_id (lexer, "BH"))
5285 t->pairwise->adjust = BH;
5286 else if (lex_match_id (lexer, "NONE"))
5287 t->pairwise->adjust = 0;
5290 lex_error_expecting (lexer, "BONFERRONI", "BH",
5295 else if (lex_match_id (lexer, "INCLUDEMRSETS"))
5297 lex_match (lexer, T_EQUALS);
5298 if (!parse_bool (lexer, &t->pairwise->include_mrsets))
5301 else if (lex_match_id (lexer, "MEANSVARIANCE"))
5303 lex_match (lexer, T_EQUALS);
5304 if (lex_match_id (lexer, "ALLCATS"))
5305 t->pairwise->meansvariance_allcats = true;
5306 else if (lex_match_id (lexer, "TESTEDCATS"))
5307 t->pairwise->meansvariance_allcats = false;
5310 lex_error_expecting (lexer, "ALLCATS", "TESTEDCATS");
5314 else if (lex_match_id (lexer, "CATEGORIES"))
5316 lex_match (lexer, T_EQUALS);
5317 if (lex_match_id (lexer, "ALLVISIBLE"))
5318 t->pairwise->all_visible = true;
5319 else if (lex_match_id (lexer, "SUBTOTALS"))
5320 t->pairwise->all_visible = false;
5323 lex_error_expecting (lexer, "ALLVISIBLE",
5328 else if (lex_match_id (lexer, "MERGE"))
5330 lex_match (lexer, T_EQUALS);
5331 if (!parse_bool (lexer, &t->pairwise->merge))
5334 else if (lex_match_id (lexer, "STYLE"))
5336 lex_match (lexer, T_EQUALS);
5337 if (lex_match_id (lexer, "APA"))
5338 t->pairwise->apa_style = true;
5339 else if (lex_match_id (lexer, "SIMPLE"))
5340 t->pairwise->apa_style = false;
5343 lex_error_expecting (lexer, "APA", "SIMPLE");
5347 else if (lex_match_id (lexer, "SHOWSIG"))
5349 lex_match (lexer, T_EQUALS);
5350 if (!parse_bool (lexer, &t->pairwise->show_sig))
5355 lex_error_expecting (lexer, "TYPE", "ALPHA", "ADJUST",
5356 "INCLUDEMRSETS", "MEANSVARIANCE",
5357 "CATEGORIES", "MERGE", "STYLE",
5362 while (lex_token (lexer) != T_SLASH
5363 && lex_token (lexer) != T_ENDCMD);
5367 lex_error_expecting (lexer, "TABLE", "SLABELS", "CLABELS",
5368 "CRITERIA", "CATEGORIES", "TITLES",
5369 "SIGTEST", "COMPARETEST");
5373 if (!lex_match (lexer, T_SLASH))
5377 if (t->label_axis[PIVOT_AXIS_ROW] != PIVOT_AXIS_ROW
5378 && t->label_axis[PIVOT_AXIS_COLUMN] != PIVOT_AXIS_COLUMN)
5380 msg (SE, _("ROWLABELS and COLLABELS may not both be specified."));
5384 if (!ctables_prepare_table (t))
5387 while (lex_token (lexer) != T_ENDCMD);
5389 bool ok = ctables_execute (ds, ct);
5390 ctables_destroy (ct);
5391 return ok ? CMD_SUCCESS : CMD_FAILURE;
5394 ctables_destroy (ct);