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 *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) {
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].var)
1980 else if (!b->specs[CSV_CELL].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].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 *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)
2257 if (var_is_value_missing (var, value))
2258 s->missing += d_weight;
2260 s->valid += d_weight;
2264 case CTSF_ROWPCT_COUNT:
2265 case CTSF_COLPCT_COUNT:
2266 case CTSF_TABLEPCT_COUNT:
2267 case CTSF_SUBTABLEPCT_COUNT:
2268 case CTSF_LAYERPCT_COUNT:
2269 case CTSF_LAYERROWPCT_COUNT:
2270 case CTSF_LAYERCOLPCT_COUNT:
2271 case CTSF_ROWPCT_VALIDN:
2272 case CTSF_COLPCT_VALIDN:
2273 case CTSF_TABLEPCT_VALIDN:
2274 case CTSF_SUBTABLEPCT_VALIDN:
2275 case CTSF_LAYERPCT_VALIDN:
2276 case CTSF_LAYERROWPCT_VALIDN:
2277 case CTSF_LAYERCOLPCT_VALIDN:
2278 case CTSF_ROWPCT_TOTALN:
2279 case CTSF_COLPCT_TOTALN:
2280 case CTSF_TABLEPCT_TOTALN:
2281 case CTSF_SUBTABLEPCT_TOTALN:
2282 case CTSF_LAYERPCT_TOTALN:
2283 case CTSF_LAYERROWPCT_TOTALN:
2284 case CTSF_LAYERCOLPCT_TOTALN:
2288 if (var_is_value_missing (var, value))
2289 s->missing += e_weight;
2291 s->valid += e_weight;
2297 if (!var_is_value_missing (var, value))
2299 assert (!var_is_alpha (var)); /* XXX? */
2300 if (s->min == SYSMIS || value->f < s->min)
2302 if (s->max == SYSMIS || value->f > s->max)
2312 case CTSF_ROWPCT_SUM:
2313 case CTSF_COLPCT_SUM:
2314 case CTSF_TABLEPCT_SUM:
2315 case CTSF_SUBTABLEPCT_SUM:
2316 case CTSF_LAYERPCT_SUM:
2317 case CTSF_LAYERROWPCT_SUM:
2318 case CTSF_LAYERCOLPCT_SUM:
2319 if (!var_is_value_missing (var, value))
2320 moments1_add (s->moments, value->f, e_weight);
2326 if (var_is_value_missing (var, value))
2328 s->ovalid += e_weight;
2330 struct ccase *c = case_create (casewriter_get_proto (s->writer));
2331 *case_num_rw_idx (c, 0) = value->f;
2332 *case_num_rw_idx (c, 1) = e_weight;
2333 casewriter_write (s->writer, c);
2339 static enum ctables_domain_type
2340 ctables_function_domain (enum ctables_summary_function function)
2364 case CTSF_COLPCT_COUNT:
2365 case CTSF_COLPCT_SUM:
2366 case CTSF_COLPCT_TOTALN:
2367 case CTSF_COLPCT_VALIDN:
2370 case CTSF_LAYERCOLPCT_COUNT:
2371 case CTSF_LAYERCOLPCT_SUM:
2372 case CTSF_LAYERCOLPCT_TOTALN:
2373 case CTSF_LAYERCOLPCT_VALIDN:
2374 return CTDT_LAYERCOL;
2376 case CTSF_LAYERPCT_COUNT:
2377 case CTSF_LAYERPCT_SUM:
2378 case CTSF_LAYERPCT_TOTALN:
2379 case CTSF_LAYERPCT_VALIDN:
2382 case CTSF_LAYERROWPCT_COUNT:
2383 case CTSF_LAYERROWPCT_SUM:
2384 case CTSF_LAYERROWPCT_TOTALN:
2385 case CTSF_LAYERROWPCT_VALIDN:
2386 return CTDT_LAYERROW;
2388 case CTSF_ROWPCT_COUNT:
2389 case CTSF_ROWPCT_SUM:
2390 case CTSF_ROWPCT_TOTALN:
2391 case CTSF_ROWPCT_VALIDN:
2394 case CTSF_SUBTABLEPCT_COUNT:
2395 case CTSF_SUBTABLEPCT_SUM:
2396 case CTSF_SUBTABLEPCT_TOTALN:
2397 case CTSF_SUBTABLEPCT_VALIDN:
2398 return CTDT_SUBTABLE;
2400 case CTSF_TABLEPCT_COUNT:
2401 case CTSF_TABLEPCT_SUM:
2402 case CTSF_TABLEPCT_TOTALN:
2403 case CTSF_TABLEPCT_VALIDN:
2411 ctables_summary_value (const struct ctables_cell *cell,
2412 union ctables_summary *s,
2413 const struct ctables_summary_spec *ss)
2415 switch (ss->function)
2421 case CTSF_ROWPCT_COUNT:
2422 case CTSF_COLPCT_COUNT:
2423 case CTSF_TABLEPCT_COUNT:
2424 case CTSF_SUBTABLEPCT_COUNT:
2425 case CTSF_LAYERPCT_COUNT:
2426 case CTSF_LAYERROWPCT_COUNT:
2427 case CTSF_LAYERCOLPCT_COUNT:
2429 enum ctables_domain_type d = ctables_function_domain (ss->function);
2430 return (cell->domains[d]->e_valid
2431 ? s->valid / cell->domains[d]->e_valid * 100
2435 case CTSF_ROWPCT_VALIDN:
2436 case CTSF_COLPCT_VALIDN:
2437 case CTSF_TABLEPCT_VALIDN:
2438 case CTSF_SUBTABLEPCT_VALIDN:
2439 case CTSF_LAYERPCT_VALIDN:
2440 case CTSF_LAYERROWPCT_VALIDN:
2441 case CTSF_LAYERCOLPCT_VALIDN:
2442 case CTSF_ROWPCT_TOTALN:
2443 case CTSF_COLPCT_TOTALN:
2444 case CTSF_TABLEPCT_TOTALN:
2445 case CTSF_SUBTABLEPCT_TOTALN:
2446 case CTSF_LAYERPCT_TOTALN:
2447 case CTSF_LAYERROWPCT_TOTALN:
2448 case CTSF_LAYERCOLPCT_TOTALN:
2456 return s->valid + s->missing;
2469 return s->max != SYSMIS && s->min != SYSMIS ? s->max - s->min : SYSMIS;
2474 moments1_calculate (s->moments, NULL, &mean, NULL, NULL, NULL);
2480 double weight, variance;
2481 moments1_calculate (s->moments, &weight, NULL, &variance, NULL, NULL);
2482 return calc_semean (variance, weight);
2488 moments1_calculate (s->moments, NULL, NULL, &variance, NULL, NULL);
2489 return variance != SYSMIS ? sqrt (variance) : SYSMIS;
2494 double weight, mean;
2495 moments1_calculate (s->moments, &weight, &mean, NULL, NULL, NULL);
2496 return weight != SYSMIS && mean != SYSMIS ? weight * mean : SYSMIS;
2502 moments1_calculate (s->moments, NULL, NULL, &variance, NULL, NULL);
2506 case CTSF_ROWPCT_SUM:
2507 case CTSF_COLPCT_SUM:
2508 case CTSF_TABLEPCT_SUM:
2509 case CTSF_SUBTABLEPCT_SUM:
2510 case CTSF_LAYERPCT_SUM:
2511 case CTSF_LAYERROWPCT_SUM:
2512 case CTSF_LAYERCOLPCT_SUM:
2519 struct casereader *reader = casewriter_make_reader (s->writer);
2522 struct percentile *ptile = percentile_create (
2523 ss->function == CTSF_PTILE ? ss->percentile : 0.5, s->ovalid);
2524 struct order_stats *os = &ptile->parent;
2525 order_stats_accumulate_idx (&os, 1, reader, 1, 0);
2526 s->ovalue = percentile_calculate (ptile, PC_HAVERAGE);
2527 statistic_destroy (&ptile->parent.parent);
2534 struct casereader *reader = casewriter_make_reader (s->writer);
2537 struct mode *mode = mode_create ();
2538 struct order_stats *os = &mode->parent;
2539 order_stats_accumulate_idx (&os, 1, reader, 1, 0);
2540 s->ovalue = mode->mode;
2541 statistic_destroy (&mode->parent.parent);
2549 struct ctables_cell_sort_aux
2551 const struct ctables_nest *nest;
2552 enum pivot_axis_type a;
2556 ctables_cell_compare_3way (const void *a_, const void *b_, const void *aux_)
2558 const struct ctables_cell_sort_aux *aux = aux_;
2559 struct ctables_cell *const *ap = a_;
2560 struct ctables_cell *const *bp = b_;
2561 const struct ctables_cell *a = *ap;
2562 const struct ctables_cell *b = *bp;
2564 const struct ctables_nest *nest = aux->nest;
2565 for (size_t i = 0; i < nest->n; i++)
2566 if (i != nest->scale_idx)
2568 const struct variable *var = nest->vars[i];
2569 const struct ctables_cell_value *a_cv = &a->axes[aux->a].cvs[i];
2570 const struct ctables_cell_value *b_cv = &b->axes[aux->a].cvs[i];
2571 if (a_cv->category != b_cv->category)
2572 return a_cv->category > b_cv->category ? 1 : -1;
2574 const union value *a_val = &a_cv->value;
2575 const union value *b_val = &b_cv->value;
2576 switch (a_cv->category->type)
2582 case CCT_POSTCOMPUTE:
2583 /* Must be equal. */
2590 int cmp = value_compare_3way (a_val, b_val, var_get_width (var));
2598 int cmp = value_compare_3way (a_val, b_val, var_get_width (var));
2600 return a_cv->category->sort_ascending ? cmp : -cmp;
2606 const char *a_label = var_lookup_value_label (var, a_val);
2607 const char *b_label = var_lookup_value_label (var, b_val);
2609 ? (b_label ? strcmp (a_label, b_label) : 1)
2610 : (b_label ? -1 : value_compare_3way (
2611 a_val, b_val, var_get_width (var))));
2613 return a_cv->category->sort_ascending ? cmp : -cmp;
2627 For each ctables_table:
2628 For each combination of row vars:
2629 For each combination of column vars:
2630 For each combination of layer vars:
2632 Make a table of row values:
2633 Sort entries by row values
2634 Assign a 0-based index to each actual value
2635 Construct a dimension
2636 Make a table of column values
2637 Make a table of layer values
2639 Fill the table entry using the indexes from before.
2642 static struct ctables_domain *
2643 ctables_domain_insert (struct ctables_section *s, struct ctables_cell *cell,
2644 enum ctables_domain_type domain)
2647 for (enum pivot_axis_type a = 0; a < PIVOT_N_AXES; a++)
2649 const struct ctables_nest *nest = s->nests[a];
2650 for (size_t i = 0; i < nest->n_domains[domain]; i++)
2652 size_t v_idx = nest->domains[domain][i];
2653 hash = value_hash (&cell->axes[a].cvs[v_idx].value,
2654 var_get_width (nest->vars[v_idx]), hash);
2658 struct ctables_domain *d;
2659 HMAP_FOR_EACH_WITH_HASH (d, struct ctables_domain, node, hash, &s->domains[domain])
2661 const struct ctables_cell *df = d->example;
2662 for (enum pivot_axis_type a = 0; a < PIVOT_N_AXES; a++)
2664 const struct ctables_nest *nest = s->nests[a];
2665 for (size_t i = 0; i < nest->n_domains[domain]; i++)
2667 size_t v_idx = nest->domains[domain][i];
2668 if (!value_equal (&df->axes[a].cvs[v_idx].value,
2669 &cell->axes[a].cvs[v_idx].value,
2670 var_get_width (nest->vars[v_idx])))
2679 d = xmalloc (sizeof *d);
2680 *d = (struct ctables_domain) { .example = cell };
2681 hmap_insert (&s->domains[domain], &d->node, hash);
2685 static const struct ctables_category *
2686 ctables_categories_match (const struct ctables_categories *c,
2687 const union value *v, const struct variable *var)
2689 const struct ctables_category *othernm = NULL;
2690 for (size_t i = c->n_cats; i-- > 0; )
2692 const struct ctables_category *cat = &c->cats[i];
2696 if (cat->number == v->f)
2704 if ((cat->range[0] == -DBL_MAX || v->f >= cat->range[0])
2705 && (cat->range[1] == DBL_MAX || v->f <= cat->range[1]))
2710 if (var_is_value_missing (var, v))
2714 case CCT_POSTCOMPUTE:
2729 return (cat->include_missing || !var_is_value_missing (var, v) ? cat
2734 return var_is_value_missing (var, v) ? NULL : othernm;
2737 static const struct ctables_category *
2738 ctables_categories_total (const struct ctables_categories *c)
2740 const struct ctables_category *first = &c->cats[0];
2741 const struct ctables_category *last = &c->cats[c->n_cats - 1];
2742 return (first->type == CCT_TOTAL ? first
2743 : last->type == CCT_TOTAL ? last
2747 static struct ctables_cell *
2748 ctables_cell_insert__ (struct ctables_section *s, const struct ccase *c,
2749 const struct ctables_category *cats[PIVOT_N_AXES][10])
2752 enum ctables_summary_variant sv = CSV_CELL;
2753 for (enum pivot_axis_type a = 0; a < PIVOT_N_AXES; a++)
2755 const struct ctables_nest *nest = s->nests[a];
2756 for (size_t i = 0; i < nest->n; i++)
2757 if (i != nest->scale_idx)
2759 hash = hash_pointer (cats[a][i], hash);
2760 if (cats[a][i]->type != CCT_TOTAL
2761 && cats[a][i]->type != CCT_SUBTOTAL
2762 && cats[a][i]->type != CCT_POSTCOMPUTE)
2763 hash = value_hash (case_data (c, nest->vars[i]),
2764 var_get_width (nest->vars[i]), hash);
2770 struct ctables_cell *cell;
2771 HMAP_FOR_EACH_WITH_HASH (cell, struct ctables_cell, node, hash, &s->cells)
2773 for (enum pivot_axis_type a = 0; a < PIVOT_N_AXES; a++)
2775 const struct ctables_nest *nest = s->nests[a];
2776 for (size_t i = 0; i < nest->n; i++)
2777 if (i != nest->scale_idx
2778 && (cats[a][i] != cell->axes[a].cvs[i].category
2779 || (cats[a][i]->type != CCT_TOTAL
2780 && cats[a][i]->type != CCT_SUBTOTAL
2781 && cats[a][i]->type != CCT_POSTCOMPUTE
2782 && !value_equal (case_data (c, nest->vars[i]),
2783 &cell->axes[a].cvs[i].value,
2784 var_get_width (nest->vars[i])))))
2793 cell = xmalloc (sizeof *cell);
2796 cell->contributes_to_domains = true;
2797 cell->postcompute = false;
2798 for (enum pivot_axis_type a = 0; a < PIVOT_N_AXES; a++)
2800 const struct ctables_nest *nest = s->nests[a];
2801 cell->axes[a].cvs = (nest->n
2802 ? xnmalloc (nest->n, sizeof *cell->axes[a].cvs)
2804 for (size_t i = 0; i < nest->n; i++)
2806 const struct ctables_category *cat = cats[a][i];
2807 if (i != nest->scale_idx)
2809 const struct ctables_category *subtotal = cat->subtotal;
2810 if (cat->hide || (subtotal && subtotal->hide_subcategories))
2813 if (cat->type == CCT_TOTAL
2814 || cat->type == CCT_SUBTOTAL
2815 || cat->type == CCT_POSTCOMPUTE)
2816 cell->contributes_to_domains = false;
2817 if (cat->type == CCT_POSTCOMPUTE)
2818 cell->postcompute = true;
2821 cell->axes[a].cvs[i].category = cat;
2822 value_clone (&cell->axes[a].cvs[i].value, case_data (c, nest->vars[i]),
2823 var_get_width (nest->vars[i]));
2827 const struct ctables_nest *ss = s->nests[s->table->summary_axis];
2828 const struct ctables_summary_spec_set *specs = &ss->specs[cell->sv];
2829 cell->summaries = xmalloc (specs->n * sizeof *cell->summaries);
2830 for (size_t i = 0; i < specs->n; i++)
2831 ctables_summary_init (&cell->summaries[i], &specs->specs[i]);
2832 for (enum ctables_domain_type dt = 0; dt < N_CTDTS; dt++)
2833 cell->domains[dt] = ctables_domain_insert (s, cell, dt);
2834 hmap_insert (&s->cells, &cell->node, hash);
2839 ctables_cell_add__ (struct ctables_section *s, const struct ccase *c,
2840 const struct ctables_category *cats[PIVOT_N_AXES][10],
2841 double d_weight, double e_weight)
2843 struct ctables_cell *cell = ctables_cell_insert__ (s, c, cats);
2844 const struct ctables_nest *ss = s->nests[s->table->summary_axis];
2846 const struct ctables_summary_spec_set *specs = &ss->specs[cell->sv];
2847 for (size_t i = 0; i < specs->n; i++)
2848 ctables_summary_add (&cell->summaries[i], &specs->specs[i], specs->var,
2849 case_data (c, specs->var), d_weight, e_weight);
2850 if (cell->contributes_to_domains)
2852 for (enum ctables_domain_type dt = 0; dt < N_CTDTS; dt++)
2854 cell->domains[dt]->d_valid += d_weight;
2855 cell->domains[dt]->e_valid += e_weight;
2861 recurse_totals (struct ctables_section *s, const struct ccase *c,
2862 const struct ctables_category *cats[PIVOT_N_AXES][10],
2863 double d_weight, double e_weight,
2864 enum pivot_axis_type start_axis, size_t start_nest)
2866 for (enum pivot_axis_type a = start_axis; a < PIVOT_N_AXES; a++)
2868 const struct ctables_nest *nest = s->nests[a];
2869 for (size_t i = start_nest; i < nest->n; i++)
2871 if (i == nest->scale_idx)
2874 const struct variable *var = nest->vars[i];
2876 const struct ctables_category *total = ctables_categories_total (
2877 s->table->categories[var_get_dict_index (var)]);
2880 const struct ctables_category *save = cats[a][i];
2882 ctables_cell_add__ (s, c, cats, d_weight, e_weight);
2883 recurse_totals (s, c, cats, d_weight, e_weight, a, i + 1);
2892 recurse_subtotals (struct ctables_section *s, const struct ccase *c,
2893 const struct ctables_category *cats[PIVOT_N_AXES][10],
2894 double d_weight, double e_weight,
2895 enum pivot_axis_type start_axis, size_t start_nest)
2897 for (enum pivot_axis_type a = start_axis; a < PIVOT_N_AXES; a++)
2899 const struct ctables_nest *nest = s->nests[a];
2900 for (size_t i = start_nest; i < nest->n; i++)
2902 if (i == nest->scale_idx)
2905 const struct ctables_category *save = cats[a][i];
2908 cats[a][i] = save->subtotal;
2909 ctables_cell_add__ (s, c, cats, d_weight, e_weight);
2910 recurse_subtotals (s, c, cats, d_weight, e_weight, a, i + 1);
2919 ctables_add_occurrence (const struct variable *var,
2920 const union value *value,
2921 struct hmap *occurrences)
2923 int width = var_get_width (var);
2924 unsigned int hash = value_hash (value, width, 0);
2926 struct ctables_occurrence *o;
2927 HMAP_FOR_EACH_WITH_HASH (o, struct ctables_occurrence, node, hash,
2929 if (value_equal (value, &o->value, width))
2932 o = xmalloc (sizeof *o);
2933 value_clone (&o->value, value, width);
2934 hmap_insert (occurrences, &o->node, hash);
2938 ctables_cell_insert (struct ctables_section *s,
2939 const struct ccase *c,
2940 double d_weight, double e_weight)
2942 const struct ctables_category *cats[PIVOT_N_AXES][10]; /* XXX */
2943 for (enum pivot_axis_type a = 0; a < PIVOT_N_AXES; a++)
2945 const struct ctables_nest *nest = s->nests[a];
2946 for (size_t i = 0; i < nest->n; i++)
2948 if (i == nest->scale_idx)
2951 const struct variable *var = nest->vars[i];
2952 const union value *value = case_data (c, var);
2954 if (var_is_numeric (var) && value->f == SYSMIS)
2957 cats[a][i] = ctables_categories_match (
2958 s->table->categories[var_get_dict_index (var)], value, var);
2964 for (enum pivot_axis_type a = 0; a < PIVOT_N_AXES; a++)
2966 const struct ctables_nest *nest = s->nests[a];
2967 for (size_t i = 0; i < nest->n; i++)
2968 if (i != nest->scale_idx)
2970 const struct variable *var = nest->vars[i];
2971 const union value *value = case_data (c, var);
2972 ctables_add_occurrence (var, value, &s->occurrences[a][i]);
2976 ctables_cell_add__ (s, c, cats, d_weight, e_weight);
2978 recurse_totals (s, c, cats, d_weight, e_weight, 0, 0);
2979 recurse_subtotals (s, c, cats, d_weight, e_weight, 0, 0);
2984 const struct ctables_summary_spec_set *set;
2989 merge_item_compare_3way (const struct merge_item *a, const struct merge_item *b)
2991 const struct ctables_summary_spec *as = &a->set->specs[a->ofs];
2992 const struct ctables_summary_spec *bs = &b->set->specs[b->ofs];
2993 if (as->function != bs->function)
2994 return as->function > bs->function ? 1 : -1;
2995 else if (as->percentile != bs->percentile)
2996 return as->percentile < bs->percentile ? 1 : -1;
2997 return strcmp (as->label, bs->label);
3000 static struct pivot_value *
3001 ctables_category_create_label (const struct ctables_category *cat,
3002 const struct variable *var,
3003 const union value *value)
3005 return (cat->type == CCT_TOTAL || cat->type == CCT_SUBTOTAL
3006 ? pivot_value_new_user_text (cat->total_label, SIZE_MAX)
3007 : cat->type == CCT_POSTCOMPUTE && cat->pc->label
3008 ? pivot_value_new_user_text (cat->pc->label, SIZE_MAX)
3009 : pivot_value_new_var_value (var, value));
3012 static struct ctables_value *
3013 ctables_value_find__ (struct ctables_table *t, const union value *value,
3014 int width, unsigned int hash)
3016 struct ctables_value *clv;
3017 HMAP_FOR_EACH_WITH_HASH (clv, struct ctables_value, node,
3018 hash, &t->clabels_values_map)
3019 if (value_equal (value, &clv->value, width))
3025 ctables_value_insert (struct ctables_table *t, const union value *value,
3028 unsigned int hash = value_hash (value, width, 0);
3029 struct ctables_value *clv = ctables_value_find__ (t, value, width, hash);
3032 clv = xmalloc (sizeof *clv);
3033 value_clone (&clv->value, value, width);
3034 hmap_insert (&t->clabels_values_map, &clv->node, hash);
3038 static struct ctables_value *
3039 ctables_value_find (struct ctables_table *t,
3040 const union value *value, int width)
3042 return ctables_value_find__ (t, value, width,
3043 value_hash (value, width, 0));
3047 ctables_table_add_section (struct ctables_table *t, enum pivot_axis_type a,
3048 size_t ix[PIVOT_N_AXES])
3050 if (a < PIVOT_N_AXES)
3052 size_t limit = MAX (t->stacks[a].n, 1);
3053 for (ix[a] = 0; ix[a] < limit; ix[a]++)
3054 ctables_table_add_section (t, a + 1, ix);
3058 struct ctables_section *s = &t->sections[t->n_sections++];
3059 *s = (struct ctables_section) {
3061 .cells = HMAP_INITIALIZER (s->cells),
3063 for (a = 0; a < PIVOT_N_AXES; a++)
3066 struct ctables_nest *nest = &t->stacks[a].nests[ix[a]];
3068 s->occurrences[a] = xnmalloc (nest->n, sizeof *s->occurrences[a]);
3069 for (size_t i = 0; i < nest->n; i++)
3070 hmap_init (&s->occurrences[a][i]);
3072 for (size_t i = 0; i < N_CTDTS; i++)
3073 hmap_init (&s->domains[i]);
3078 ctpo_add (double a, double b)
3084 ctpo_sub (double a, double b)
3090 ctpo_mul (double a, double b)
3096 ctpo_div (double a, double b)
3098 return b ? a / b : SYSMIS;
3102 ctpo_pow (double a, double b)
3104 int save_errno = errno;
3106 double result = pow (a, b);
3114 ctpo_neg (double a, double b UNUSED)
3119 struct ctables_pcexpr_evaluate_ctx
3121 const struct ctables_cell *cell;
3122 const struct ctables_section *section;
3123 const struct ctables_categories *cats;
3124 enum pivot_axis_type pc_a;
3128 static double ctables_pcexpr_evaluate (
3129 const struct ctables_pcexpr_evaluate_ctx *, const struct ctables_pcexpr *);
3132 ctables_pcexpr_evaluate_nonterminal (
3133 const struct ctables_pcexpr_evaluate_ctx *ctx,
3134 const struct ctables_pcexpr *e, size_t n_args,
3135 double evaluate (double, double))
3137 double args[2] = { 0, 0 };
3138 for (size_t i = 0; i < n_args; i++)
3140 args[i] = ctables_pcexpr_evaluate (ctx, e->subs[i]);
3141 if (!isfinite (args[i]) || args[i] == SYSMIS)
3144 return evaluate (args[0], args[1]);
3148 ctables_pcexpr_evaluate_category (const struct ctables_pcexpr_evaluate_ctx *ctx,
3149 const struct ctables_cell_value *pc_cv)
3151 const struct ctables_section *s = ctx->section;
3154 for (enum pivot_axis_type a = 0; a < PIVOT_N_AXES; a++)
3156 const struct ctables_nest *nest = s->nests[a];
3157 for (size_t i = 0; i < nest->n; i++)
3158 if (i != nest->scale_idx)
3160 const struct ctables_cell_value *cv
3161 = (a == ctx->pc_a && i == ctx->pc_a_idx ? pc_cv
3162 : &ctx->cell->axes[a].cvs[i]);
3163 hash = hash_pointer (cv->category, hash);
3164 if (cv->category->type != CCT_TOTAL
3165 && cv->category->type != CCT_SUBTOTAL
3166 && cv->category->type != CCT_POSTCOMPUTE)
3167 hash = value_hash (&cv->value,
3168 var_get_width (nest->vars[i]), hash);
3172 struct ctables_cell *tc;
3173 HMAP_FOR_EACH_WITH_HASH (tc, struct ctables_cell, node, hash, &s->cells)
3175 for (enum pivot_axis_type a = 0; a < PIVOT_N_AXES; a++)
3177 const struct ctables_nest *nest = s->nests[a];
3178 for (size_t i = 0; i < nest->n; i++)
3179 if (i != nest->scale_idx)
3181 const struct ctables_cell_value *p_cv
3182 = (a == ctx->pc_a && i == ctx->pc_a_idx ? pc_cv
3183 : &ctx->cell->axes[a].cvs[i]);
3184 const struct ctables_cell_value *t_cv = &tc->axes[a].cvs[i];
3185 if (p_cv->category != t_cv->category
3186 || (p_cv->category->type != CCT_TOTAL
3187 && p_cv->category->type != CCT_SUBTOTAL
3188 && p_cv->category->type != CCT_POSTCOMPUTE
3189 && !value_equal (&p_cv->value,
3191 var_get_width (nest->vars[i]))))
3203 const struct ctables_table *t = s->table;
3204 const struct ctables_nest *specs_nest = s->nests[t->summary_axis];
3205 const struct ctables_summary_spec_set *specs = &specs_nest->specs[tc->sv];
3206 size_t j = 0 /* XXX */;
3207 return ctables_summary_value (tc, &tc->summaries[j], &specs->specs[j]);
3211 ctables_pcexpr_evaluate (const struct ctables_pcexpr_evaluate_ctx *ctx,
3212 const struct ctables_pcexpr *e)
3219 case CTPO_CAT_RANGE:
3221 struct ctables_cell_value cv = {
3222 .category = ctables_find_category_for_postcompute (ctx->cats, e)
3224 assert (cv.category != NULL);
3226 struct hmap *occurrences = &ctx->section->occurrences[ctx->pc_a][ctx->pc_a_idx];
3227 const struct ctables_occurrence *o;
3230 const struct variable *var = ctx->section->nests[ctx->pc_a]->vars[ctx->pc_a_idx];
3231 HMAP_FOR_EACH (o, struct ctables_occurrence, node, occurrences)
3232 if (ctables_categories_match (ctx->cats, &o->value, var) == cv.category)
3234 cv.value = o->value;
3235 sum += ctables_pcexpr_evaluate_category (ctx, &cv);
3240 case CTPO_CAT_NUMBER:
3241 case CTPO_CAT_STRING:
3242 case CTPO_CAT_MISSING:
3243 case CTPO_CAT_OTHERNM:
3244 case CTPO_CAT_SUBTOTAL:
3245 case CTPO_CAT_TOTAL:
3247 struct ctables_cell_value cv = {
3248 .category = ctables_find_category_for_postcompute (ctx->cats, e),
3249 .value = { .f = e->number },
3251 assert (cv.category != NULL);
3252 return ctables_pcexpr_evaluate_category (ctx, &cv);
3256 return ctables_pcexpr_evaluate_nonterminal (ctx, e, 2, ctpo_add);
3259 return ctables_pcexpr_evaluate_nonterminal (ctx, e, 2, ctpo_sub);
3262 return ctables_pcexpr_evaluate_nonterminal (ctx, e, 2, ctpo_mul);
3265 return ctables_pcexpr_evaluate_nonterminal (ctx, e, 2, ctpo_div);
3268 return ctables_pcexpr_evaluate_nonterminal (ctx, e, 2, ctpo_pow);
3271 return ctables_pcexpr_evaluate_nonterminal (ctx, e, 1, ctpo_neg);
3278 ctables_cell_calculate_postcompute (const struct ctables_section *s,
3279 const struct ctables_cell *cell)
3281 enum pivot_axis_type pc_a;
3283 const struct ctables_postcompute *pc;
3284 for (pc_a = 0; ; pc_a++)
3286 assert (pc_a < PIVOT_N_AXES);
3287 for (pc_a_idx = 0; pc_a_idx < s->nests[pc_a]->n; pc_a_idx++)
3289 const struct ctables_cell_value *cv = &cell->axes[pc_a].cvs[pc_a_idx];
3290 if (cv->category->type == CCT_POSTCOMPUTE)
3292 pc = cv->category->pc;
3299 const struct variable *var = s->nests[pc_a]->vars[pc_a_idx];
3300 const struct ctables_categories *cats = s->table->categories[
3301 var_get_dict_index (var)];
3302 struct ctables_pcexpr_evaluate_ctx ctx = {
3307 .pc_a_idx = pc_a_idx,
3309 return ctables_pcexpr_evaluate (&ctx, pc->expr);
3313 ctables_table_output (struct ctables *ct, struct ctables_table *t)
3315 struct pivot_table *pt = pivot_table_create__ (
3317 ? pivot_value_new_user_text (t->title, SIZE_MAX)
3318 : pivot_value_new_text (N_("Custom Tables"))),
3321 pivot_table_set_caption (
3322 pt, pivot_value_new_user_text (t->caption, SIZE_MAX));
3324 pivot_table_set_caption (
3325 pt, pivot_value_new_user_text (t->corner, SIZE_MAX));
3327 bool summary_dimension = (t->summary_axis != t->slabels_axis
3328 || (!t->slabels_visible
3329 && t->summary_specs.n > 1));
3330 if (summary_dimension)
3332 struct pivot_dimension *d = pivot_dimension_create (
3333 pt, t->slabels_axis, N_("Statistics"));
3334 const struct ctables_summary_spec_set *specs = &t->summary_specs;
3335 if (!t->slabels_visible)
3336 d->hide_all_labels = true;
3337 for (size_t i = 0; i < specs->n; i++)
3338 pivot_category_create_leaf (
3339 d->root, pivot_value_new_text (specs->specs[i].label));
3342 bool categories_dimension = t->clabels_example != NULL;
3343 if (categories_dimension)
3345 struct pivot_dimension *d = pivot_dimension_create (
3346 pt, t->label_axis[t->clabels_from_axis],
3347 t->clabels_from_axis == PIVOT_AXIS_ROW
3348 ? N_("Row Categories")
3349 : N_("Column Categories"));
3350 const struct variable *var = t->clabels_example;
3351 const struct ctables_categories *c = t->categories[var_get_dict_index (var)];
3352 for (size_t i = 0; i < t->n_clabels_values; i++)
3354 const struct ctables_value *value = t->clabels_values[i];
3355 const struct ctables_category *cat = ctables_categories_match (c, &value->value, var);
3356 assert (cat != NULL);
3357 pivot_category_create_leaf (d->root, ctables_category_create_label (
3358 cat, t->clabels_example, &value->value));
3362 pivot_table_set_look (pt, ct->look);
3363 struct pivot_dimension *d[PIVOT_N_AXES];
3364 for (enum pivot_axis_type a = 0; a < PIVOT_N_AXES; a++)
3366 static const char *names[] = {
3367 [PIVOT_AXIS_ROW] = N_("Rows"),
3368 [PIVOT_AXIS_COLUMN] = N_("Columns"),
3369 [PIVOT_AXIS_LAYER] = N_("Layers"),
3371 d[a] = (t->axes[a] || a == t->summary_axis
3372 ? pivot_dimension_create (pt, a, names[a])
3377 assert (t->axes[a]);
3379 for (size_t i = 0; i < t->stacks[a].n; i++)
3381 struct ctables_nest *nest = &t->stacks[a].nests[i];
3382 struct ctables_section **sections = xnmalloc (t->n_sections,
3384 size_t n_sections = 0;
3386 size_t n_total_cells = 0;
3387 size_t max_depth = 0;
3388 for (size_t j = 0; j < t->n_sections; j++)
3389 if (t->sections[j].nests[a] == nest)
3391 struct ctables_section *s = &t->sections[j];
3392 sections[n_sections++] = s;
3393 n_total_cells += s->cells.count;
3395 size_t depth = s->nests[a]->n;
3396 max_depth = MAX (depth, max_depth);
3399 struct ctables_cell **sorted = xnmalloc (n_total_cells,
3401 size_t n_sorted = 0;
3403 for (size_t j = 0; j < n_sections; j++)
3405 struct ctables_section *s = sections[j];
3407 struct ctables_cell *cell;
3408 HMAP_FOR_EACH (cell, struct ctables_cell, node, &s->cells)
3410 sorted[n_sorted++] = cell;
3411 assert (n_sorted <= n_total_cells);
3414 struct ctables_cell_sort_aux aux = { .nest = nest, .a = a };
3415 sort (sorted, n_sorted, sizeof *sorted, ctables_cell_compare_3way, &aux);
3417 struct ctables_level
3419 enum ctables_level_type
3421 CTL_VAR, /* Variable label for nest->vars[var_idx]. */
3422 CTL_CATEGORY, /* Category for nest->vars[var_idx]. */
3423 CTL_SUMMARY, /* Summary functions. */
3427 enum settings_value_show vlabel; /* CTL_VAR only. */
3430 struct ctables_level *levels = xnmalloc (1 + 2 * max_depth, sizeof *levels);
3431 size_t n_levels = 0;
3432 for (size_t k = 0; k < nest->n; k++)
3434 enum ctables_vlabel vlabel = ct->vlabels[var_get_dict_index (nest->vars[k])];
3435 if (vlabel != CTVL_NONE)
3437 levels[n_levels++] = (struct ctables_level) {
3439 .vlabel = (enum settings_value_show) vlabel,
3444 if (nest->scale_idx != k
3445 && (k != nest->n - 1 || t->label_axis[a] == a))
3447 levels[n_levels++] = (struct ctables_level) {
3448 .type = CTL_CATEGORY,
3454 if (!summary_dimension && a == t->slabels_axis)
3456 levels[n_levels++] = (struct ctables_level) {
3457 .type = CTL_SUMMARY,
3458 .var_idx = SIZE_MAX,
3462 /* Pivot categories:
3464 - variable label for nest->vars[0], if vlabel != CTVL_NONE
3465 - category for nest->vars[0], if nest->scale_idx != 0
3466 - variable label for nest->vars[1], if vlabel != CTVL_NONE
3467 - category for nest->vars[1], if nest->scale_idx != 1
3469 - variable label for nest->vars[n - 1], if vlabel != CTVL_NONE
3470 - category for nest->vars[n - 1], if t->label_axis[a] == a && nest->scale_idx != n - 1.
3471 - summary function, if 'a == t->slabels_axis && a ==
3474 Additional dimensions:
3476 - If 'a == t->slabels_axis && a != t->summary_axis', add a summary
3478 - If 't->label_axis[b] == a' for some 'b != a', add a category
3483 struct pivot_category **groups = xnmalloc (1 + 2 * max_depth, sizeof *groups);
3485 for (size_t j = 0; j < n_sorted; j++)
3487 struct ctables_cell *cell = sorted[j];
3488 struct ctables_cell *prev = j > 0 ? sorted[j - 1] : NULL;
3490 size_t n_common = 0;
3493 for (; n_common < n_levels; n_common++)
3495 const struct ctables_level *level = &levels[n_common];
3496 if (level->type == CTL_CATEGORY)
3498 size_t var_idx = level->var_idx;
3499 const struct ctables_category *c = cell->axes[a].cvs[var_idx].category;
3500 if (prev->axes[a].cvs[var_idx].category != c)
3502 else if (c->type != CCT_SUBTOTAL
3503 && c->type != CCT_TOTAL
3504 && c->type != CCT_POSTCOMPUTE
3505 && !value_equal (&prev->axes[a].cvs[var_idx].value,
3506 &cell->axes[a].cvs[var_idx].value,
3507 var_get_type (nest->vars[var_idx])))
3513 for (size_t k = n_common; k < n_levels; k++)
3515 const struct ctables_level *level = &levels[k];
3516 struct pivot_category *parent = k ? groups[k - 1] : d[a]->root;
3517 if (level->type == CTL_SUMMARY)
3519 assert (k == n_levels - 1);
3521 const struct ctables_summary_spec_set *specs = &t->summary_specs;
3522 for (size_t m = 0; m < specs->n; m++)
3524 int leaf = pivot_category_create_leaf (
3525 parent, pivot_value_new_text (specs->specs[m].label));
3532 const struct variable *var = nest->vars[level->var_idx];
3533 struct pivot_value *label;
3534 if (level->type == CTL_VAR)
3536 label = pivot_value_new_variable (var);
3537 label->variable.show = level->vlabel;
3539 else if (level->type == CTL_CATEGORY)
3541 const struct ctables_cell_value *cv = &cell->axes[a].cvs[level->var_idx];
3542 label = ctables_category_create_label (cv->category,
3548 if (k == n_levels - 1)
3549 prev_leaf = pivot_category_create_leaf (parent, label);
3551 groups[k] = pivot_category_create_group__ (parent, label);
3555 cell->axes[a].leaf = prev_leaf;
3562 for (size_t i = 0; i < t->n_sections; i++)
3564 struct ctables_section *s = &t->sections[i];
3566 struct ctables_cell *cell;
3567 HMAP_FOR_EACH (cell, struct ctables_cell, node, &s->cells)
3572 const struct ctables_nest *specs_nest = s->nests[t->summary_axis];
3573 const struct ctables_summary_spec_set *specs = &specs_nest->specs[cell->sv];
3574 for (size_t j = 0; j < specs->n; j++)
3577 size_t n_dindexes = 0;
3579 if (summary_dimension)
3580 dindexes[n_dindexes++] = specs->specs[j].axis_idx;
3582 if (categories_dimension)
3584 const struct ctables_nest *clabels_nest = s->nests[t->clabels_from_axis];
3585 const struct variable *var = clabels_nest->vars[clabels_nest->n - 1];
3586 const union value *value = &cell->axes[t->clabels_from_axis].cvs[clabels_nest->n - 1].value;
3587 const struct ctables_value *ctv = ctables_value_find (t, value, var_get_width (var));
3590 dindexes[n_dindexes++] = ctv->leaf;
3593 for (enum pivot_axis_type a = 0; a < PIVOT_N_AXES; a++)
3596 int leaf = cell->axes[a].leaf;
3597 if (a == t->summary_axis && !summary_dimension)
3599 dindexes[n_dindexes++] = leaf;
3602 const struct ctables_summary_spec *ss = &specs->specs[j];
3604 double d = (cell->postcompute
3605 ? ctables_cell_calculate_postcompute (s, cell)
3606 : ctables_summary_value (cell, &cell->summaries[j], ss));
3607 struct pivot_value *value;
3608 if (ct->hide_threshold != 0
3609 && d < ct->hide_threshold
3610 && (cell->postcompute
3612 : ctables_summary_function_is_count (ss->function)))
3614 value = pivot_value_new_user_text_nocopy (
3615 xasprintf ("<%d", ct->hide_threshold));
3617 else if (d == 0 && ct->zero)
3618 value = pivot_value_new_user_text (ct->zero, SIZE_MAX);
3619 else if (d == SYSMIS && ct->missing)
3620 value = pivot_value_new_user_text (ct->missing, SIZE_MAX);
3621 else if (specs->specs[j].is_ctables_format)
3623 char *s = data_out_stretchy (&(union value) { .f = d },
3625 &specs->specs[j].format,
3626 &ct->ctables_formats, NULL);
3627 value = pivot_value_new_user_text_nocopy (s);
3631 value = pivot_value_new_number (d);
3632 value->numeric.format = specs->specs[j].format;
3634 pivot_table_put (pt, dindexes, n_dindexes, value);
3639 pivot_table_submit (pt);
3643 ctables_check_label_position (struct ctables_table *t, enum pivot_axis_type a)
3645 enum pivot_axis_type label_pos = t->label_axis[a];
3649 t->clabels_from_axis = a;
3651 const char *subcommand_name = a == PIVOT_AXIS_ROW ? "ROWLABELS" : "COLLABELS";
3652 const char *pos_name = label_pos == PIVOT_AXIS_LAYER ? "LAYER" : "OPPOSITE";
3654 const struct ctables_stack *stack = &t->stacks[a];
3658 const struct ctables_nest *n0 = &stack->nests[0];
3660 const struct variable *v0 = n0->vars[n0->n - 1];
3661 struct ctables_categories *c0 = t->categories[var_get_dict_index (v0)];
3662 t->clabels_example = v0;
3664 for (size_t i = 0; i < c0->n_cats; i++)
3665 if (c0->cats[i].type == CCT_FUNCTION)
3667 msg (SE, _("%s=%s is not allowed with sorting based "
3668 "on a summary function."),
3669 subcommand_name, pos_name);
3672 if (n0->n - 1 == n0->scale_idx)
3674 msg (SE, _("%s=%s requires the variables to be moved to be categorical, "
3675 "but %s is a scale variable."),
3676 subcommand_name, pos_name, var_get_name (v0));
3680 for (size_t i = 1; i < stack->n; i++)
3682 const struct ctables_nest *ni = &stack->nests[i];
3684 const struct variable *vi = ni->vars[ni->n - 1];
3685 struct ctables_categories *ci = t->categories[var_get_dict_index (vi)];
3687 if (ni->n - 1 == ni->scale_idx)
3689 msg (SE, _("%s=%s requires the variables to be moved to be "
3690 "categorical, but %s is a scale variable."),
3691 subcommand_name, pos_name, var_get_name (vi));
3694 if (var_get_width (v0) != var_get_width (vi))
3696 msg (SE, _("%s=%s requires the variables to be "
3697 "moved to have the same width, but %s has "
3698 "width %d and %s has width %d."),
3699 subcommand_name, pos_name,
3700 var_get_name (v0), var_get_width (v0),
3701 var_get_name (vi), var_get_width (vi));
3704 if (!val_labs_equal (var_get_value_labels (v0),
3705 var_get_value_labels (vi)))
3707 msg (SE, _("%s=%s requires the variables to be "
3708 "moved to have the same value labels, but %s "
3709 "and %s have different value labels."),
3710 subcommand_name, pos_name,
3711 var_get_name (v0), var_get_name (vi));
3714 if (!ctables_categories_equal (c0, ci))
3716 msg (SE, _("%s=%s requires the variables to be "
3717 "moved to have the same category "
3718 "specifications, but %s and %s have different "
3719 "category specifications."),
3720 subcommand_name, pos_name,
3721 var_get_name (v0), var_get_name (vi));
3730 ctables_prepare_table (struct ctables_table *t)
3732 for (enum pivot_axis_type a = 0; a < PIVOT_N_AXES; a++)
3735 t->stacks[a] = enumerate_fts (a, t->axes[a]);
3737 for (size_t j = 0; j < t->stacks[a].n; j++)
3739 struct ctables_nest *nest = &t->stacks[a].nests[j];
3740 for (enum ctables_domain_type dt = 0; dt < N_CTDTS; dt++)
3742 nest->domains[dt] = xmalloc (nest->n * sizeof *nest->domains[dt]);
3743 nest->n_domains[dt] = 0;
3745 for (size_t k = 0; k < nest->n; k++)
3747 if (k == nest->scale_idx)
3756 if (a != PIVOT_AXIS_LAYER)
3763 if (dt == CTDT_SUBTABLE ? a != PIVOT_AXIS_LAYER
3764 : dt == CTDT_ROW ? a == PIVOT_AXIS_COLUMN
3765 : a == PIVOT_AXIS_ROW)
3767 if (k == nest->n - 1
3768 || (nest->scale_idx == nest->n - 1
3769 && k == nest->n - 2))
3775 if (a == PIVOT_AXIS_COLUMN)
3780 if (a == PIVOT_AXIS_ROW)
3785 nest->domains[dt][nest->n_domains[dt]++] = k;
3792 struct ctables_nest *nest = xmalloc (sizeof *nest);
3793 *nest = (struct ctables_nest) { .n = 0 };
3794 t->stacks[a] = (struct ctables_stack) { .nests = nest, .n = 1 };
3797 struct ctables_stack *stack = &t->stacks[t->summary_axis];
3798 for (size_t i = 0; i < stack->n; i++)
3800 struct ctables_nest *nest = &stack->nests[i];
3801 if (!nest->specs[CSV_CELL].n)
3803 struct ctables_summary_spec_set *specs = &nest->specs[CSV_CELL];
3804 specs->specs = xmalloc (sizeof *specs->specs);
3807 enum ctables_summary_function function
3808 = specs->var ? CTSF_MEAN : CTSF_COUNT;
3809 struct ctables_var var = { .is_mrset = false, .var = specs->var };
3811 *specs->specs = (struct ctables_summary_spec) {
3812 .function = function,
3813 .format = ctables_summary_default_format (function, &var),
3814 .label = ctables_summary_default_label (function, 0),
3817 specs->var = nest->vars[0];
3819 ctables_summary_spec_set_clone (&nest->specs[CSV_TOTAL],
3820 &nest->specs[CSV_CELL]);
3822 else if (!nest->specs[CSV_TOTAL].n)
3823 ctables_summary_spec_set_clone (&nest->specs[CSV_TOTAL],
3824 &nest->specs[CSV_CELL]);
3827 struct ctables_summary_spec_set *merged = &t->summary_specs;
3828 struct merge_item *items = xnmalloc (2 * stack->n, sizeof *items);
3830 for (size_t j = 0; j < stack->n; j++)
3832 const struct ctables_nest *nest = &stack->nests[j];
3834 for (enum ctables_summary_variant sv = 0; sv < N_CSVS; sv++)
3835 items[n_left++] = (struct merge_item) { .set = &nest->specs[sv] };
3840 struct merge_item min = items[0];
3841 for (size_t j = 1; j < n_left; j++)
3842 if (merge_item_compare_3way (&items[j], &min) < 0)
3845 if (merged->n >= merged->allocated)
3846 merged->specs = x2nrealloc (merged->specs, &merged->allocated,
3847 sizeof *merged->specs);
3848 merged->specs[merged->n++] = min.set->specs[min.ofs];
3850 for (size_t j = 0; j < n_left; )
3852 if (merge_item_compare_3way (&items[j], &min) == 0)
3854 struct merge_item *item = &items[j];
3855 item->set->specs[item->ofs].axis_idx = merged->n - 1;
3856 if (++item->ofs >= item->set->n)
3858 items[j] = items[--n_left];
3867 for (size_t j = 0; j < merged->n; j++)
3868 printf ("%s\n", ctables_summary_function_name (merged->specs[j].function));
3870 for (size_t j = 0; j < stack->n; j++)
3872 const struct ctables_nest *nest = &stack->nests[j];
3873 for (enum ctables_summary_variant sv = 0; sv < N_CSVS; sv++)
3875 const struct ctables_summary_spec_set *specs = &nest->specs[sv];
3876 for (size_t k = 0; k < specs->n; k++)
3877 printf ("(%s, %zu) ", ctables_summary_function_name (specs->specs[k].function),
3878 specs->specs[k].axis_idx);
3884 return (ctables_check_label_position (t, PIVOT_AXIS_ROW)
3885 && ctables_check_label_position (t, PIVOT_AXIS_COLUMN));
3889 ctables_insert_clabels_values (struct ctables_table *t, const struct ccase *c,
3890 enum pivot_axis_type a)
3892 struct ctables_stack *stack = &t->stacks[a];
3893 for (size_t i = 0; i < stack->n; i++)
3895 const struct ctables_nest *nest = &stack->nests[i];
3896 const struct variable *var = nest->vars[nest->n - 1];
3897 const union value *value = case_data (c, var);
3899 if (var_is_numeric (var) && value->f == SYSMIS)
3902 if (ctables_categories_match (t->categories [var_get_dict_index (var)],
3904 ctables_value_insert (t, value, var_get_width (var));
3909 compare_clabels_values_3way (const void *a_, const void *b_, const void *width_)
3911 const struct ctables_value *const *ap = a_;
3912 const struct ctables_value *const *bp = b_;
3913 const struct ctables_value *a = *ap;
3914 const struct ctables_value *b = *bp;
3915 const int *width = width_;
3916 return value_compare_3way (&a->value, &b->value, *width);
3920 ctables_sort_clabels_values (struct ctables_table *t)
3922 const struct variable *v0 = t->clabels_example;
3923 int width = var_get_width (v0);
3925 struct ctables_categories *c0 = t->categories[var_get_dict_index (v0)];
3928 const struct val_labs *val_labs = var_get_value_labels (v0);
3929 for (const struct val_lab *vl = val_labs_first (val_labs); vl;
3930 vl = val_labs_next (val_labs, vl))
3931 if (ctables_categories_match (c0, &vl->value, v0))
3932 ctables_value_insert (t, &vl->value, width);
3935 size_t n = hmap_count (&t->clabels_values_map);
3936 t->clabels_values = xnmalloc (n, sizeof *t->clabels_values);
3938 struct ctables_value *clv;
3940 HMAP_FOR_EACH (clv, struct ctables_value, node, &t->clabels_values_map)
3941 t->clabels_values[i++] = clv;
3942 t->n_clabels_values = n;
3945 sort (t->clabels_values, n, sizeof *t->clabels_values,
3946 compare_clabels_values_3way, &width);
3948 for (size_t i = 0; i < n; i++)
3949 t->clabels_values[i]->leaf = i;
3953 ctables_add_category_occurrences (const struct variable *var,
3954 struct hmap *occurrences,
3955 const struct ctables_categories *cats)
3957 const struct val_labs *val_labs = var_get_value_labels (var);
3959 for (size_t i = 0; i < cats->n_cats; i++)
3961 const struct ctables_category *c = &cats->cats[i];
3965 ctables_add_occurrence (var, &(const union value) { .f = c->number },
3973 assert (var_is_numeric (var));
3974 for (const struct val_lab *vl = val_labs_first (val_labs); vl;
3975 vl = val_labs_next (val_labs, vl))
3976 if (vl->value.f >= c->range[0] && vl->value.f <= c->range[1])
3977 ctables_add_occurrence (var, &vl->value, occurrences);
3981 for (const struct val_lab *vl = val_labs_first (val_labs); vl;
3982 vl = val_labs_next (val_labs, vl))
3983 if (var_is_value_missing (var, &vl->value))
3984 ctables_add_occurrence (var, &vl->value, occurrences);
3988 for (const struct val_lab *vl = val_labs_first (val_labs); vl;
3989 vl = val_labs_next (val_labs, vl))
3990 ctables_add_occurrence (var, &vl->value, occurrences);
3993 case CCT_POSTCOMPUTE:
4003 for (const struct val_lab *vl = val_labs_first (val_labs); vl;
4004 vl = val_labs_next (val_labs, vl))
4005 if (c->include_missing || !var_is_value_missing (var, &vl->value))
4006 ctables_add_occurrence (var, &vl->value, occurrences);
4013 ctables_section_recurse_add_empty_categories (
4014 struct ctables_section *s,
4015 const struct ctables_category *cats[PIVOT_N_AXES][10], struct ccase *c,
4016 enum pivot_axis_type a, size_t a_idx)
4018 if (a >= PIVOT_N_AXES)
4019 ctables_cell_insert__ (s, c, cats);
4020 else if (!s->nests[a] || a_idx >= s->nests[a]->n)
4021 ctables_section_recurse_add_empty_categories (s, cats, c, a + 1, 0);
4024 const struct variable *var = s->nests[a]->vars[a_idx];
4025 const struct ctables_categories *categories = s->table->categories[
4026 var_get_dict_index (var)];
4027 int width = var_get_width (var);
4028 const struct hmap *occurrences = &s->occurrences[a][a_idx];
4029 const struct ctables_occurrence *o;
4030 HMAP_FOR_EACH (o, struct ctables_occurrence, node, occurrences)
4032 union value *value = case_data_rw (c, var);
4033 value_destroy (value, width);
4034 value_clone (value, &o->value, width);
4035 cats[a][a_idx] = ctables_categories_match (categories, value, var);
4036 assert (cats[a][a_idx] != NULL);
4037 ctables_section_recurse_add_empty_categories (s, cats, c, a, a_idx + 1);
4040 for (size_t i = 0; i < categories->n_cats; i++)
4042 const struct ctables_category *cat = &categories->cats[i];
4043 if (cat->type == CCT_POSTCOMPUTE)
4045 cats[a][a_idx] = cat;
4046 ctables_section_recurse_add_empty_categories (s, cats, c, a, a_idx + 1);
4053 ctables_section_add_empty_categories (struct ctables_section *s)
4055 bool show_empty = false;
4056 for (size_t a = 0; a < PIVOT_N_AXES; a++)
4058 for (size_t k = 0; k < s->nests[a]->n; k++)
4059 if (k != s->nests[a]->scale_idx)
4061 const struct variable *var = s->nests[a]->vars[k];
4062 const struct ctables_categories *cats = s->table->categories[
4063 var_get_dict_index (var)];
4064 if (cats->show_empty)
4067 ctables_add_category_occurrences (var, &s->occurrences[a][k], cats);
4073 const struct ctables_category *cats[PIVOT_N_AXES][10]; /* XXX */
4074 struct ccase *c = case_create (dict_get_proto (s->table->ctables->dict));
4075 ctables_section_recurse_add_empty_categories (s, cats, c, 0, 0);
4080 ctables_execute (struct dataset *ds, struct ctables *ct)
4082 for (size_t i = 0; i < ct->n_tables; i++)
4084 struct ctables_table *t = ct->tables[i];
4085 t->sections = xnmalloc (MAX (1, t->stacks[PIVOT_AXIS_ROW].n) *
4086 MAX (1, t->stacks[PIVOT_AXIS_COLUMN].n) *
4087 MAX (1, t->stacks[PIVOT_AXIS_LAYER].n),
4088 sizeof *t->sections);
4089 size_t ix[PIVOT_N_AXES];
4090 ctables_table_add_section (t, 0, ix);
4093 struct casereader *input = proc_open (ds);
4094 bool warn_on_invalid = true;
4095 for (struct ccase *c = casereader_read (input); c;
4096 case_unref (c), c = casereader_read (input))
4098 double d_weight = dict_get_case_weight (dataset_dict (ds), c,
4100 double e_weight = (ct->e_weight
4101 ? var_force_valid_weight (ct->e_weight,
4102 case_num (c, ct->e_weight),
4106 for (size_t i = 0; i < ct->n_tables; i++)
4108 struct ctables_table *t = ct->tables[i];
4110 for (size_t j = 0; j < t->n_sections; j++)
4111 ctables_cell_insert (&t->sections[j], c, d_weight, e_weight);
4113 for (enum pivot_axis_type a = 0; a < PIVOT_N_AXES; a++)
4114 if (t->label_axis[a] != a)
4115 ctables_insert_clabels_values (t, c, a);
4118 casereader_destroy (input);
4120 for (size_t i = 0; i < ct->n_tables; i++)
4122 struct ctables_table *t = ct->tables[i];
4124 if (t->clabels_example)
4125 ctables_sort_clabels_values (t);
4127 for (size_t j = 0; j < t->n_sections; j++)
4128 ctables_section_add_empty_categories (&t->sections[j]);
4130 ctables_table_output (ct, ct->tables[i]);
4132 return proc_commit (ds);
4137 typedef struct ctables_pcexpr *parse_recursively_func (struct lexer *);
4140 ctables_pcexpr_destroy (struct ctables_pcexpr *e)
4146 case CTPO_CAT_STRING:
4156 for (size_t i = 0; i < 2; i++)
4157 ctables_pcexpr_destroy (e->subs[i]);
4161 case CTPO_CAT_NUMBER:
4162 case CTPO_CAT_RANGE:
4163 case CTPO_CAT_MISSING:
4164 case CTPO_CAT_OTHERNM:
4165 case CTPO_CAT_SUBTOTAL:
4166 case CTPO_CAT_TOTAL:
4170 msg_location_destroy (e->location);
4175 static struct ctables_pcexpr *
4176 ctables_pcexpr_allocate_binary (enum ctables_postcompute_op op,
4177 struct ctables_pcexpr *sub0,
4178 struct ctables_pcexpr *sub1)
4180 struct ctables_pcexpr *e = xmalloc (sizeof *e);
4181 *e = (struct ctables_pcexpr) {
4183 .subs = { sub0, sub1 },
4184 .location = msg_location_merged (sub0->location, sub1->location),
4189 /* How to parse an operator. */
4192 enum token_type token;
4193 enum ctables_postcompute_op op;
4196 static const struct operator *
4197 match_operator (struct lexer *lexer, const struct operator ops[], size_t n_ops)
4199 for (const struct operator *op = ops; op < ops + n_ops; op++)
4200 if (lex_token (lexer) == op->token)
4202 if (op->token != T_NEG_NUM)
4211 static struct ctables_pcexpr *
4212 parse_binary_operators__ (struct lexer *lexer,
4213 const struct operator ops[], size_t n_ops,
4214 parse_recursively_func *parse_next_level,
4215 const char *chain_warning,
4216 struct ctables_pcexpr *lhs)
4218 for (int op_count = 0; ; op_count++)
4220 const struct operator *op = match_operator (lexer, ops, n_ops);
4223 if (op_count > 1 && chain_warning)
4224 msg_at (SW, lhs->location, "%s", chain_warning);
4229 struct ctables_pcexpr *rhs = parse_next_level (lexer);
4232 ctables_pcexpr_destroy (lhs);
4236 lhs = ctables_pcexpr_allocate_binary (op->op, lhs, rhs);
4240 static struct ctables_pcexpr *
4241 parse_binary_operators (struct lexer *lexer,
4242 const struct operator ops[], size_t n_ops,
4243 parse_recursively_func *parse_next_level,
4244 const char *chain_warning)
4246 struct ctables_pcexpr *lhs = parse_next_level (lexer);
4250 return parse_binary_operators__ (lexer, ops, n_ops, parse_next_level,
4251 chain_warning, lhs);
4254 static struct ctables_pcexpr *parse_add (struct lexer *);
4256 static struct ctables_pcexpr
4257 ctpo_cat_range (double low, double high)
4259 return (struct ctables_pcexpr) {
4260 .op = CTPO_CAT_RANGE,
4261 .range = { low, high },
4265 static struct ctables_pcexpr *
4266 parse_primary (struct lexer *lexer)
4268 int start_ofs = lex_ofs (lexer);
4269 struct ctables_pcexpr e;
4270 if (lex_is_number (lexer))
4272 e = (struct ctables_pcexpr) { .op = CTPO_CONSTANT,
4273 .number = lex_number (lexer) };
4276 else if (lex_match_id (lexer, "MISSING"))
4277 e = (struct ctables_pcexpr) { .op = CTPO_CAT_MISSING };
4278 else if (lex_match_id (lexer, "OTHERNM"))
4279 e = (struct ctables_pcexpr) { .op = CTPO_CAT_OTHERNM };
4280 else if (lex_match_id (lexer, "TOTAL"))
4281 e = (struct ctables_pcexpr) { .op = CTPO_CAT_TOTAL };
4282 else if (lex_match_id (lexer, "SUBTOTAL"))
4284 size_t subtotal_index = 0;
4285 if (lex_match (lexer, T_LBRACK))
4287 if (!lex_force_int_range (lexer, "SUBTOTAL", 1, LONG_MAX))
4289 subtotal_index = lex_integer (lexer);
4291 if (!lex_force_match (lexer, T_RBRACK))
4294 e = (struct ctables_pcexpr) { .op = CTPO_CAT_SUBTOTAL,
4295 .subtotal_index = subtotal_index };
4297 else if (lex_match (lexer, T_LBRACK))
4299 if (lex_match_id (lexer, "LO"))
4301 if (!lex_force_match_id (lexer, "THRU") || lex_force_num (lexer))
4303 e = ctpo_cat_range (-DBL_MAX, lex_number (lexer));
4306 else if (lex_is_number (lexer))
4308 double number = lex_number (lexer);
4310 if (lex_match_id (lexer, "THRU"))
4312 if (lex_match_id (lexer, "HI"))
4313 e = ctpo_cat_range (number, DBL_MAX);
4316 if (!lex_force_num (lexer))
4318 e = ctpo_cat_range (number, lex_number (lexer));
4323 e = (struct ctables_pcexpr) { .op = CTPO_CAT_NUMBER,
4326 else if (lex_is_string (lexer))
4328 e = (struct ctables_pcexpr) {
4329 .op = CTPO_CAT_STRING,
4330 .string = ss_xstrdup (lex_tokss (lexer)),
4336 lex_error (lexer, NULL);
4340 if (!lex_force_match (lexer, T_RBRACK))
4342 if (e.op == CTPO_CAT_STRING)
4347 else if (lex_match (lexer, T_LPAREN))
4349 struct ctables_pcexpr *ep = parse_add (lexer);
4352 if (!lex_force_match (lexer, T_RPAREN))
4354 ctables_pcexpr_destroy (ep);
4361 lex_error (lexer, NULL);
4365 e.location = lex_ofs_location (lexer, start_ofs, lex_ofs (lexer) - 1);
4366 return xmemdup (&e, sizeof e);
4369 static struct ctables_pcexpr *
4370 ctables_pcexpr_allocate_neg (struct ctables_pcexpr *sub,
4371 struct lexer *lexer, int start_ofs)
4373 struct ctables_pcexpr *e = xmalloc (sizeof *e);
4374 *e = (struct ctables_pcexpr) {
4377 .location = lex_ofs_location (lexer, start_ofs, lex_ofs (lexer) - 1),
4382 static struct ctables_pcexpr *
4383 parse_exp (struct lexer *lexer)
4385 static const struct operator op = { T_EXP, CTPO_POW };
4387 const char *chain_warning =
4388 _("The exponentiation operator (`**') is left-associative: "
4389 "`a**b**c' equals `(a**b)**c', not `a**(b**c)'. "
4390 "To disable this warning, insert parentheses.");
4392 if (lex_token (lexer) != T_NEG_NUM || lex_next_token (lexer, 1) != T_EXP)
4393 return parse_binary_operators (lexer, &op, 1,
4394 parse_primary, chain_warning);
4396 /* Special case for situations like "-5**6", which must be parsed as
4399 int start_ofs = lex_ofs (lexer);
4400 struct ctables_pcexpr *lhs = xmalloc (sizeof *lhs);
4401 *lhs = (struct ctables_pcexpr) {
4402 .op = CTPO_CONSTANT,
4403 .number = -lex_tokval (lexer),
4404 .location = lex_ofs_location (lexer, start_ofs, lex_ofs (lexer)),
4408 struct ctables_pcexpr *node = parse_binary_operators__ (
4409 lexer, &op, 1, parse_primary, chain_warning, lhs);
4413 return ctables_pcexpr_allocate_neg (node, lexer, start_ofs);
4416 /* Parses the unary minus level. */
4417 static struct ctables_pcexpr *
4418 parse_neg (struct lexer *lexer)
4420 int start_ofs = lex_ofs (lexer);
4421 if (!lex_match (lexer, T_DASH))
4422 return parse_exp (lexer);
4424 struct ctables_pcexpr *inner = parse_neg (lexer);
4428 return ctables_pcexpr_allocate_neg (inner, lexer, start_ofs);
4431 /* Parses the multiplication and division level. */
4432 static struct ctables_pcexpr *
4433 parse_mul (struct lexer *lexer)
4435 static const struct operator ops[] =
4437 { T_ASTERISK, CTPO_MUL },
4438 { T_SLASH, CTPO_DIV },
4441 return parse_binary_operators (lexer, ops, sizeof ops / sizeof *ops,
4445 /* Parses the addition and subtraction level. */
4446 static struct ctables_pcexpr *
4447 parse_add (struct lexer *lexer)
4449 static const struct operator ops[] =
4451 { T_PLUS, CTPO_ADD },
4452 { T_DASH, CTPO_SUB },
4453 { T_NEG_NUM, CTPO_ADD },
4456 return parse_binary_operators (lexer, ops, sizeof ops / sizeof *ops,
4460 static struct ctables_postcompute *
4461 ctables_find_postcompute (struct ctables *ct, const char *name)
4463 struct ctables_postcompute *pc;
4464 HMAP_FOR_EACH_WITH_HASH (pc, struct ctables_postcompute, hmap_node,
4465 utf8_hash_case_string (name, 0), &ct->postcomputes)
4466 if (!utf8_strcasecmp (pc->name, name))
4472 ctables_parse_pcompute (struct lexer *lexer, struct ctables *ct)
4474 int pcompute_start = lex_ofs (lexer) - 1;
4476 if (!lex_force_match (lexer, T_AND) || !lex_force_id (lexer))
4479 char *name = ss_xstrdup (lex_tokss (lexer));
4482 if (!lex_force_match (lexer, T_EQUALS)
4483 || !lex_force_match_id (lexer, "EXPR")
4484 || !lex_force_match (lexer, T_LPAREN))
4490 int expr_start = lex_ofs (lexer);
4491 struct ctables_pcexpr *expr = parse_add (lexer);
4492 int expr_end = lex_ofs (lexer) - 1;
4493 if (!expr || !lex_force_match (lexer, T_RPAREN))
4498 int pcompute_end = lex_ofs (lexer) - 1;
4500 struct msg_location *location = lex_ofs_location (lexer, pcompute_start,
4503 struct ctables_postcompute *pc = ctables_find_postcompute (ct, name);
4506 msg_at (SW, location, _("New definition of &%s will override the "
4507 "previous definition."),
4509 msg_at (SN, pc->location, _("This is the previous definition."));
4511 ctables_pcexpr_destroy (pc->expr);
4512 msg_location_destroy (pc->location);
4517 pc = xmalloc (sizeof *pc);
4518 *pc = (struct ctables_postcompute) { .name = name };
4519 hmap_insert (&ct->postcomputes, &pc->hmap_node,
4520 utf8_hash_case_string (pc->name, 0));
4523 pc->location = location;
4525 pc->label = lex_ofs_representation (lexer, expr_start, expr_end);
4530 ctables_parse_pproperties_format (struct lexer *lexer,
4531 struct ctables_summary_spec_set *sss)
4533 *sss = (struct ctables_summary_spec_set) { .n = 0 };
4535 while (lex_token (lexer) != T_ENDCMD && lex_token (lexer) != T_SLASH
4536 && !(lex_token (lexer) == T_ID
4537 && (lex_id_match (ss_cstr ("LABEL"), lex_tokss (lexer))
4538 || lex_id_match (ss_cstr ("HIDESOURCECATS"),
4539 lex_tokss (lexer)))))
4541 /* Parse function. */
4542 enum ctables_summary_function function;
4543 if (!parse_ctables_summary_function (lexer, &function))
4546 /* Parse percentile. */
4547 double percentile = 0;
4548 if (function == CTSF_PTILE)
4550 if (!lex_force_num_range_closed (lexer, "PTILE", 0, 100))
4552 percentile = lex_number (lexer);
4557 struct fmt_spec format;
4558 if (!parse_format_specifier (lexer, &format)
4559 || !fmt_check_output (&format)
4560 || !fmt_check_type_compat (&format, VAL_NUMERIC))
4563 if (sss->n >= sss->allocated)
4564 sss->specs = x2nrealloc (sss->specs, &sss->allocated,
4565 sizeof *sss->specs);
4566 sss->specs[sss->n++] = (struct ctables_summary_spec) {
4567 .function = function,
4568 .percentile = percentile,
4575 ctables_summary_spec_set_uninit (sss);
4580 ctables_parse_pproperties (struct lexer *lexer, struct ctables *ct)
4582 struct ctables_postcompute **pcs = NULL;
4584 size_t allocated_pcs = 0;
4586 while (lex_match (lexer, T_AND))
4588 if (!lex_force_id (lexer))
4590 struct ctables_postcompute *pc
4591 = ctables_find_postcompute (ct, lex_tokcstr (lexer));
4594 msg (SE, _("Unknown computed category &%s."), lex_tokcstr (lexer));
4599 if (n_pcs >= allocated_pcs)
4600 pcs = x2nrealloc (pcs, &allocated_pcs, sizeof *pcs);
4604 while (lex_token (lexer) != T_SLASH && lex_token (lexer) != T_ENDCMD)
4606 if (lex_match_id (lexer, "LABEL"))
4608 lex_match (lexer, T_EQUALS);
4609 if (!lex_force_string (lexer))
4612 for (size_t i = 0; i < n_pcs; i++)
4614 free (pcs[i]->label);
4615 pcs[i]->label = ss_xstrdup (lex_tokss (lexer));
4620 else if (lex_match_id (lexer, "FORMAT"))
4622 lex_match (lexer, T_EQUALS);
4624 struct ctables_summary_spec_set sss;
4625 if (!ctables_parse_pproperties_format (lexer, &sss))
4628 for (size_t i = 0; i < n_pcs; i++)
4631 ctables_summary_spec_set_uninit (pcs[i]->specs);
4633 pcs[i]->specs = xmalloc (sizeof *pcs[i]->specs);
4634 ctables_summary_spec_set_clone (pcs[i]->specs, &sss);
4636 ctables_summary_spec_set_uninit (&sss);
4638 else if (lex_match_id (lexer, "HIDESOURCECATS"))
4640 lex_match (lexer, T_EQUALS);
4641 bool hide_source_cats;
4642 if (!parse_bool (lexer, &hide_source_cats))
4644 for (size_t i = 0; i < n_pcs; i++)
4645 pcs[i]->hide_source_cats = hide_source_cats;
4649 lex_error_expecting (lexer, "LABEL", "FORMAT", "HIDESOURCECATS");
4662 cmd_ctables (struct lexer *lexer, struct dataset *ds)
4664 size_t n_vars = dict_get_n_vars (dataset_dict (ds));
4665 enum ctables_vlabel *vlabels = xnmalloc (n_vars, sizeof *vlabels);
4666 enum settings_value_show tvars = settings_get_show_variables ();
4667 for (size_t i = 0; i < n_vars; i++)
4668 vlabels[i] = (enum ctables_vlabel) tvars;
4670 struct pivot_table_look *look = pivot_table_look_unshare (
4671 pivot_table_look_ref (pivot_table_look_get_default ()));
4672 look->omit_empty = false;
4674 struct ctables *ct = xmalloc (sizeof *ct);
4675 *ct = (struct ctables) {
4676 .dict = dataset_dict (ds),
4678 .ctables_formats = FMT_SETTINGS_INIT,
4680 .postcomputes = HMAP_INITIALIZER (ct->postcomputes),
4686 const char *dot_string;
4687 const char *comma_string;
4689 static const struct ctf ctfs[4] = {
4690 { CTEF_NEGPAREN, "(,,,)", "(...)" },
4691 { CTEF_NEQUAL, "-,N=,,", "-.N=.." },
4692 { CTEF_PAREN, "-,(,),", "-.(.)." },
4693 { CTEF_PCTPAREN, "-,(,%),", "-.(.%)." },
4695 bool is_dot = settings_get_fmt_settings ()->decimal == '.';
4696 for (size_t i = 0; i < 4; i++)
4698 const char *s = is_dot ? ctfs[i].dot_string : ctfs[i].comma_string;
4699 fmt_settings_set_cc (&ct->ctables_formats, ctfs[i].type,
4700 fmt_number_style_from_string (s));
4703 if (!lex_force_match (lexer, T_SLASH))
4706 while (!lex_match_id (lexer, "TABLE"))
4708 if (lex_match_id (lexer, "FORMAT"))
4710 double widths[2] = { SYSMIS, SYSMIS };
4711 double units_per_inch = 72.0;
4713 while (lex_token (lexer) != T_SLASH)
4715 if (lex_match_id (lexer, "MINCOLWIDTH"))
4717 if (!parse_col_width (lexer, "MINCOLWIDTH", &widths[0]))
4720 else if (lex_match_id (lexer, "MAXCOLWIDTH"))
4722 if (!parse_col_width (lexer, "MAXCOLWIDTH", &widths[1]))
4725 else if (lex_match_id (lexer, "UNITS"))
4727 lex_match (lexer, T_EQUALS);
4728 if (lex_match_id (lexer, "POINTS"))
4729 units_per_inch = 72.0;
4730 else if (lex_match_id (lexer, "INCHES"))
4731 units_per_inch = 1.0;
4732 else if (lex_match_id (lexer, "CM"))
4733 units_per_inch = 2.54;
4736 lex_error_expecting (lexer, "POINTS", "INCHES", "CM");
4740 else if (lex_match_id (lexer, "EMPTY"))
4745 lex_match (lexer, T_EQUALS);
4746 if (lex_match_id (lexer, "ZERO"))
4748 /* Nothing to do. */
4750 else if (lex_match_id (lexer, "BLANK"))
4751 ct->zero = xstrdup ("");
4752 else if (lex_force_string (lexer))
4754 ct->zero = ss_xstrdup (lex_tokss (lexer));
4760 else if (lex_match_id (lexer, "MISSING"))
4762 lex_match (lexer, T_EQUALS);
4763 if (!lex_force_string (lexer))
4767 ct->missing = (strcmp (lex_tokcstr (lexer), ".")
4768 ? ss_xstrdup (lex_tokss (lexer))
4774 lex_error_expecting (lexer, "MINCOLWIDTH", "MAXCOLWIDTH",
4775 "UNITS", "EMPTY", "MISSING");
4780 if (widths[0] != SYSMIS && widths[1] != SYSMIS
4781 && widths[0] > widths[1])
4783 msg (SE, _("MINCOLWIDTH must not be greater than MAXCOLWIDTH."));
4787 for (size_t i = 0; i < 2; i++)
4788 if (widths[i] != SYSMIS)
4790 int *wr = ct->look->width_ranges[TABLE_HORZ];
4791 wr[i] = widths[i] / units_per_inch * 96.0;
4796 else if (lex_match_id (lexer, "VLABELS"))
4798 if (!lex_force_match_id (lexer, "VARIABLES"))
4800 lex_match (lexer, T_EQUALS);
4802 struct variable **vars;
4804 if (!parse_variables (lexer, dataset_dict (ds), &vars, &n_vars,
4808 if (!lex_force_match_id (lexer, "DISPLAY"))
4813 lex_match (lexer, T_EQUALS);
4815 enum ctables_vlabel vlabel;
4816 if (lex_match_id (lexer, "DEFAULT"))
4817 vlabel = (enum ctables_vlabel) settings_get_show_variables ();
4818 else if (lex_match_id (lexer, "NAME"))
4820 else if (lex_match_id (lexer, "LABEL"))
4821 vlabel = CTVL_LABEL;
4822 else if (lex_match_id (lexer, "BOTH"))
4824 else if (lex_match_id (lexer, "NONE"))
4828 lex_error_expecting (lexer, "DEFAULT", "NAME", "LABEL",
4834 for (size_t i = 0; i < n_vars; i++)
4835 ct->vlabels[var_get_dict_index (vars[i])] = vlabel;
4838 else if (lex_match_id (lexer, "MRSETS"))
4840 if (!lex_force_match_id (lexer, "COUNTDUPLICATES"))
4842 lex_match (lexer, T_EQUALS);
4843 if (!parse_bool (lexer, &ct->mrsets_count_duplicates))
4846 else if (lex_match_id (lexer, "SMISSING"))
4848 if (lex_match_id (lexer, "VARIABLE"))
4849 ct->smissing_listwise = false;
4850 else if (lex_match_id (lexer, "LISTWISE"))
4851 ct->smissing_listwise = true;
4854 lex_error_expecting (lexer, "VARIABLE", "LISTWISE");
4858 else if (lex_match_id (lexer, "PCOMPUTE"))
4860 if (!ctables_parse_pcompute (lexer, ct))
4863 else if (lex_match_id (lexer, "PPROPERTIES"))
4865 if (!ctables_parse_pproperties (lexer, ct))
4868 else if (lex_match_id (lexer, "WEIGHT"))
4870 if (!lex_force_match_id (lexer, "VARIABLE"))
4872 lex_match (lexer, T_EQUALS);
4873 ct->e_weight = parse_variable (lexer, dataset_dict (ds));
4877 else if (lex_match_id (lexer, " HIDESMALLCOUNTS"))
4879 if (lex_match_id (lexer, "COUNT"))
4881 lex_match (lexer, T_EQUALS);
4882 if (!lex_force_int_range (lexer, "HIDESMALLCOUNTS COUNT",
4885 ct->hide_threshold = lex_integer (lexer);
4888 else if (ct->hide_threshold == 0)
4889 ct->hide_threshold = 5;
4893 lex_error_expecting (lexer, "FORMAT", "VLABELS", "MRSETS",
4894 "SMISSING", "PCOMPUTE", "PPROPERTIES",
4895 "WEIGHT", "HIDESMALLCOUNTS", "TABLE");
4899 if (!lex_force_match (lexer, T_SLASH))
4903 size_t allocated_tables = 0;
4906 if (ct->n_tables >= allocated_tables)
4907 ct->tables = x2nrealloc (ct->tables, &allocated_tables,
4908 sizeof *ct->tables);
4910 struct ctables_category *cat = xmalloc (sizeof *cat);
4911 *cat = (struct ctables_category) {
4913 .include_missing = false,
4914 .sort_ascending = true,
4917 struct ctables_categories *c = xmalloc (sizeof *c);
4918 size_t n_vars = dict_get_n_vars (dataset_dict (ds));
4919 *c = (struct ctables_categories) {
4926 struct ctables_categories **categories = xnmalloc (n_vars,
4927 sizeof *categories);
4928 for (size_t i = 0; i < n_vars; i++)
4931 struct ctables_table *t = xmalloc (sizeof *t);
4932 *t = (struct ctables_table) {
4934 .slabels_axis = PIVOT_AXIS_COLUMN,
4935 .slabels_visible = true,
4936 .clabels_values_map = HMAP_INITIALIZER (t->clabels_values_map),
4938 [PIVOT_AXIS_ROW] = PIVOT_AXIS_ROW,
4939 [PIVOT_AXIS_COLUMN] = PIVOT_AXIS_COLUMN,
4940 [PIVOT_AXIS_LAYER] = PIVOT_AXIS_LAYER,
4942 .clabels_from_axis = PIVOT_AXIS_LAYER,
4943 .categories = categories,
4944 .n_categories = n_vars,
4947 ct->tables[ct->n_tables++] = t;
4949 lex_match (lexer, T_EQUALS);
4950 if (!ctables_axis_parse (lexer, dataset_dict (ds), ct, t, PIVOT_AXIS_ROW))
4952 if (lex_match (lexer, T_BY))
4954 if (!ctables_axis_parse (lexer, dataset_dict (ds),
4955 ct, t, PIVOT_AXIS_COLUMN))
4958 if (lex_match (lexer, T_BY))
4960 if (!ctables_axis_parse (lexer, dataset_dict (ds),
4961 ct, t, PIVOT_AXIS_LAYER))
4966 if (!t->axes[PIVOT_AXIS_ROW] && !t->axes[PIVOT_AXIS_COLUMN]
4967 && !t->axes[PIVOT_AXIS_LAYER])
4969 lex_error (lexer, _("At least one variable must be specified."));
4973 const struct ctables_axis *scales[PIVOT_N_AXES];
4974 size_t n_scales = 0;
4975 for (enum pivot_axis_type a = 0; a < PIVOT_N_AXES; a++)
4977 scales[a] = find_scale (t->axes[a]);
4983 msg (SE, _("Scale variables may appear only on one axis."));
4984 if (scales[PIVOT_AXIS_ROW])
4985 msg_at (SN, scales[PIVOT_AXIS_ROW]->loc,
4986 _("This scale variable appears on the rows axis."));
4987 if (scales[PIVOT_AXIS_COLUMN])
4988 msg_at (SN, scales[PIVOT_AXIS_COLUMN]->loc,
4989 _("This scale variable appears on the columns axis."));
4990 if (scales[PIVOT_AXIS_LAYER])
4991 msg_at (SN, scales[PIVOT_AXIS_LAYER]->loc,
4992 _("This scale variable appears on the layer axis."));
4996 const struct ctables_axis *summaries[PIVOT_N_AXES];
4997 size_t n_summaries = 0;
4998 for (enum pivot_axis_type a = 0; a < PIVOT_N_AXES; a++)
5000 summaries[a] = (scales[a]
5002 : find_categorical_summary_spec (t->axes[a]));
5006 if (n_summaries > 1)
5008 msg (SE, _("Summaries may appear only on one axis."));
5009 if (summaries[PIVOT_AXIS_ROW])
5010 msg_at (SN, summaries[PIVOT_AXIS_ROW]->loc,
5011 _("This variable on the rows axis has a summary."));
5012 if (summaries[PIVOT_AXIS_COLUMN])
5013 msg_at (SN, summaries[PIVOT_AXIS_COLUMN]->loc,
5014 _("This variable on the columns axis has a summary."));
5015 if (summaries[PIVOT_AXIS_LAYER])
5016 msg_at (SN, summaries[PIVOT_AXIS_LAYER]->loc,
5017 _("This variable on the layers axis has a summary."));
5020 for (enum pivot_axis_type a = 0; a < PIVOT_N_AXES; a++)
5021 if (n_summaries ? summaries[a] : t->axes[a])
5023 t->summary_axis = a;
5027 if (lex_token (lexer) == T_ENDCMD)
5029 if (!ctables_prepare_table (t))
5033 if (!lex_force_match (lexer, T_SLASH))
5036 while (!lex_match_id (lexer, "TABLE") && lex_token (lexer) != T_ENDCMD)
5038 if (lex_match_id (lexer, "SLABELS"))
5040 while (lex_token (lexer) != T_SLASH && lex_token (lexer) != T_ENDCMD)
5042 if (lex_match_id (lexer, "POSITION"))
5044 lex_match (lexer, T_EQUALS);
5045 if (lex_match_id (lexer, "COLUMN"))
5046 t->slabels_axis = PIVOT_AXIS_COLUMN;
5047 else if (lex_match_id (lexer, "ROW"))
5048 t->slabels_axis = PIVOT_AXIS_ROW;
5049 else if (lex_match_id (lexer, "LAYER"))
5050 t->slabels_axis = PIVOT_AXIS_LAYER;
5053 lex_error_expecting (lexer, "COLUMN", "ROW", "LAYER");
5057 else if (lex_match_id (lexer, "VISIBLE"))
5059 lex_match (lexer, T_EQUALS);
5060 if (!parse_bool (lexer, &t->slabels_visible))
5065 lex_error_expecting (lexer, "POSITION", "VISIBLE");
5070 else if (lex_match_id (lexer, "CLABELS"))
5072 while (lex_token (lexer) != T_SLASH && lex_token (lexer) != T_ENDCMD)
5074 if (lex_match_id (lexer, "AUTO"))
5076 t->label_axis[PIVOT_AXIS_ROW] = PIVOT_AXIS_ROW;
5077 t->label_axis[PIVOT_AXIS_COLUMN] = PIVOT_AXIS_COLUMN;
5079 else if (lex_match_id (lexer, "ROWLABELS"))
5081 lex_match (lexer, T_EQUALS);
5082 if (lex_match_id (lexer, "OPPOSITE"))
5083 t->label_axis[PIVOT_AXIS_ROW] = PIVOT_AXIS_COLUMN;
5084 else if (lex_match_id (lexer, "LAYER"))
5085 t->label_axis[PIVOT_AXIS_ROW] = PIVOT_AXIS_LAYER;
5088 lex_error_expecting (lexer, "OPPOSITE", "LAYER");
5092 else if (lex_match_id (lexer, "COLLABELS"))
5094 lex_match (lexer, T_EQUALS);
5095 if (lex_match_id (lexer, "OPPOSITE"))
5096 t->label_axis[PIVOT_AXIS_COLUMN] = PIVOT_AXIS_ROW;
5097 else if (lex_match_id (lexer, "LAYER"))
5098 t->label_axis[PIVOT_AXIS_COLUMN] = PIVOT_AXIS_LAYER;
5101 lex_error_expecting (lexer, "OPPOSITE", "LAYER");
5107 lex_error_expecting (lexer, "AUTO", "ROWLABELS",
5113 else if (lex_match_id (lexer, "CRITERIA"))
5115 if (!lex_force_match_id (lexer, "CILEVEL"))
5117 lex_match (lexer, T_EQUALS);
5119 if (!lex_force_num_range_halfopen (lexer, "CILEVEL", 0, 100))
5121 t->cilevel = lex_number (lexer);
5124 else if (lex_match_id (lexer, "CATEGORIES"))
5126 if (!ctables_table_parse_categories (lexer, dataset_dict (ds),
5130 else if (lex_match_id (lexer, "TITLES"))
5135 if (lex_match_id (lexer, "CAPTION"))
5136 textp = &t->caption;
5137 else if (lex_match_id (lexer, "CORNER"))
5139 else if (lex_match_id (lexer, "TITLE"))
5143 lex_error_expecting (lexer, "CAPTION", "CORNER", "TITLE");
5146 lex_match (lexer, T_EQUALS);
5148 struct string s = DS_EMPTY_INITIALIZER;
5149 while (lex_is_string (lexer))
5151 if (!ds_is_empty (&s))
5152 ds_put_byte (&s, ' ');
5153 ds_put_substring (&s, lex_tokss (lexer));
5157 *textp = ds_steal_cstr (&s);
5159 while (lex_token (lexer) != T_SLASH
5160 && lex_token (lexer) != T_ENDCMD);
5162 else if (lex_match_id (lexer, "SIGTEST"))
5166 t->chisq = xmalloc (sizeof *t->chisq);
5167 *t->chisq = (struct ctables_chisq) {
5169 .include_mrsets = true,
5170 .all_visible = true,
5176 if (lex_match_id (lexer, "TYPE"))
5178 lex_match (lexer, T_EQUALS);
5179 if (!lex_force_match_id (lexer, "CHISQUARE"))
5182 else if (lex_match_id (lexer, "ALPHA"))
5184 lex_match (lexer, T_EQUALS);
5185 if (!lex_force_num_range_halfopen (lexer, "ALPHA", 0, 1))
5187 t->chisq->alpha = lex_number (lexer);
5190 else if (lex_match_id (lexer, "INCLUDEMRSETS"))
5192 lex_match (lexer, T_EQUALS);
5193 if (parse_bool (lexer, &t->chisq->include_mrsets))
5196 else if (lex_match_id (lexer, "CATEGORIES"))
5198 lex_match (lexer, T_EQUALS);
5199 if (lex_match_id (lexer, "ALLVISIBLE"))
5200 t->chisq->all_visible = true;
5201 else if (lex_match_id (lexer, "SUBTOTALS"))
5202 t->chisq->all_visible = false;
5205 lex_error_expecting (lexer,
5206 "ALLVISIBLE", "SUBTOTALS");
5212 lex_error_expecting (lexer, "TYPE", "ALPHA",
5213 "INCLUDEMRSETS", "CATEGORIES");
5217 while (lex_token (lexer) != T_SLASH
5218 && lex_token (lexer) != T_ENDCMD);
5220 else if (lex_match_id (lexer, "COMPARETEST"))
5224 t->pairwise = xmalloc (sizeof *t->pairwise);
5225 *t->pairwise = (struct ctables_pairwise) {
5227 .alpha = { .05, .05 },
5228 .adjust = BONFERRONI,
5229 .include_mrsets = true,
5230 .meansvariance_allcats = true,
5231 .all_visible = true,
5240 if (lex_match_id (lexer, "TYPE"))
5242 lex_match (lexer, T_EQUALS);
5243 if (lex_match_id (lexer, "PROP"))
5244 t->pairwise->type = PROP;
5245 else if (lex_match_id (lexer, "MEAN"))
5246 t->pairwise->type = MEAN;
5249 lex_error_expecting (lexer, "PROP", "MEAN");
5253 else if (lex_match_id (lexer, "ALPHA"))
5255 lex_match (lexer, T_EQUALS);
5257 if (!lex_force_num_range_open (lexer, "ALPHA", 0, 1))
5259 double a0 = lex_number (lexer);
5262 lex_match (lexer, T_COMMA);
5263 if (lex_is_number (lexer))
5265 if (!lex_force_num_range_open (lexer, "ALPHA", 0, 1))
5267 double a1 = lex_number (lexer);
5270 t->pairwise->alpha[0] = MIN (a0, a1);
5271 t->pairwise->alpha[1] = MAX (a0, a1);
5274 t->pairwise->alpha[0] = t->pairwise->alpha[1] = a0;
5276 else if (lex_match_id (lexer, "ADJUST"))
5278 lex_match (lexer, T_EQUALS);
5279 if (lex_match_id (lexer, "BONFERRONI"))
5280 t->pairwise->adjust = BONFERRONI;
5281 else if (lex_match_id (lexer, "BH"))
5282 t->pairwise->adjust = BH;
5283 else if (lex_match_id (lexer, "NONE"))
5284 t->pairwise->adjust = 0;
5287 lex_error_expecting (lexer, "BONFERRONI", "BH",
5292 else if (lex_match_id (lexer, "INCLUDEMRSETS"))
5294 lex_match (lexer, T_EQUALS);
5295 if (!parse_bool (lexer, &t->pairwise->include_mrsets))
5298 else if (lex_match_id (lexer, "MEANSVARIANCE"))
5300 lex_match (lexer, T_EQUALS);
5301 if (lex_match_id (lexer, "ALLCATS"))
5302 t->pairwise->meansvariance_allcats = true;
5303 else if (lex_match_id (lexer, "TESTEDCATS"))
5304 t->pairwise->meansvariance_allcats = false;
5307 lex_error_expecting (lexer, "ALLCATS", "TESTEDCATS");
5311 else if (lex_match_id (lexer, "CATEGORIES"))
5313 lex_match (lexer, T_EQUALS);
5314 if (lex_match_id (lexer, "ALLVISIBLE"))
5315 t->pairwise->all_visible = true;
5316 else if (lex_match_id (lexer, "SUBTOTALS"))
5317 t->pairwise->all_visible = false;
5320 lex_error_expecting (lexer, "ALLVISIBLE",
5325 else if (lex_match_id (lexer, "MERGE"))
5327 lex_match (lexer, T_EQUALS);
5328 if (!parse_bool (lexer, &t->pairwise->merge))
5331 else if (lex_match_id (lexer, "STYLE"))
5333 lex_match (lexer, T_EQUALS);
5334 if (lex_match_id (lexer, "APA"))
5335 t->pairwise->apa_style = true;
5336 else if (lex_match_id (lexer, "SIMPLE"))
5337 t->pairwise->apa_style = false;
5340 lex_error_expecting (lexer, "APA", "SIMPLE");
5344 else if (lex_match_id (lexer, "SHOWSIG"))
5346 lex_match (lexer, T_EQUALS);
5347 if (!parse_bool (lexer, &t->pairwise->show_sig))
5352 lex_error_expecting (lexer, "TYPE", "ALPHA", "ADJUST",
5353 "INCLUDEMRSETS", "MEANSVARIANCE",
5354 "CATEGORIES", "MERGE", "STYLE",
5359 while (lex_token (lexer) != T_SLASH
5360 && lex_token (lexer) != T_ENDCMD);
5364 lex_error_expecting (lexer, "TABLE", "SLABELS", "CLABELS",
5365 "CRITERIA", "CATEGORIES", "TITLES",
5366 "SIGTEST", "COMPARETEST");
5370 if (!lex_match (lexer, T_SLASH))
5374 if (t->label_axis[PIVOT_AXIS_ROW] != PIVOT_AXIS_ROW
5375 && t->label_axis[PIVOT_AXIS_COLUMN] != PIVOT_AXIS_COLUMN)
5377 msg (SE, _("ROWLABELS and COLLABELS may not both be specified."));
5381 if (!ctables_prepare_table (t))
5384 while (lex_token (lexer) != T_ENDCMD);
5386 bool ok = ctables_execute (ds, ct);
5387 ctables_destroy (ct);
5388 return ok ? CMD_SUCCESS : CMD_FAILURE;
5391 ctables_destroy (ct);