more work
[pspp] / src / language / stats / ctables.c
1 /* PSPP - a program for statistical analysis.
2    Copyright (C) 2021 Free Software Foundation, Inc.
3
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.
8
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.
13
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/>. */
16
17 #include <config.h>
18
19 #include "data/dataset.h"
20 #include "data/dictionary.h"
21 #include "data/mrset.h"
22 #include "language/command.h"
23 #include "language/lexer/format-parser.h"
24 #include "language/lexer/lexer.h"
25 #include "language/lexer/variable-parser.h"
26 #include "libpspp/assertion.h"
27 #include "libpspp/hmap.h"
28 #include "libpspp/message.h"
29 #include "output/pivot-table.h"
30
31 #include "gl/minmax.h"
32 #include "gl/xalloc.h"
33
34 #include "gettext.h"
35 #define _(msgid) gettext (msgid)
36 #define N_(msgid) (msgid)
37
38 enum ctables_vlabel
39   {
40     CTVL_DEFAULT = SETTINGS_VALUE_SHOW_DEFAULT,
41     CTVL_NAME = SETTINGS_VALUE_SHOW_VALUE,
42     CTVL_LABEL = SETTINGS_VALUE_SHOW_LABEL,
43     CTVL_BOTH = SETTINGS_VALUE_SHOW_BOTH,
44     CTVL_NONE,
45   };
46 static void UNUSED
47 ctables_vlabel_unique (enum ctables_vlabel vlabel)
48 {
49   /* This ensures that all of the values are unique. */
50   switch (vlabel)
51     {
52     case CTVL_DEFAULT:
53     case CTVL_NAME:
54     case CTVL_LABEL:
55     case CTVL_BOTH:
56     case CTVL_NONE:
57       abort ();
58     }
59 }
60
61 /* XXX:
62    - unweighted summaries (U*)
63    - lower confidence limits (*.LCL)
64    - upper confidence limits (*.UCL)
65    - standard error (*.SE)
66  */
67 #define SUMMARIES                                                       \
68     /* All variables. */                                                \
69     S(CTSF_COUNT, "COUNT", N_("Count"), CTF_COUNT, CTFA_ALL)            \
70     S(CTSF_ECOUNT, "ECOUNT", N_("Adjusted Count"), CTF_COUNT, CTFA_ALL) \
71     S(CTSF_ROWPCT_COUNT, "ROWPCT.COUNT", N_("Row %"), CTF_PERCENT, CTFA_ALL) \
72     S(CTSF_COLPCT_COUNT, "COLPCT.COUNT", N_("Column %"), CTF_PERCENT, CTFA_ALL) \
73     S(CTSF_TABLEPCT_COUNT, "TABLEPCT.COUNT", N_("Table %"), CTF_PERCENT, CTFA_ALL) \
74     S(CTSF_SUBTABLEPCT_COUNT, "SUBTABLEPCT.COUNT", N_("Subtable %"), CTF_PERCENT, CTFA_ALL) \
75     S(CTSF_LAYERPCT_COUNT, "LAYERPCT.COUNT", N_("Layer %"), CTF_PERCENT, CTFA_ALL) \
76     S(CTSF_LAYERROWPCT_COUNT, "LAYERROWPCT.COUNT", N_("Layer Row %"), CTF_PERCENT, CTFA_ALL) \
77     S(CTSF_LAYERCOLPCT_COUNT, "LAYERCOLPCT.COUNT", N_("Layer Column %"), CTF_PERCENT, CTFA_ALL) \
78     S(CTSF_ROWPCT_VALIDN, "ROWPCT.VALIDN", N_("Row Valid N %"), CTF_PERCENT, CTFA_ALL) \
79     S(CTSF_COLPCT_VALIDN, "COLPCT.VALIDN", N_("Column Valid N %"), CTF_PERCENT, CTFA_ALL) \
80     S(CTSF_TABLEPCT_VALIDN, "TABLEPCT.VALIDN", N_("Table Valid N %"), CTF_PERCENT, CTFA_ALL) \
81     S(CTSF_SUBTABLEPCT_VALIDN, "SUBTABLEPCT.VALIDN", N_("Subtable Valid N %"), CTF_PERCENT, CTFA_ALL) \
82     S(CTSF_LAYERPCT_VALIDN, "LAYERPCT.VALIDN", N_("Layer Valid N %"), CTF_PERCENT, CTFA_ALL) \
83     S(CTSF_LAYERROWPCT_VALIDN, "LAYERROWPCT.VALIDN", N_("Layer Row Valid N %"), CTF_PERCENT, CTFA_ALL) \
84     S(CTSF_LAYERCOLPCT_VALIDN, "LAYERCOLPCT.VALIDN", N_("Layer Column Valid N %"), CTF_PERCENT, CTFA_ALL) \
85     S(CTSF_ROWPCT_TOTALN, "ROWPCT.TOTALN", N_("Row Total N %"), CTF_PERCENT, CTFA_ALL) \
86     S(CTSF_COLPCT_TOTALN, "COLPCT.TOTALN", N_("Column Total N %"), CTF_PERCENT, CTFA_ALL) \
87     S(CTSF_TABLEPCT_TOTALN, "TABLEPCT.TOTALN", N_("Table Total N %"), CTF_PERCENT, CTFA_ALL) \
88     S(CTSF_SUBTABLEPCT_TOTALN, "SUBTABLEPCT.TOTALN", N_("Subtable Total N %"), CTF_PERCENT, CTFA_ALL) \
89     S(CTSF_LAYERPCT_TOTALN, "LAYERPCT.TOTALN", N_("Layer Total N %"), CTF_PERCENT, CTFA_ALL) \
90     S(CTSF_LAYERROWPCT_TOTALN, "LAYERROWPCT.TOTALN", N_("Layer Row Total N %"), CTF_PERCENT, CTFA_ALL) \
91     S(CTSF_LAYERCOLPCT_TOTALN, "LAYERCOLPCT.TOTALN", N_("Layer Column Total N %"), CTF_PERCENT, CTFA_ALL) \
92                                                                         \
93     /* Scale variables, totals, and subtotals. */                       \
94     S(CTSF_MAXIMUM, "MAXIMUM", N_("Maximum"), CTF_GENERAL, CTFA_SCALE)  \
95     S(CTSF_MEAN, "MEAN", N_("Mean"), CTF_GENERAL, CTFA_SCALE)           \
96     S(CTSF_MEDIAN, "MEDIAN", N_("Median"), CTF_GENERAL, CTFA_SCALE)     \
97     S(CTSF_MINIMUM, "MINIMUM", N_("Minimum"), CTF_GENERAL, CTFA_SCALE)  \
98     S(CTSF_MISSING, "MISSING", N_("Missing"), CTF_GENERAL, CTFA_SCALE)  \
99     S(CTSF_MODE, "MODE", N_("Mode"), CTF_GENERAL, CTFA_SCALE)           \
100     S(CTSF_PTILE, "PTILE", N_("Percentile"), CTF_GENERAL, CTFA_SCALE)   \
101     S(CTSF_RANGE, "RANGE", N_("Range"), CTF_GENERAL, CTFA_SCALE)        \
102     S(CTSF_SEMEAN, "SEMEAN", N_("Std Error of Mean"), CTF_GENERAL, CTFA_SCALE) \
103     S(CTSF_STDDEV, "STDDEV", N_("Std Deviation"), CTF_GENERAL, CTFA_SCALE) \
104     S(CTSF_SUM, "SUM", N_("Sum"), CTF_GENERAL, CTFA_SCALE)              \
105     S(CSTF_TOTALN, "TOTALN", N_("Total N"), CTF_COUNT, CTFA_SCALE)      \
106     S(CTSF_ETOTALN, "ETOTALN", N_("Adjusted Total N"), CTF_COUNT, CTFA_SCALE) \
107     S(CTSF_VALIDN, "VALIDN", N_("Valid N"), CTF_COUNT, CTFA_SCALE)      \
108     S(CTSF_EVALIDN, "EVALIDN", N_("Adjusted Valid N"), CTF_COUNT, CTFA_SCALE) \
109     S(CTSF_VARIANCE, "VARIANCE", N_("Variance"), CTF_GENERAL, CTFA_SCALE) \
110     S(CTSF_ROWPCT_SUM, "ROWPCT.SUM", N_("Row Sum %"), CTF_PERCENT, CTFA_SCALE) \
111     S(CTSF_COLPCT_SUM, "COLPCT.SUM", N_("Column Sum %"), CTF_PERCENT, CTFA_SCALE) \
112     S(CTSF_TABLEPCT_SUM, "TABLEPCT.SUM", N_("Table Sum %"), CTF_PERCENT, CTFA_SCALE) \
113     S(CTSF_SUBTABLEPCT_SUM, "SUBTABLEPCT.SUM", N_("Subtable Sum %"), CTF_PERCENT, CTFA_SCALE) \
114     S(CTSF_LAYERPCT_SUM, "LAYERPCT.SUM", N_("Layer Sum %"), CTF_PERCENT, CTFA_SCALE) \
115     S(CTSF_LAYERROWPCT_SUM, "LAYERROWPCT.SUM", N_("Layer Row Sum %"), CTF_PERCENT, CTFA_SCALE) \
116     S(CTSF_LAYERCOLPCT_SUM, "LAYERCOLPCT.SUM", N_("Layer Column Sum %"), CTF_PERCENT, CTFA_SCALE) \
117                                                                         \
118     /* Multiple response sets. */                                       \
119   S(CTSF_RESPONSES, "RESPONSES", N_("Responses"), CTF_COUNT, CTFA_MRSETS) \
120     S(CTSF_ROWPCT_RESPONSES, "ROWPCT.RESPONSES", N_("Row Responses %"), CTF_PERCENT, CTFA_MRSETS) \
121     S(CTSF_COLPCT_RESPONSES, "COLPCT.RESPONSES", N_("Column Responses %"), CTF_PERCENT, CTFA_MRSETS) \
122     S(CTSF_TABLEPCT_RESPONSES, "TABLEPCT.RESPONSES", N_("Table Responses %"), CTF_PERCENT, CTFA_MRSETS) \
123     S(CTSF_SUBTABLEPCT_RESPONSES, "SUBTABLEPCT.RESPONSES", N_("Subtable Responses %"), CTF_PERCENT, CTFA_MRSETS) \
124     S(CTSF_LAYERPCT_RESPONSES, "LAYERPCT.RESPONSES", N_("Layer Responses %"), CTF_PERCENT, CTFA_MRSETS) \
125     S(CTSF_LAYERROWPCT_RESPONSES, "LAYERROWPCT.RESPONSES", N_("Layer Row Responses %"), CTF_PERCENT, CTFA_MRSETS) \
126     S(CTSF_LAYERCOLPCT_RESPONSES, "LAYERCOLPCT.RESPONSES", N_("Layer Column Responses %"), CTF_PERCENT, CTFA_MRSETS) \
127     S(CTSF_ROWPCT_RESPONSES_COUNT, "ROWPCT.RESPONSES.COUNT", N_("Row Responses % (Base: Count)"), CTF_PERCENT, CTFA_MRSETS) \
128     S(CTSF_COLPCT_RESPONSES_COUNT, "COLPCT.RESPONSES.COUNT", N_("Column Responses % (Base: Count)"), CTF_PERCENT, CTFA_MRSETS) \
129     S(CTSF_TABLEPCT_RESPONSES_COUNT, "TABLEPCT.RESPONSES.COUNT", N_("Table Responses % (Base: Count)"), CTF_PERCENT, CTFA_MRSETS) \
130     S(CTSF_SUBTABLEPCT_RESPONSES_COUNT, "SUBTABLEPCT.RESPONSES.COUNT", N_("Subtable Responses % (Base: Count)"), CTF_PERCENT, CTFA_MRSETS) \
131     S(CTSF_LAYERPCT_RESPONSES_COUNT, "LAYERPCT.RESPONSES.COUNT", N_("Layer Responses % (Base: Count)"), CTF_PERCENT, CTFA_MRSETS) \
132     S(CTSF_LAYERROWPCT_RESPONSES_COUNT, "LAYERROWPCT.RESPONSES.COUNT", N_("Layer Row Responses % (Base: Count)"), CTF_PERCENT, CTFA_MRSETS) \
133     S(CTSF_LAYERCOLPCT_RESPONSES_COUNT, "LAYERCOLPCT.RESPONSES.COUNT", N_("Layer Column Responses % (Base: Count)"), CTF_PERCENT, CTFA_MRSETS) \
134     S(CTSF_ROWPCT_COUNT_RESPONSES, "ROWPCT.COUNT.RESPONSES", N_("Row Count % (Base: Responses)"), CTF_PERCENT, CTFA_MRSETS) \
135     S(CTSF_COLPCT_COUNT_RESPONSES, "COLPCT.COUNT.RESPONSES", N_("Column Count % (Base: Responses)"), CTF_PERCENT, CTFA_MRSETS) \
136     S(CTSF_TABLEPCT_COUNT_RESPONSES, "TABLEPCT.COUNT.RESPONSES", N_("Table Count % (Base: Responses)"), CTF_PERCENT, CTFA_MRSETS) \
137     S(CTSF_SUBTABLEPCT_COUNT_RESPONSES, "SUBTABLEPCT.COUNT.RESPONSES", N_("Subtable Count % (Base: Responses)"), CTF_PERCENT, CTFA_MRSETS) \
138     S(CTSF_LAYERPCT_COUNT_RESPONSES, "LAYERPCT.COUNT.RESPONSES", N_("Layer Count % (Base: Responses)"), CTF_PERCENT, CTFA_MRSETS) \
139     S(CTSF_LAYERROWPCT_COUNT_RESPONSES, "LAYERROWPCT.COUNT.RESPONSES", N_("Layer Row Count % (Base: Responses)"), CTF_PERCENT, CTFA_MRSETS) \
140     S(CTSF_LAYERCOLPCT_COUNT_RESPONSES, "LAYERCOLPCT.RESPONSES.COUNT", N_("Layer Column Count % (Base: Responses)"), CTF_PERCENT, CTFA_MRSETS)
141
142 enum ctables_summary_function
143   {
144 #define S(ENUM, NAME, LABEL, FORMAT, AVAILABILITY) ENUM,
145     SUMMARIES
146 #undef S
147   };
148
149 enum {
150 #define S(ENUM, NAME, LABEL, FORMAT, AVAILABILITY) +1
151   N_CTSF_FUNCTIONS = SUMMARIES
152 #undef S
153 };
154
155 struct ctables
156   {
157     struct pivot_table_look *look;
158
159     /* If this is NULL, zeros are displayed using the normal print format.
160        Otherwise, this string is displayed. */
161     char *zero;
162
163     /* If this is NULL, missing values are displayed using the normal print
164        format.  Otherwise, this string is displayed. */
165     char *missing;
166
167     /* Indexed by variable dictionary index. */
168     enum ctables_vlabel *vlabels;
169
170     bool mrsets_count_duplicates; /* MRSETS. */
171     bool smissing_listwise;       /* SMISSING. */
172     struct variable *base_weight; /* WEIGHT. */
173     int hide_threshold;           /* HIDESMALLCOUNTS. */
174
175     struct ctables_table *tables;
176     size_t n_tables;
177   };
178
179 struct ctables_postcompute
180   {
181     struct hmap_node hmap_node; /* In struct ctables's 'pcompute' hmap. */
182     const char *name;           /* Name, without leading &. */
183
184     struct ctables_postcompute_expr *expr;
185     char *label;
186     /* XXX FORMAT */
187     bool hide_source_cats;
188   };
189
190 struct ctables_postcompute_expr
191   {
192     enum ctables_postcompute_op
193       {
194         /* Terminals. */
195         CTPO_CAT_NUMBER,
196         CTPO_CAT_STRING,
197         CTPO_CAT_RANGE,
198         CTPO_CAT_MISSING,
199         /* XXX OTHERNM */
200         /* XXX SUBTOTAL and HSUBTOTAL */
201
202         /* Nonterminals. */
203         CTPO_ADD,
204         CTPO_SUB,
205         CTPO_MUL,
206         CTPO_DIV,
207         CTPO_POW,
208       }
209     op;
210
211     union
212       {
213         /* CTPO_CAT_NUMBER, CTPO_NUMBER. */
214         double number;
215
216         /* CTPO_CAT_RANGE.
217
218            XXX what about string ranges? */
219         double range[2];
220
221         /* CTPO_ADD, CTPO_SUB, CTPO_MUL, CTPO_DIV, CTPO_POW. */
222         struct ctables_postcompute_expr *subs[2];
223       };
224   };
225
226 enum ctables_label_position
227   {
228     CTLP_NORMAL,
229     CTLP_OPPOSITE,
230     CTLP_LAYER,
231   };
232
233 struct ctables_table
234   {
235     struct ctables_axis *axes[PIVOT_N_AXES];
236
237     enum pivot_axis_type slabels_position;
238     bool slabels_visible;
239
240     enum ctables_label_position row_labels;
241     enum ctables_label_position col_labels;
242
243     /* Indexed by variable dictionary index. */
244     struct ctables_categories **categories;
245     size_t n_categories;
246
247     double cilevel;
248
249     char *caption;
250     char *corner;
251     char *title;
252
253     struct ctables_chisq *chisq;
254     struct ctables_pairwise *pairwise;
255   };
256
257 struct ctables_var
258   {
259     bool is_mrset;
260     union
261       {
262         struct variable *var;
263         const struct mrset *mrset;
264       };
265   };
266
267 static const struct fmt_spec *
268 ctables_var_get_print_format (const struct ctables_var *var)
269 {
270   return (var->is_mrset
271           ? var_get_print_format (var->mrset->vars[0])
272           : var_get_print_format (var->var));
273 }
274
275 static const char *
276 ctables_var_name (const struct ctables_var *var)
277 {
278   return var->is_mrset ? var->mrset->name : var_get_name (var->var);
279 }
280
281 struct ctables_categories
282   {
283     size_t n_refs;
284
285     /* Explicit categories. */
286     struct ctables_cat_value *values;
287     size_t n_values;
288
289     /* Implicit categories. */
290     bool sort_ascending;
291     bool include_missing;
292     enum { CTCS_VALUE, CTCS_LABEL, CTCS_FUNCTION } key;
293     enum ctables_summary_function sort_func;
294     struct variable *sort_func_var;
295     double percentile;
296
297     /* Totals. */
298     bool show_totals;
299     bool totals_before;
300     char *total_label;
301
302     /* Empty categories. */
303     bool show_empty;
304   };
305
306 struct ctables_cat_value
307   {
308     enum ctables_cat_value_type
309       {
310         CCVT_NUMBER,
311         CCVT_STRING,
312         CCVT_RANGE,
313         CCVT_MISSING,
314         CCVT_OTHERNM,
315         CCVT_SUBTOTAL,
316         CCVT_HSUBTOTAL,
317       }
318     type;
319
320     union
321       {
322         double number;          /* CCVT_NUMBER. */
323         char *string;           /* CCVT_STRING. */
324         double range[2];        /* CCVT_RANGE. */
325         char *subtotal_label;   /* CCVT_SUBTOTAL, CCVT_HSUBTOTAL. */
326       };
327   };
328
329 static void
330 ctables_cat_value_uninit (struct ctables_cat_value *cv)
331 {
332   if (!cv)
333     return;
334
335   switch (cv->type)
336     {
337     case CCVT_NUMBER:
338     case CCVT_RANGE:
339     case CCVT_MISSING:
340     case CCVT_OTHERNM:
341       break;
342
343     case CCVT_STRING:
344       free (cv->string);
345       break;
346
347     case CCVT_SUBTOTAL:
348     case CCVT_HSUBTOTAL:
349       free (cv->subtotal_label);
350     }
351 }
352
353 static void
354 ctables_categories_unref (struct ctables_categories *c)
355 {
356   if (!c)
357     return;
358
359   assert (c->n_refs > 0);
360   if (--c->n_refs)
361     return;
362
363   for (size_t i = 0; i < c->n_values; i++)
364     ctables_cat_value_uninit (&c->values[i]);
365   free (c->values);
366   free (c->total_label);
367   free (c);
368 }
369
370 /* Chi-square test (SIGTEST). */
371 struct ctables_chisq
372   {
373     double alpha;
374     bool include_mrsets;
375     bool all_visible;
376   };
377
378 /* Pairwise comparison test (COMPARETEST). */
379 struct ctables_pairwise
380   {
381     enum { PROP, MEAN } type;
382     double alpha[2];
383     bool include_mrsets;
384     bool meansvariance_allcats;
385     bool all_visible;
386     enum { BONFERRONI = 1, BH } adjust;
387     bool merge;
388     bool apa_style;
389     bool show_sig;
390   };
391
392 struct ctables_axis
393   {
394     enum ctables_axis_op
395       {
396         /* Terminals. */
397         CTAO_VAR,
398
399         /* Nonterminals. */
400         CTAO_STACK,             /* + */
401         CTAO_NEST,              /* > */
402       }
403     op;
404
405     union
406       {
407         /* Terminals. */
408         struct
409           {
410             struct ctables_var var;
411             bool scale;
412             struct ctables_summary *summaries;
413             size_t n_summaries;
414             size_t allocated_summaries;
415           };
416
417         /* Nonterminals. */
418         struct ctables_axis *subs[2];
419       };
420
421     struct msg_location *loc;
422   };
423
424 static void ctables_axis_destroy (struct ctables_axis *);
425
426 enum ctables_format
427   {
428     CTF_COUNT,
429     CTF_PERCENT,
430     CTF_GENERAL
431   };
432
433 enum ctables_function_availability
434   {
435     CTFA_ALL,                /* Any variables. */
436     CTFA_SCALE,              /* Only scale variables, totals, and subtotals. */
437     CTFA_MRSETS,             /* Only multiple-response sets */
438   };
439
440 struct ctables_summary
441   {
442     enum ctables_summary_function function;
443     double percentile;          /* CTSF_PTILE only. */
444     char *label;
445     struct fmt_spec format;     /* XXX extra CTABLES formats */
446   };
447
448 static void
449 ctables_summary_uninit (struct ctables_summary *s)
450 {
451   if (s)
452     free (s->label);
453 }
454
455 static bool
456 parse_col_width (struct lexer *lexer, const char *name, double *width)
457 {
458   lex_match (lexer, T_EQUALS);
459   if (lex_match_id (lexer, "DEFAULT"))
460     *width = SYSMIS;
461   else if (lex_force_num_range_closed (lexer, name, 0, DBL_MAX))
462     {
463       *width = lex_number (lexer);
464       lex_get (lexer);
465     }
466   else
467     return false;
468
469   return true;
470 }
471
472 static bool
473 parse_bool (struct lexer *lexer, bool *b)
474 {
475   if (lex_match_id (lexer, "NO"))
476     *b = false;
477   else if (lex_match_id (lexer, "YES"))
478     *b = true;
479   else
480     {
481       lex_error_expecting (lexer, "YES", "NO");
482       return false;
483     }
484   return true;
485 }
486
487 static enum ctables_function_availability
488 ctables_function_availability (enum ctables_summary_function f)
489 {
490   static enum ctables_function_availability availability[] = {
491 #define S(ENUM, NAME, LABEL, FORMAT, AVAILABILITY) [ENUM] = AVAILABILITY,
492     SUMMARIES
493 #undef S
494   };
495
496   return availability[f];
497 }
498
499 static bool
500 parse_ctables_summary_function (struct lexer *lexer,
501                                 enum ctables_summary_function *f)
502 {
503   struct pair
504     {
505       enum ctables_summary_function function;
506       struct substring name;
507     };
508   static struct pair names[] = {
509 #define S(ENUM, NAME, LABEL, FORMAT, AVAILABILITY) \
510     { ENUM, SS_LITERAL_INITIALIZER (NAME) },
511     SUMMARIES
512
513     /* The .COUNT suffix may be omitted. */
514     S(CTSF_ROWPCT_COUNT, "ROWPCT", _, _, _)
515     S(CTSF_COLPCT_COUNT, "COLPCT", _, _, _)
516     S(CTSF_TABLEPCT_COUNT, "TABLEPCT", _, _, _)
517     S(CTSF_SUBTABLEPCT_COUNT, "SUBTABLEPCT", _, _, _)
518     S(CTSF_LAYERPCT_COUNT, "LAYERPCT", _, _, _)
519     S(CTSF_LAYERROWPCT_COUNT, "LAYERROWPCT", _, _, _)
520     S(CTSF_LAYERCOLPCT_COUNT, "LAYERCOLPCT", _, _, _)
521 #undef S
522   };
523
524   if (!lex_force_id (lexer))
525     return false;
526
527   for (size_t i = 0; i < sizeof names / sizeof *names; i++)
528     if (ss_equals_case (names[i].name, lex_tokss (lexer)))
529       {
530         *f = names[i].function;
531         lex_get (lexer);
532         return true;
533       }
534
535   lex_error (lexer, _("Expecting summary function name."));
536   return false;
537 }
538
539 static void
540 ctables_axis_destroy (struct ctables_axis *axis)
541 {
542   if (!axis)
543     return;
544
545   switch (axis->op)
546     {
547     case CTAO_VAR:
548       for (size_t i = 0; i < axis->n_summaries; i++)
549         ctables_summary_uninit (&axis->summaries[i]);
550       free (axis->summaries);
551       break;
552
553     case CTAO_STACK:
554     case CTAO_NEST:
555       ctables_axis_destroy (axis->subs[0]);
556       ctables_axis_destroy (axis->subs[1]);
557       break;
558     }
559   msg_location_destroy (axis->loc);
560   free (axis);
561 }
562
563 static struct ctables_axis *
564 ctables_axis_new_nonterminal (enum ctables_axis_op op,
565                               struct ctables_axis *sub0,
566                               struct ctables_axis *sub1,
567                               struct lexer *lexer, int start_ofs)
568 {
569   struct ctables_axis *axis = xmalloc (sizeof *axis);
570   *axis = (struct ctables_axis) {
571     .op = op,
572     .subs = { sub0, sub1 },
573     .loc = lex_ofs_location (lexer, start_ofs, lex_ofs (lexer) - 1),
574   };
575   return axis;
576 }
577
578 struct ctables_axis_parse_ctx
579   {
580     struct lexer *lexer;
581     struct dictionary *dict;
582     struct ctables *ct;
583     struct ctables_table *t;
584   };
585
586 static struct fmt_spec
587 ctables_summary_default_format (enum ctables_summary_function function,
588                                 const struct ctables_var *var)
589 {
590   static const enum ctables_format default_formats[] = {
591 #define S(ENUM, NAME, LABEL, FORMAT, AVAILABILITY) [ENUM] = FORMAT,
592     SUMMARIES
593 #undef S
594   };
595   switch (default_formats[function])
596     {
597     case CTF_COUNT:
598       return (struct fmt_spec) { .type = FMT_F, .w = 40 };
599
600     case CTF_PERCENT:
601       return (struct fmt_spec) { .type = FMT_PCT, .w = 40, .d = 1 };
602
603     case CTF_GENERAL:
604       return *ctables_var_get_print_format (var);
605
606     default:
607       NOT_REACHED ();
608     }
609 }
610
611 static const char *
612 ctables_summary_function_name (enum ctables_summary_function function)
613 {
614   static const char *names[] = {
615 #define S(ENUM, NAME, LABEL, FORMAT, AVAILABILITY) [ENUM] = NAME,
616     SUMMARIES
617 #undef S
618   };
619   return names[function];
620 }
621
622 static bool
623 add_summary (struct ctables_axis *axis,
624              enum ctables_summary_function function, double percentile,
625              const char *label, const struct fmt_spec *format,
626              const struct msg_location *loc)
627 {
628   if (axis->op == CTAO_VAR)
629     {
630       if (axis->n_summaries >= axis->allocated_summaries)
631         axis->summaries = x2nrealloc (axis->summaries,
632                                       &axis->allocated_summaries,
633                                       sizeof *axis->summaries);
634
635       const char *function_name = ctables_summary_function_name (function);
636       const char *var_name = ctables_var_name (&axis->var);
637       switch (ctables_function_availability (function))
638         {
639         case CTFA_MRSETS:
640           if (!axis->var.is_mrset)
641             {
642               msg_at (SE, loc, _("Summary function %s applies only to multiple "
643                                  "response sets."), function_name);
644               msg_at (SN, axis->loc, _("'%s' is not a multiple response set."),
645                       var_name);
646               return false;
647             }
648           break;
649
650         case CTFA_SCALE:
651           if (!axis->scale)
652             {
653               msg_at (SE, loc,
654                       _("Summary function %s applies only to scale variables."),
655                       function_name);
656               msg_at (SN, axis->loc, _("'%s' is not a scale variable."),
657                       var_name);
658               return false;
659             }
660           break;
661
662         case CTFA_ALL:
663           break;
664         }
665
666       struct ctables_summary *dst = &axis->summaries[axis->n_summaries++];
667       *dst = (struct ctables_summary) {
668         .function = function,
669         .percentile = percentile,
670         .label = xstrdup (label),
671         .format = (format ? *format
672                    : ctables_summary_default_format (function, &axis->var)),
673       };
674       return true;
675     }
676   else
677     {
678       for (size_t i = 0; i < 2; i++)
679         if (!add_summary (axis->subs[i], function, percentile, label, format,
680                           loc))
681           return false;
682       return true;
683     }
684 }
685
686 static struct ctables_axis *ctables_axis_parse_stack (
687   struct ctables_axis_parse_ctx *);
688
689 static bool
690 ctables_var_parse (struct lexer *lexer, struct dictionary *dict,
691                    struct ctables_var *var)
692 {
693   if (ss_starts_with (lex_tokss (lexer), ss_cstr ("$")))
694     {
695       *var = (struct ctables_var) {
696         .is_mrset = true,
697         .mrset = dict_lookup_mrset (dict, lex_tokcstr (lexer))
698       };
699       if (!var->mrset)
700         {
701           lex_error (lexer, _("'%s' does not name a multiple-response set "
702                               "in the active file dictionary."),
703                      lex_tokcstr (lexer));
704           return false;
705         }
706       lex_get (lexer);
707       return true;
708     }
709   else
710     {
711       *var = (struct ctables_var) {
712         .is_mrset = false,
713         .var = parse_variable (lexer, dict),
714       };
715       return var->var != NULL;
716     }
717 }
718
719 static struct ctables_axis *
720 ctables_axis_parse_primary (struct ctables_axis_parse_ctx *ctx)
721 {
722   if (lex_match (ctx->lexer, T_LPAREN))
723     {
724       struct ctables_axis *sub = ctables_axis_parse_stack (ctx);
725       if (!sub || !lex_force_match (ctx->lexer, T_RPAREN))
726         {
727           ctables_axis_destroy (sub);
728           return NULL;
729         }
730       return sub;
731     }
732
733   if (!lex_force_id (ctx->lexer))
734     return NULL;
735
736   int start_ofs = lex_ofs (ctx->lexer);
737   struct ctables_var var;
738   if (!ctables_var_parse (ctx->lexer, ctx->dict, &var))
739     return NULL;
740
741   struct ctables_axis *axis = xmalloc (sizeof *axis);
742   *axis = (struct ctables_axis) { .op = CTAO_VAR, .var = var };
743
744   /* XXX should figure out default measures by reading data */
745   axis->scale = (var.is_mrset ? false
746                  : lex_match_phrase (ctx->lexer, "[S]") ? true
747                  : lex_match_phrase (ctx->lexer, "[C]") ? false
748                  : var_get_measure (var.var) == MEASURE_SCALE);
749   axis->loc = lex_ofs_location (ctx->lexer, start_ofs,
750                                 lex_ofs (ctx->lexer) - 1);
751   return axis;
752 }
753
754 static struct ctables_axis *
755 ctables_axis_parse_postfix (struct ctables_axis_parse_ctx *ctx)
756 {
757   struct ctables_axis *sub = ctables_axis_parse_primary (ctx);
758   if (!sub || !lex_match (ctx->lexer, T_LBRACK))
759     return sub;
760
761   do
762     {
763       int start_ofs = lex_ofs (ctx->lexer);
764
765       /* Parse function. */
766       enum ctables_summary_function function;
767       if (!parse_ctables_summary_function (ctx->lexer, &function))
768         goto error;
769
770       /* Parse percentile. */
771       double percentile = 0;
772       if (function == CTSF_PTILE)
773         {
774           if (!lex_force_num_range_closed (ctx->lexer, "PTILE", 0, 100))
775             goto error;
776           percentile = lex_number (ctx->lexer);
777           lex_get (ctx->lexer);
778         }
779
780       /* Parse label. */
781       char *label;
782       if (lex_is_string (ctx->lexer))
783         {
784           label = ss_xstrdup (lex_tokss (ctx->lexer));
785           lex_get (ctx->lexer);
786         }
787       else if (function == CTSF_PTILE)
788         label = xasprintf (_("Percentile %.2f"), percentile);
789       else
790         {
791           static const char *default_labels[] = {
792 #define S(ENUM, NAME, LABEL, FORMAT, AVAILABILITY) [ENUM] = LABEL,
793             SUMMARIES
794 #undef S
795           };
796           label = xstrdup (gettext (default_labels[function]));
797         }
798
799       /* Parse format. */
800       struct fmt_spec format;
801       const struct fmt_spec *formatp;
802       if (lex_token (ctx->lexer) == T_ID)
803         {
804           if (!parse_format_specifier (ctx->lexer, &format)
805               || !fmt_check_output (&format)
806               || !fmt_check_type_compat (&format, VAL_NUMERIC))
807             {
808               free (label);
809               goto error;
810             }
811           formatp = &format;
812         }
813       else
814         formatp = NULL;
815
816       struct msg_location *loc = lex_ofs_location (ctx->lexer, start_ofs,
817                                                    lex_ofs (ctx->lexer) - 1);
818       add_summary (sub, function, percentile, label, formatp, loc);
819       free (label);
820       msg_location_destroy (loc);
821
822       lex_match (ctx->lexer, T_COMMA);
823     }
824   while (!lex_match (ctx->lexer, T_RBRACK));
825
826   return sub;
827
828 error:
829   ctables_axis_destroy (sub);
830   return NULL;
831 }
832
833 static const struct ctables_axis *
834 find_scale (const struct ctables_axis *axis)
835 {
836   if (!axis)
837     return NULL;
838   else if (axis->op == CTAO_VAR)
839     {
840       if (axis->scale)
841         {
842           assert (!axis->var.is_mrset);
843           return axis;
844         }
845       else
846         return NULL;
847     }
848   else
849     {
850       for (size_t i = 0; i < 2; i++)
851         {
852           const struct ctables_axis *scale = find_scale (axis->subs[i]);
853           if (scale)
854             return scale;
855         }
856       return NULL;
857     }
858 }
859
860 static const struct ctables_axis *
861 find_categorical_summary (const struct ctables_axis *axis)
862 {
863   if (!axis)
864     return NULL;
865   else if (axis->op == CTAO_VAR)
866     return !axis->scale && axis->n_summaries ? axis : NULL;
867   else
868     {
869       for (size_t i = 0; i < 2; i++)
870         {
871           const struct ctables_axis *sum
872             = find_categorical_summary (axis->subs[i]);
873           if (sum)
874             return sum;
875         }
876       return NULL;
877     }
878 }
879
880 static struct ctables_axis *
881 ctables_axis_parse_nest (struct ctables_axis_parse_ctx *ctx)
882 {
883   int start_ofs = lex_ofs (ctx->lexer);
884   struct ctables_axis *lhs = ctables_axis_parse_postfix (ctx);
885   if (!lhs)
886     return NULL;
887
888   while (lex_match (ctx->lexer, T_GT))
889     {
890       struct ctables_axis *rhs = ctables_axis_parse_postfix (ctx);
891       if (!rhs)
892         return NULL;
893
894       struct ctables_axis *nest = ctables_axis_new_nonterminal (
895         CTAO_NEST, lhs, rhs, ctx->lexer, start_ofs);
896
897       const struct ctables_axis *outer_scale = find_scale (lhs);
898       const struct ctables_axis *inner_scale = find_scale (rhs);
899       if (outer_scale && inner_scale)
900         {
901           msg_at (SE, nest->loc, _("Cannot nest scale variables."));
902           msg_at (SN, outer_scale->loc, _("This is an outer scale variable."));
903           msg_at (SN, inner_scale->loc, _("This is an inner scale variable."));
904           ctables_axis_destroy (nest);
905           return NULL;
906         }
907
908       const struct ctables_axis *outer_sum = find_categorical_summary (lhs);
909       if (outer_sum)
910         {
911           msg_at (SE, nest->loc,
912                   _("Summaries may only be requested for categorical variables "
913                     "at the innermost nesting level."));
914           msg_at (SN, outer_sum->loc,
915                   _("This outer categorical variable has a summary."));
916           ctables_axis_destroy (nest);
917           return NULL;
918         }
919
920       lhs = nest;
921     }
922
923   return lhs;
924 }
925
926 static struct ctables_axis *
927 ctables_axis_parse_stack (struct ctables_axis_parse_ctx *ctx)
928 {
929   int start_ofs = lex_ofs (ctx->lexer);
930   struct ctables_axis *lhs = ctables_axis_parse_nest (ctx);
931   if (!lhs)
932     return NULL;
933
934   while (lex_match (ctx->lexer, T_PLUS))
935     {
936       struct ctables_axis *rhs = ctables_axis_parse_nest (ctx);
937       if (!rhs)
938         return NULL;
939
940       lhs = ctables_axis_new_nonterminal (CTAO_STACK, lhs, rhs,
941                                           ctx->lexer, start_ofs);
942     }
943
944   return lhs;
945 }
946
947 static bool
948 ctables_axis_parse (struct lexer *lexer, struct dictionary *dict,
949                     struct ctables *ct, struct ctables_table *t,
950                     enum pivot_axis_type a)
951 {
952   if (lex_token (lexer) == T_BY
953       || lex_token (lexer) == T_SLASH
954       || lex_token (lexer) == T_ENDCMD)
955     return true;
956
957   struct ctables_axis_parse_ctx ctx = {
958     .lexer = lexer,
959     .dict = dict,
960     .ct = ct,
961     .t = t
962   };
963   t->axes[a] = ctables_axis_parse_stack (&ctx);
964   return t->axes[a] != NULL;
965 }
966
967 static void
968 ctables_chisq_destroy (struct ctables_chisq *chisq)
969 {
970   free (chisq);
971 }
972
973 static void
974 ctables_pairwise_destroy (struct ctables_pairwise *pairwise)
975 {
976   free (pairwise);
977 }
978
979 static void
980 ctables_table_uninit (struct ctables_table *t)
981 {
982   if (!t)
983     return;
984
985   for (size_t i = 0; i < t->n_categories; i++)
986     ctables_categories_unref (t->categories[i]);
987   free (t->categories);
988
989   ctables_axis_destroy (t->axes[PIVOT_AXIS_COLUMN]);
990   ctables_axis_destroy (t->axes[PIVOT_AXIS_ROW]);
991   ctables_axis_destroy (t->axes[PIVOT_AXIS_LAYER]);
992   free (t->caption);
993   free (t->corner);
994   free (t->title);
995   ctables_chisq_destroy (t->chisq);
996   ctables_pairwise_destroy (t->pairwise);
997 }
998
999 static void
1000 ctables_destroy (struct ctables *ct)
1001 {
1002   if (!ct)
1003     return;
1004
1005   pivot_table_look_unref (ct->look);
1006   free (ct->zero);
1007   free (ct->missing);
1008   free (ct->vlabels);
1009   for (size_t i = 0; i < ct->n_tables; i++)
1010     ctables_table_uninit (&ct->tables[i]);
1011   free (ct->tables);
1012   free (ct);
1013 }
1014
1015 static struct ctables_cat_value
1016 ccvt_range (double low, double high)
1017 {
1018   return (struct ctables_cat_value) {
1019     .type = CCVT_RANGE,
1020     .range = { low, high }
1021   };
1022 }
1023
1024 static bool
1025 ctables_table_parse_categories (struct lexer *lexer, struct dictionary *dict,
1026                                 struct ctables_table *t)
1027 {
1028   if (!lex_match_id (lexer, "VARIABLES"))
1029     return false;
1030   lex_match (lexer, T_EQUALS);
1031
1032   struct variable **vars;
1033   size_t n_vars;
1034   if (!parse_variables (lexer, dict, &vars, &n_vars, PV_NO_SCRATCH))
1035     return false;
1036
1037   struct ctables_categories *c = xmalloc (sizeof *c);
1038   *c = (struct ctables_categories) { .n_refs = n_vars };
1039   for (size_t i = 0; i < n_vars; i++)
1040     {
1041       struct ctables_categories **cp
1042         = &t->categories[var_get_dict_index (vars[i])];
1043       ctables_categories_unref (*cp);
1044       *cp = c;
1045     }
1046   free (vars);
1047
1048   if (lex_match (lexer, T_LBRACK))
1049     {
1050       size_t allocated_values = 0;
1051       do
1052         {
1053           if (c->n_values >= allocated_values)
1054             c->values = x2nrealloc (c->values, &allocated_values,
1055                                     sizeof *c->values);
1056
1057           struct ctables_cat_value *v = &c->values[c->n_values];
1058           if (lex_match_id (lexer, "OTHERNM"))
1059             v->type = CCVT_OTHERNM;
1060           else if (lex_match_id (lexer, "MISSING"))
1061             v->type = CCVT_MISSING;
1062           else if (lex_match_id (lexer, "SUBTOTAL"))
1063             *v = (struct ctables_cat_value)
1064               { .type = CCVT_SUBTOTAL, .subtotal_label = NULL };
1065           else if (lex_match_id (lexer, "HSUBTOTAL"))
1066             *v = (struct ctables_cat_value)
1067               { .type = CCVT_HSUBTOTAL, .subtotal_label = NULL };
1068           else if (lex_match_id (lexer, "LO"))
1069             {
1070               if (!lex_force_match_id (lexer, "THRU") || lex_force_num (lexer))
1071                 return false;
1072               *v = ccvt_range (-DBL_MAX, lex_number (lexer));
1073               lex_get (lexer);
1074             }
1075           else if (lex_is_number (lexer))
1076             {
1077               double number = lex_number (lexer);
1078               lex_get (lexer);
1079               if (lex_match_id (lexer, "THRU"))
1080                 {
1081                   v->type = CCVT_RANGE;
1082                   v->range[0] = number;
1083                   if (lex_match_id (lexer, "HI"))
1084                     *v = ccvt_range (number, DBL_MAX);
1085                   else
1086                     {
1087                       if (!lex_force_num (lexer))
1088                         return false;
1089                       *v = ccvt_range (number, lex_number (lexer));
1090                       lex_get (lexer);
1091                     }
1092                 }
1093               else
1094                 *v = (struct ctables_cat_value) {
1095                   .type = CCVT_NUMBER,
1096                   .number = number
1097                 };
1098             }
1099           else if (lex_is_string (lexer))
1100             {
1101               *v = (struct ctables_cat_value) {
1102                 .type = CCVT_STRING,
1103                 .string = ss_xstrdup (lex_tokss (lexer)),
1104               };
1105               lex_get (lexer);
1106             }
1107           else
1108             {
1109               lex_error (lexer, NULL);
1110               return false;
1111             }
1112
1113           if ((v->type == CCVT_SUBTOTAL || v->type == CCVT_HSUBTOTAL)
1114               && lex_match (lexer, T_EQUALS))
1115             {
1116               if (!lex_force_string (lexer))
1117                 return false;
1118
1119               v->subtotal_label = ss_xstrdup (lex_tokss (lexer));
1120               lex_get (lexer);
1121             }
1122
1123           c->n_values++;
1124           lex_match (lexer, T_COMMA);
1125         }
1126       while (!lex_match (lexer, T_RBRACK));
1127     }
1128
1129   while (lex_token (lexer) != T_SLASH && lex_token (lexer) != T_ENDCMD)
1130     {
1131       if (!c->n_values && lex_match_id (lexer, "ORDER"))
1132         {
1133           lex_match (lexer, T_EQUALS);
1134           if (lex_match_id (lexer, "A"))
1135             c->sort_ascending = true;
1136           else if (lex_match_id (lexer, "D"))
1137             c->sort_ascending = false;
1138           else
1139             {
1140               lex_error_expecting (lexer, "A", "D");
1141               return false;
1142             }
1143         }
1144       else if (!c->n_values && lex_match_id (lexer, "KEY"))
1145         {
1146           lex_match (lexer, T_EQUALS);
1147           if (lex_match_id (lexer, "VALUE"))
1148             c->key = CTCS_VALUE;
1149           else if (lex_match_id (lexer, "LABEL"))
1150             c->key = CTCS_LABEL;
1151           else
1152             {
1153               c->key = CTCS_FUNCTION;
1154               if (!parse_ctables_summary_function (lexer, &c->sort_func))
1155                 return false;
1156
1157               if (lex_match (lexer, T_LPAREN))
1158                 {
1159                   c->sort_func_var = parse_variable (lexer, dict);
1160                   if (!c->sort_func_var)
1161                     return false;
1162
1163                   if (c->sort_func == CTSF_PTILE)
1164                     {
1165                       lex_match (lexer, T_COMMA);
1166                       if (!lex_force_num_range_closed (lexer, "PTILE", 0, 100))
1167                         return false;
1168                       c->percentile = lex_number (lexer);
1169                       lex_get (lexer);
1170                     }
1171
1172                   if (!lex_force_match (lexer, T_RPAREN))
1173                     return false;
1174                 }
1175               else if (ctables_function_availability (c->sort_func)
1176                        == CTFA_SCALE)
1177                 {
1178                   bool UNUSED b = lex_force_match (lexer, T_LPAREN);
1179                   return false;
1180                 }
1181             }
1182         }
1183       else if (!c->n_values && lex_match_id (lexer, "MISSING"))
1184         {
1185           lex_match (lexer, T_EQUALS);
1186           if (lex_match_id (lexer, "INCLUDE"))
1187             c->include_missing = true;
1188           else if (lex_match_id (lexer, "EXCLUDE"))
1189             c->include_missing = false;
1190           else
1191             {
1192               lex_error_expecting (lexer, "INCLUDE", "EXCLUDE");
1193               return false;
1194             }
1195         }
1196       else if (lex_match_id (lexer, "TOTAL"))
1197         {
1198           lex_match (lexer, T_EQUALS);
1199           if (!parse_bool (lexer, &c->show_totals))
1200             return false;
1201         }
1202       else if (lex_match_id (lexer, "LABEL"))
1203         {
1204           lex_match (lexer, T_EQUALS);
1205           if (!lex_force_string (lexer))
1206             return false;
1207           free (c->total_label);
1208           c->total_label = ss_xstrdup (lex_tokss (lexer));
1209           lex_get (lexer);
1210         }
1211       else if (lex_match_id (lexer, "POSITION"))
1212         {
1213           lex_match (lexer, T_EQUALS);
1214           if (lex_match_id (lexer, "BEFORE"))
1215             c->totals_before = true;
1216           else if (lex_match_id (lexer, "AFTER"))
1217             c->totals_before = false;
1218           else
1219             {
1220               lex_error_expecting (lexer, "BEFORE", "AFTER");
1221               return false;
1222             }
1223         }
1224       else if (lex_match_id (lexer, "EMPTY"))
1225         {
1226           lex_match (lexer, T_EQUALS);
1227           if (lex_match_id (lexer, "INCLUDE"))
1228             c->show_empty = true;
1229           else if (lex_match_id (lexer, "EXCLUDE"))
1230             c->show_empty = false;
1231           else
1232             {
1233               lex_error_expecting (lexer, "INCLUDE", "EXCLUDE");
1234               return false;
1235             }
1236         }
1237       else
1238         {
1239           if (!c->n_values)
1240             lex_error_expecting (lexer, "ORDER", "KEY", "MISSING",
1241                                  "TOTAL", "LABEL", "POSITION", "EMPTY");
1242           else
1243             lex_error_expecting (lexer, "TOTAL", "LABEL", "POSITION", "EMPTY");
1244           return false;
1245         }
1246     }
1247   return true;
1248 }
1249
1250 int
1251 cmd_ctables (struct lexer *lexer, struct dataset *ds)
1252 {
1253   size_t n_vars = dict_get_n_vars (dataset_dict (ds));
1254   enum ctables_vlabel *vlabels = xnmalloc (n_vars, sizeof *vlabels);
1255   for (size_t i = 0; i < n_vars; i++)
1256     vlabels[i] = CTVL_DEFAULT;
1257
1258   struct ctables *ct = xmalloc (sizeof *ct);
1259   *ct = (struct ctables) {
1260     .look = pivot_table_look_unshare (pivot_table_look_ref (
1261                                         pivot_table_look_get_default ())),
1262     .vlabels = vlabels,
1263     .hide_threshold = 5,
1264   };
1265
1266   if (!lex_force_match (lexer, T_SLASH))
1267     goto error;
1268
1269   while (!lex_match_id (lexer, "TABLE"))
1270     {
1271       if (lex_match_id (lexer, "FORMAT"))
1272         {
1273           double widths[2] = { SYSMIS, SYSMIS };
1274           double units_per_inch = 72.0;
1275
1276           while (lex_token (lexer) != T_SLASH)
1277             {
1278               if (lex_match_id (lexer, "MINCOLWIDTH"))
1279                 {
1280                   if (!parse_col_width (lexer, "MINCOLWIDTH", &widths[0]))
1281                     goto error;
1282                 }
1283               else if (lex_match_id (lexer, "MAXCOLWIDTH"))
1284                 {
1285                   if (!parse_col_width (lexer, "MAXCOLWIDTH", &widths[1]))
1286                     goto error;
1287                 }
1288               else if (lex_match_id (lexer, "UNITS"))
1289                 {
1290                   lex_match (lexer, T_EQUALS);
1291                   if (lex_match_id (lexer, "POINTS"))
1292                     units_per_inch = 72.0;
1293                   else if (lex_match_id (lexer, "INCHES"))
1294                     units_per_inch = 1.0;
1295                   else if (lex_match_id (lexer, "CM"))
1296                     units_per_inch = 2.54;
1297                   else
1298                     {
1299                       lex_error_expecting (lexer, "POINTS", "INCHES", "CM");
1300                       goto error;
1301                     }
1302                 }
1303               else if (lex_match_id (lexer, "EMPTY"))
1304                 {
1305                   free (ct->zero);
1306                   ct->zero = NULL;
1307
1308                   lex_match (lexer, T_EQUALS);
1309                   if (lex_match_id (lexer, "ZERO"))
1310                     {
1311                       /* Nothing to do. */
1312                     }
1313                   else if (lex_match_id (lexer, "BLANK"))
1314                     ct->zero = xstrdup ("");
1315                   else if (lex_force_string (lexer))
1316                     {
1317                       ct->zero = ss_xstrdup (lex_tokss (lexer));
1318                       lex_get (lexer);
1319                     }
1320                   else
1321                     goto error;
1322                 }
1323               else if (lex_match_id (lexer, "MISSING"))
1324                 {
1325                   lex_match (lexer, T_EQUALS);
1326                   if (!lex_force_string (lexer))
1327                     goto error;
1328
1329                   free (ct->missing);
1330                   ct->missing = (strcmp (lex_tokcstr (lexer), ".")
1331                                  ? ss_xstrdup (lex_tokss (lexer))
1332                                  : NULL);
1333                   lex_get (lexer);
1334                 }
1335               else
1336                 {
1337                   lex_error_expecting (lexer, "MINCOLWIDTH", "MAXCOLWIDTH",
1338                                        "UNITS", "EMPTY", "MISSING");
1339                   goto error;
1340                 }
1341             }
1342
1343           if (widths[0] != SYSMIS && widths[1] != SYSMIS
1344               && widths[0] > widths[1])
1345             {
1346               msg (SE, _("MINCOLWIDTH must not be greater than MAXCOLWIDTH."));
1347               goto error;
1348             }
1349
1350           for (size_t i = 0; i < 2; i++)
1351             if (widths[i] != SYSMIS)
1352               {
1353                 int *wr = ct->look->width_ranges[TABLE_HORZ];
1354                 wr[i] = widths[i] / units_per_inch * 96.0;
1355                 if (wr[0] > wr[1])
1356                   wr[!i] = wr[i];
1357               }
1358         }
1359       else if (lex_match_id (lexer, "VLABELS"))
1360         {
1361           if (!lex_force_match_id (lexer, "VARIABLES"))
1362             goto error;
1363           lex_match (lexer, T_EQUALS);
1364
1365           struct variable **vars;
1366           size_t n_vars;
1367           if (!parse_variables (lexer, dataset_dict (ds), &vars, &n_vars,
1368                                 PV_NO_SCRATCH))
1369             goto error;
1370
1371           if (!lex_force_match_id (lexer, "DISPLAY"))
1372             {
1373               free (vars);
1374               goto error;
1375             }
1376           lex_match (lexer, T_EQUALS);
1377
1378           enum ctables_vlabel vlabel;
1379           if (lex_match_id (lexer, "DEFAULT"))
1380             vlabel = CTVL_DEFAULT;
1381           else if (lex_match_id (lexer, "NAME"))
1382             vlabel = CTVL_NAME;
1383           else if (lex_match_id (lexer, "LABEL"))
1384             vlabel = CTVL_LABEL;
1385           else if (lex_match_id (lexer, "BOTH"))
1386             vlabel = CTVL_BOTH;
1387           else if (lex_match_id (lexer, "NONE"))
1388             vlabel = CTVL_NONE;
1389           else
1390             {
1391               lex_error_expecting (lexer, "DEFAULT", "NAME", "LABEL",
1392                                    "BOTH", "NONE");
1393               free (vars);
1394               goto error;
1395             }
1396
1397           for (size_t i = 0; i < n_vars; i++)
1398             ct->vlabels[var_get_dict_index (vars[i])] = vlabel;
1399           free (vars);
1400         }
1401       else if (lex_match_id (lexer, "MRSETS"))
1402         {
1403           if (!lex_force_match_id (lexer, "COUNTDUPLICATES"))
1404             goto error;
1405           lex_match (lexer, T_EQUALS);
1406           if (!parse_bool (lexer, &ct->mrsets_count_duplicates))
1407             goto error;
1408         }
1409       else if (lex_match_id (lexer, "SMISSING"))
1410         {
1411           if (lex_match_id (lexer, "VARIABLE"))
1412             ct->smissing_listwise = false;
1413           else if (lex_match_id (lexer, "LISTWISE"))
1414             ct->smissing_listwise = true;
1415           else
1416             {
1417               lex_error_expecting (lexer, "VARIABLE", "LISTWISE");
1418               goto error;
1419             }
1420         }
1421       /* XXX PCOMPUTE */
1422       else if (lex_match_id (lexer, "WEIGHT"))
1423         {
1424           if (!lex_force_match_id (lexer, "VARIABLE"))
1425             goto error;
1426           lex_match (lexer, T_EQUALS);
1427           ct->base_weight = parse_variable (lexer, dataset_dict (ds));
1428           if (!ct->base_weight)
1429             goto error;
1430         }
1431       else if (lex_match_id (lexer, "HIDESMALLCOUNTS"))
1432         {
1433           if (!lex_force_match_id (lexer, "COUNT"))
1434             goto error;
1435           lex_match (lexer, T_EQUALS);
1436           if (!lex_force_int_range (lexer, "HIDESMALLCOUNTS COUNT", 2, INT_MAX))
1437             goto error;
1438           ct->hide_threshold = lex_integer (lexer);
1439           lex_get (lexer);
1440         }
1441       else
1442         {
1443           lex_error_expecting (lexer, "FORMAT", "VLABELS", "MRSETS",
1444                                "SMISSING", "PCOMPUTE", "PPROPERTIES",
1445                                "WEIGHT", "HIDESMALLCOUNTS", "TABLE");
1446           goto error;
1447         }
1448
1449       if (!lex_force_match (lexer, T_SLASH))
1450         goto error;
1451     }
1452
1453   size_t allocated_tables = 0;
1454   do
1455     {
1456       if (ct->n_tables >= allocated_tables)
1457         ct->tables = x2nrealloc (ct->tables, &allocated_tables,
1458                                  sizeof *ct->tables);
1459
1460       struct ctables_table *t = &ct->tables[ct->n_tables++];
1461       *t = (struct ctables_table) {
1462         .slabels_position = PIVOT_AXIS_COLUMN,
1463         .slabels_visible = true,
1464         .row_labels = CTLP_NORMAL,
1465         .col_labels = CTLP_NORMAL,
1466         .categories = xcalloc (dict_get_n_vars (dataset_dict (ds)),
1467                                sizeof *t->categories),
1468         .n_categories = dict_get_n_vars (dataset_dict (ds)),
1469         .cilevel = 95,
1470       };
1471
1472       lex_match (lexer, T_EQUALS);
1473       if (!ctables_axis_parse (lexer, dataset_dict (ds), ct, t, PIVOT_AXIS_ROW))
1474         goto error;
1475       if (lex_match (lexer, T_BY))
1476         {
1477           if (!ctables_axis_parse (lexer, dataset_dict (ds),
1478                                    ct, t, PIVOT_AXIS_COLUMN))
1479             goto error;
1480
1481           if (lex_match (lexer, T_BY))
1482             {
1483               if (!ctables_axis_parse (lexer, dataset_dict (ds),
1484                                        ct, t, PIVOT_AXIS_LAYER))
1485                 goto error;
1486             }
1487         }
1488
1489       if (!t->axes[PIVOT_AXIS_ROW] && !t->axes[PIVOT_AXIS_COLUMN]
1490           && !t->axes[PIVOT_AXIS_LAYER])
1491         {
1492           lex_error (lexer, _("At least one variable must be specified."));
1493           goto error;
1494         }
1495
1496       const struct ctables_axis *scales[PIVOT_N_AXES];
1497       size_t n_scales = 0;
1498       for (size_t i = 0; i < 3; i++)
1499         {
1500           scales[i] = find_scale (t->axes[i]);
1501           if (scales[i])
1502             n_scales++;
1503         }
1504       if (n_scales > 1)
1505         {
1506           msg (SE, _("Scale variables may appear only on one dimension."));
1507           if (scales[PIVOT_AXIS_ROW])
1508             msg_at (SN, scales[PIVOT_AXIS_ROW]->loc,
1509                     _("This scale variable appears in the rows dimension."));
1510           if (scales[PIVOT_AXIS_COLUMN])
1511             msg_at (SN, scales[PIVOT_AXIS_COLUMN]->loc,
1512                     _("This scale variable appears in the columns dimension."));
1513           if (scales[PIVOT_AXIS_LAYER])
1514             msg_at (SN, scales[PIVOT_AXIS_LAYER]->loc,
1515                     _("This scale variable appears in the layer dimension."));
1516           goto error;
1517         }
1518
1519       if (lex_token (lexer) == T_ENDCMD)
1520         break;
1521       if (!lex_force_match (lexer, T_SLASH))
1522         break;
1523
1524       while (!lex_match_id (lexer, "TABLE") && lex_token (lexer) != T_ENDCMD)
1525         {
1526           if (lex_match_id (lexer, "SLABELS"))
1527             {
1528               while (lex_token (lexer) != T_SLASH)
1529                 {
1530                   if (lex_match_id (lexer, "POSITION"))
1531                     {
1532                       lex_match (lexer, T_EQUALS);
1533                       if (lex_match_id (lexer, "COLUMN"))
1534                         t->slabels_position = PIVOT_AXIS_COLUMN;
1535                       else if (lex_match_id (lexer, "ROW"))
1536                         t->slabels_position = PIVOT_AXIS_ROW;
1537                       else if (lex_match_id (lexer, "LAYER"))
1538                         t->slabels_position = PIVOT_AXIS_LAYER;
1539                       else
1540                         {
1541                           lex_error_expecting (lexer, "COLUMN", "ROW", "LAYER");
1542                           goto error;
1543                         }
1544                     }
1545                   else if (lex_match_id (lexer, "VISIBLE"))
1546                     {
1547                       lex_match (lexer, T_EQUALS);
1548                       if (!parse_bool (lexer, &t->slabels_visible))
1549                         goto error;
1550                     }
1551                   else
1552                     {
1553                       lex_error_expecting (lexer, "POSITION", "VISIBLE");
1554                       goto error;
1555                     }
1556                 }
1557             }
1558           else if (lex_match_id (lexer, "CLABELS"))
1559             {
1560               while (lex_token (lexer) != T_SLASH)
1561                 {
1562                   if (lex_match_id (lexer, "AUTO"))
1563                     t->row_labels = t->col_labels = CTLP_NORMAL;
1564                   else if (lex_match_id (lexer, "ROWLABELS"))
1565                     {
1566                       lex_match (lexer, T_EQUALS);
1567                       if (lex_match_id (lexer, "OPPOSITE"))
1568                         t->row_labels = CTLP_OPPOSITE;
1569                       else if (lex_match_id (lexer, "LAYER"))
1570                         t->row_labels = CTLP_LAYER;
1571                       else
1572                         {
1573                           lex_error_expecting (lexer, "OPPOSITE", "LAYER");
1574                           goto error;
1575                         }
1576                     }
1577                   else if (lex_match_id (lexer, "COLLABELS"))
1578                     {
1579                       lex_match (lexer, T_EQUALS);
1580                       if (lex_match_id (lexer, "OPPOSITE"))
1581                         t->col_labels = CTLP_OPPOSITE;
1582                       else if (lex_match_id (lexer, "LAYER"))
1583                         t->col_labels = CTLP_LAYER;
1584                       else
1585                         {
1586                           lex_error_expecting (lexer, "OPPOSITE", "LAYER");
1587                           goto error;
1588                         }
1589                     }
1590                   else
1591                     {
1592                       lex_error_expecting (lexer, "AUTO", "ROWLABELS",
1593                                            "COLLABELS");
1594                       goto error;
1595                     }
1596                 }
1597             }
1598           else if (lex_match_id (lexer, "CRITERIA"))
1599             {
1600               if (!lex_force_match_id (lexer, "CILEVEL"))
1601                 goto error;
1602               lex_match (lexer, T_EQUALS);
1603
1604               if (!lex_force_num_range_halfopen (lexer, "CILEVEL", 0, 100))
1605                 goto error;
1606               t->cilevel = lex_number (lexer);
1607               lex_get (lexer);
1608             }
1609           else if (lex_match_id (lexer, "CATEGORIES"))
1610             {
1611               if (!ctables_table_parse_categories (lexer, dataset_dict (ds), t))
1612                 goto error;
1613             }
1614           else if (lex_match_id (lexer, "TITLES"))
1615             {
1616               do
1617                 {
1618                   char **textp;
1619                   if (lex_match_id (lexer, "CAPTION"))
1620                     textp = &t->caption;
1621                   else if (lex_match_id (lexer, "CORNER"))
1622                     textp = &t->corner;
1623                   else if (lex_match_id (lexer, "TITLE"))
1624                     textp = &t->title;
1625                   else
1626                     {
1627                       lex_error_expecting (lexer, "CAPTION", "CORNER", "TITLE");
1628                       goto error;
1629                     }
1630                   lex_match (lexer, T_EQUALS);
1631
1632                   struct string s = DS_EMPTY_INITIALIZER;
1633                   while (lex_is_string (lexer))
1634                     {
1635                       if (!ds_is_empty (&s))
1636                         ds_put_byte (&s, ' ');
1637                       ds_put_substring (&s, lex_tokss (lexer));
1638                       lex_get (lexer);
1639                     }
1640                   free (*textp);
1641                   *textp = ds_steal_cstr (&s);
1642                 }
1643               while (lex_token (lexer) != T_SLASH
1644                      && lex_token (lexer) != T_ENDCMD);
1645             }
1646           else if (lex_match_id (lexer, "SIGTEST"))
1647             {
1648               if (!t->chisq)
1649                 {
1650                   t->chisq = xmalloc (sizeof *t->chisq);
1651                   *t->chisq = (struct ctables_chisq) {
1652                     .alpha = .05,
1653                     .include_mrsets = true,
1654                     .all_visible = true,
1655                   };
1656                 }
1657
1658               do
1659                 {
1660                   if (lex_match_id (lexer, "TYPE"))
1661                     {
1662                       lex_match (lexer, T_EQUALS);
1663                       if (!lex_force_match_id (lexer, "CHISQUARE"))
1664                         goto error;
1665                     }
1666                   else if (lex_match_id (lexer, "ALPHA"))
1667                     {
1668                       lex_match (lexer, T_EQUALS);
1669                       if (!lex_force_num_range_halfopen (lexer, "ALPHA", 0, 1))
1670                         goto error;
1671                       t->chisq->alpha = lex_number (lexer);
1672                       lex_get (lexer);
1673                     }
1674                   else if (lex_match_id (lexer, "INCLUDEMRSETS"))
1675                     {
1676                       lex_match (lexer, T_EQUALS);
1677                       if (parse_bool (lexer, &t->chisq->include_mrsets))
1678                         goto error;
1679                     }
1680                   else if (lex_match_id (lexer, "CATEGORIES"))
1681                     {
1682                       lex_match (lexer, T_EQUALS);
1683                       if (lex_match_id (lexer, "ALLVISIBLE"))
1684                         t->chisq->all_visible = true;
1685                       else if (lex_match_id (lexer, "SUBTOTALS"))
1686                         t->chisq->all_visible = false;
1687                       else
1688                         {
1689                           lex_error_expecting (lexer,
1690                                                "ALLVISIBLE", "SUBTOTALS");
1691                           goto error;
1692                         }
1693                     }
1694                   else
1695                     {
1696                       lex_error_expecting (lexer, "TYPE", "ALPHA",
1697                                            "INCLUDEMRSETS", "CATEGORIES");
1698                       goto error;
1699                     }
1700                 }
1701               while (lex_token (lexer) != T_SLASH
1702                      && lex_token (lexer) != T_ENDCMD);
1703             }
1704           else if (lex_match_id (lexer, "COMPARETEST"))
1705             {
1706               if (!t->pairwise)
1707                 {
1708                   t->pairwise = xmalloc (sizeof *t->pairwise);
1709                   *t->pairwise = (struct ctables_pairwise) {
1710                     .type = PROP,
1711                     .alpha = { .05, .05 },
1712                     .adjust = BONFERRONI,
1713                     .include_mrsets = true,
1714                     .meansvariance_allcats = true,
1715                     .all_visible = true,
1716                     .merge = false,
1717                     .apa_style = true,
1718                     .show_sig = false,
1719                   };
1720                 }
1721
1722               do
1723                 {
1724                   if (lex_match_id (lexer, "TYPE"))
1725                     {
1726                       lex_match (lexer, T_EQUALS);
1727                       if (lex_match_id (lexer, "PROP"))
1728                         t->pairwise->type = PROP;
1729                       else if (lex_match_id (lexer, "MEAN"))
1730                         t->pairwise->type = MEAN;
1731                       else
1732                         {
1733                           lex_error_expecting (lexer, "PROP", "MEAN");
1734                           goto error;
1735                         }
1736                     }
1737                   else if (lex_match_id (lexer, "ALPHA"))
1738                     {
1739                       lex_match (lexer, T_EQUALS);
1740
1741                       if (!lex_force_num_range_open (lexer, "ALPHA", 0, 1))
1742                         goto error;
1743                       double a0 = lex_number (lexer);
1744                       lex_get (lexer);
1745
1746                       lex_match (lexer, T_COMMA);
1747                       if (lex_is_number (lexer))
1748                         {
1749                           if (!lex_force_num_range_open (lexer, "ALPHA", 0, 1))
1750                             goto error;
1751                           double a1 = lex_number (lexer);
1752                           lex_get (lexer);
1753
1754                           t->pairwise->alpha[0] = MIN (a0, a1);
1755                           t->pairwise->alpha[1] = MAX (a0, a1);
1756                         }
1757                       else
1758                         t->pairwise->alpha[0] = t->pairwise->alpha[1] = a0;
1759                     }
1760                   else if (lex_match_id (lexer, "ADJUST"))
1761                     {
1762                       lex_match (lexer, T_EQUALS);
1763                       if (lex_match_id (lexer, "BONFERRONI"))
1764                         t->pairwise->adjust = BONFERRONI;
1765                       else if (lex_match_id (lexer, "BH"))
1766                         t->pairwise->adjust = BH;
1767                       else if (lex_match_id (lexer, "NONE"))
1768                         t->pairwise->adjust = 0;
1769                       else
1770                         {
1771                           lex_error_expecting (lexer, "BONFERRONI", "BH",
1772                                                "NONE");
1773                           goto error;
1774                         }
1775                     }
1776                   else if (lex_match_id (lexer, "INCLUDEMRSETS"))
1777                     {
1778                       lex_match (lexer, T_EQUALS);
1779                       if (!parse_bool (lexer, &t->pairwise->include_mrsets))
1780                         goto error;
1781                     }
1782                   else if (lex_match_id (lexer, "MEANSVARIANCE"))
1783                     {
1784                       lex_match (lexer, T_EQUALS);
1785                       if (lex_match_id (lexer, "ALLCATS"))
1786                         t->pairwise->meansvariance_allcats = true;
1787                       else if (lex_match_id (lexer, "TESTEDCATS"))
1788                         t->pairwise->meansvariance_allcats = false;
1789                       else
1790                         {
1791                           lex_error_expecting (lexer, "ALLCATS", "TESTEDCATS");
1792                           goto error;
1793                         }
1794                     }
1795                   else if (lex_match_id (lexer, "CATEGORIES"))
1796                     {
1797                       lex_match (lexer, T_EQUALS);
1798                       if (lex_match_id (lexer, "ALLVISIBLE"))
1799                         t->pairwise->all_visible = true;
1800                       else if (lex_match_id (lexer, "SUBTOTALS"))
1801                         t->pairwise->all_visible = false;
1802                       else
1803                         {
1804                           lex_error_expecting (lexer, "ALLVISIBLE",
1805                                                "SUBTOTALS");
1806                           goto error;
1807                         }
1808                     }
1809                   else if (lex_match_id (lexer, "MERGE"))
1810                     {
1811                       lex_match (lexer, T_EQUALS);
1812                       if (!parse_bool (lexer, &t->pairwise->merge))
1813                         goto error;
1814                     }
1815                   else if (lex_match_id (lexer, "STYLE"))
1816                     {
1817                       lex_match (lexer, T_EQUALS);
1818                       if (lex_match_id (lexer, "APA"))
1819                         t->pairwise->apa_style = true;
1820                       else if (lex_match_id (lexer, "SIMPLE"))
1821                         t->pairwise->apa_style = false;
1822                       else
1823                         {
1824                           lex_error_expecting (lexer, "APA", "SIMPLE");
1825                           goto error;
1826                         }
1827                     }
1828                   else if (lex_match_id (lexer, "SHOWSIG"))
1829                     {
1830                       lex_match (lexer, T_EQUALS);
1831                       if (!parse_bool (lexer, &t->pairwise->show_sig))
1832                         goto error;
1833                     }
1834                   else
1835                     {
1836                       lex_error_expecting (lexer, "TYPE", "ALPHA", "ADJUST",
1837                                            "INCLUDEMRSETS", "MEANSVARIANCE",
1838                                            "CATEGORIES", "MERGE", "STYLE",
1839                                            "SHOWSIG");
1840                       goto error;
1841                     }
1842                 }
1843               while (lex_token (lexer) != T_SLASH
1844                      && lex_token (lexer) != T_ENDCMD);
1845             }
1846           else
1847             {
1848               lex_error_expecting (lexer, "TABLE", "SLABELS", "CLABELS",
1849                                    "CRITERIA", "CATEGORIES", "TITLES",
1850                                    "SIGTEST", "COMPARETEST");
1851               goto error;
1852             }
1853         }
1854
1855       if (t->row_labels != CTLP_NORMAL && t->col_labels != CTLP_NORMAL)
1856         {
1857           msg (SE, _("ROWLABELS and COLLABELS may not both be specified."));
1858           goto error;
1859         }
1860
1861     }
1862   while (lex_token (lexer) != T_ENDCMD);
1863   ctables_destroy (ct);
1864   return CMD_SUCCESS;
1865
1866 error:
1867   ctables_destroy (ct);
1868   return CMD_FAILURE;
1869 }
1870