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