1 /* PSPP - a program for statistical analysis.
2 Copyright (C) 2021 Free Software Foundation, Inc.
4 This program is free software: you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation, either version 3 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program. If not, see <http://www.gnu.org/licenses/>. */
19 #include "data/dataset.h"
20 #include "data/dictionary.h"
21 #include "language/command.h"
22 #include "language/lexer/format-parser.h"
23 #include "language/lexer/lexer.h"
24 #include "language/lexer/variable-parser.h"
25 #include "libpspp/hmap.h"
26 #include "libpspp/message.h"
27 #include "output/pivot-table.h"
29 #include "gl/minmax.h"
30 #include "gl/xalloc.h"
33 #define _(msgid) gettext (msgid)
37 CTVL_DEFAULT = SETTINGS_VALUE_SHOW_DEFAULT,
38 CTVL_NAME = SETTINGS_VALUE_SHOW_VALUE,
39 CTVL_LABEL = SETTINGS_VALUE_SHOW_LABEL,
40 CTVL_BOTH = SETTINGS_VALUE_SHOW_BOTH,
44 ctables_vlabel_unique (enum ctables_vlabel vlabel)
46 /* This ensures that all of the values are unique. */
60 struct pivot_table_look *look;
62 /* If this is NULL, zeros are displayed using the normal print format.
63 Otherwise, this string is displayed. */
66 /* If this is NULL, missing values are displayed using the normal print
67 format. Otherwise, this string is displayed. */
70 enum ctables_vlabel *vlabels;
72 bool mrsets_count_duplicates; /* MRSETS. */
73 bool smissing_listwise; /* SMISSING. */
74 struct variable *base_weight; /* WEIGHT. */
75 int hide_threshold; /* HIDESMALLCOUNTS. */
77 struct ctables_table *tables;
81 struct ctables_postcompute
83 struct hmap_node hmap_node; /* In struct ctables's 'pcompute' hmap. */
84 const char *name; /* Name, without leading &. */
86 struct ctables_postcompute_expr *expr;
89 bool hide_source_cats;
92 struct ctables_postcompute_expr
94 enum ctables_postcompute_op
102 /* XXX SUBTOTAL and HSUBTOTAL */
115 /* CTPO_CAT_NUMBER, CTPO_NUMBER. */
120 XXX what about string ranges? */
123 double low; /* -DBL_MAX for LO. */
124 double high; /* DBL_MAX for HIGH. */
128 /* CTPO_ADD, CTPO_SUB, CTPO_MUL, CTPO_DIV, CTPO_POW. */
129 struct ctables_postcompute_expr *subs[2];
133 enum ctables_label_position
142 struct ctables_axis *axes[PIVOT_N_AXES];
144 enum pivot_axis_type slabels_position;
145 bool slabels_visible;
147 enum ctables_label_position row_labels;
148 enum ctables_label_position col_labels;
158 struct ctables_chisq *chisq;
159 struct ctables_pairwise *pairwise;
162 /* Chi-square test (SIGTEST). */
170 /* Pairwise comparison test (COMPARETEST). */
171 struct ctables_pairwise
173 enum { PROP, MEAN } type;
176 bool meansvariance_allcats;
178 enum { BONFERRONI = 1, BH } adjust;
205 struct variable *var;
206 const struct mrset *mrset;
210 struct ctables_summary *summaries;
215 struct ctables_axis *subs[2];
219 static void ctables_axis_destroy (struct ctables_axis *);
222 /* All variables. */ \
223 S(CTSF_COUNT, "COUNT") \
224 S(CTSF_ECOUNT, "ECOUNT") \
225 S(CTSF_ROWPCT_COUNT, "ROWPCT.COUNT") \
226 S(CTSF_COLPCT_COUNT, "COLPCT.COUNT") \
227 S(CTSF_TABLEPCT_COUNT, "TABLEPCT.COUNT") \
228 S(CTSF_SUBTABLEPCT_COUNT, "SUBTABLEPCT.COUNT") \
229 S(CTSF_LAYERPCT_COUNT, "LAYERPCT.COUNT") \
230 S(CTSF_LAYERROWPCT_COUNT, "LAYERROWPCT.COUNT") \
231 S(CTSF_LAYERCOLPCT_COUNT, "LAYERCOLPCT.COUNT") \
232 S(CTSF_ROWPCT_VALIDN, "ROWPCT.VALIDN") \
233 S(CTSF_COLPCT_VALIDN, "COLPCT.VALIDN") \
234 S(CTSF_TABLEPCT_VALIDN, "TABLEPCT.VALIDN") \
235 S(CTSF_SUBTABLEPCT_VALIDN, "SUBTABLEPCT.VALIDN") \
236 S(CTSF_LAYERPCT_VALIDN, "LAYERPCT.VALIDN") \
237 S(CTSF_LAYERROWPCT_VALIDN, "LAYERROWPCT.VALIDN") \
238 S(CTSF_LAYERCOLPCT_VALIDN, "LAYERCOLPCT.VALIDN") \
239 S(CTSF_ROWPCT_TOTALN, "ROWPCT.TOTALN") \
240 S(CTSF_COLPCT_TOTALN, "COLPCT.TOTALN") \
241 S(CTSF_TABLEPCT_TOTALN, "TABLEPCT.TOTALN") \
242 S(CTSF_SUBTABLEPCT_TOTALN, "SUBTABLEPCT.TOTALN") \
243 S(CTSF_LAYERPCT_TOTALN, "LAYERPCT.TOTALN") \
244 S(CTSF_LAYERROWPCT_TOTALN, "LAYERROWPCT.TOTALN") \
245 S(CTSF_LAYERCOLPCT_TOTALN, "LAYERCOLPCT.TOTALN") \
247 /* Scale variables, totals, and subtotals. */ \
248 S(CTSF_MAXIMUM, "!MAXIMUM") \
249 S(CTSF_MEAN, "!MEAN") \
250 S(CTSF_MEDIAN, "!MEDIAN") \
251 S(CTSF_MINIMUM, "!MINIMUM") \
252 S(CTSF_MISSING, "!MISSING") \
253 S(CTSF_MODE, "!MODE") \
254 S(CTSF_PTILE, "!PTILE") \
255 S(CTSF_RANGE, "!RANGE") \
256 S(CTSF_SEMAN, "!SEMAN") \
257 S(CTSF_STDDEV, "!STDDEV") \
258 S(CTSF_SUM, "!SUM") \
259 S(CSTF_TOTALN, "!TOTALN") \
260 S(CTSF_ETOTALN, "!ETOTALN") \
261 S(CTSF_VALIDN, "!VALIDN") \
262 S(CTSF_EVALIDN, "!EVALIDN") \
263 S(CTSF_VARIANCE, "!VARIANCE") \
264 S(CTSF_ROWPCT_SUM, "ROWPCT.SUM") \
265 S(CTSF_COLPCT_SUM, "COLPCT.SUM") \
266 S(CTSF_TABLEPCT_SUM, "TABLEPCT.SUM") \
267 S(CTSF_SUBTABLEPCT_SUM, "SUBTABLEPCT.SUM") \
268 S(CTSF_LAYERPCT_SUM, "LAYERPCT.SUM") \
269 S(CTSF_LAYERROWPCT_SUM, "LAYERROWPCT.SUM") \
270 S(CTSF_LAYERCOLPCT_SUM, "LAYERCOLPCT.SUM") \
272 /* Multiple response sets. */ \
273 S(CTSF_ROWPCT_RESPONSES, "ROWPCT.RESPONSES") \
274 S(CTSF_COLPCT_RESPONSES, "COLPCT.RESPONSES") \
275 S(CTSF_TABLEPCT_RESPONSES, "TABLEPCT.RESPONSES") \
276 S(CTSF_SUBTABLEPCT_RESPONSES, "SUBTABLEPCT.RESPONSES") \
277 S(CTSF_LAYERPCT_RESPONSES, "LAYERPCT.RESPONSES") \
278 S(CTSF_LAYERROWPCT_RESPONSES, "LAYERROWPCT.RESPONSES") \
279 S(CTSF_LAYERCOLPCT_RESPONSES, "LAYERCOLPCT.RESPONSES") \
280 S(CTSF_ROWPCT_RESPONSES_COUNT, "ROWPCT.RESPONSES.COUNT") \
281 S(CTSF_COLPCT_RESPONSES_COUNT, "COLPCT.RESPONSES.COUNT") \
282 S(CTSF_TABLEPCT_RESPONSES_COUNT, "TABLEPCT.RESPONSES.COUNT") \
283 S(CTSF_SUBTABLEPCT_RESPONSES_COUNT, "SUBTABLEPCT.RESPONSES.COUNT") \
284 S(CTSF_LAYERPCT_RESPONSES_COUNT, "LAYERPCT.RESPONSES.COUNT") \
285 S(CTSF_LAYERROWPCT_RESPONSES_COUNT, "LAYERROWPCT.RESPONSES.COUNT") \
286 S(CTSF_LAYERCOLPCT_RESPONSES_COUNT, "LAYERCOLPCT.RESPONSES.COUNT") \
287 S(CTSF_ROWPCT_COUNT_RESPONSES, "ROWPCT.COUNT.RESPONSES") \
288 S(CTSF_COLPCT_COUNT_RESPONSES, "COLPCT.COUNT.RESPONSES") \
289 S(CTSF_TABLEPCT_COUNT_RESPONSES, "TABLEPCT.COUNT.RESPONSES") \
290 S(CTSF_SUBTABLEPCT_COUNT_RESPONSES, "SUBTABLEPCT.COUNT.RESPONSES") \
291 S(CTSF_LAYERPCT_COUNT_RESPONSES, "LAYERPCT.COUNT.RESPONSES") \
292 S(CTSF_LAYERROWPCT_COUNT_RESPONSES, "LAYERROWPCT.COUNT.RESPONSES") \
293 S(CTSF_LAYERCOLPCT_COUNT_RESPONSES, "LAYERCOLPCT.COUNT.RESPONSES")
295 enum ctables_summary_function
297 #define S(ENUM, NAME) ENUM,
303 #define S(ENUM, NAME) +1
304 N_CTSF_FUNCTIONS = SUMMARIES
308 struct ctables_summary
310 enum ctables_summary_function function;
312 struct fmt_spec format; /* XXX extra CTABLES formats */
316 ctables_summary_uninit (struct ctables_summary *s)
323 parse_col_width (struct lexer *lexer, const char *name, double *width)
325 lex_match (lexer, T_EQUALS);
326 if (lex_match_id (lexer, "DEFAULT"))
328 else if (lex_force_num_range_closed (lexer, name, 0, DBL_MAX))
330 *width = lex_number (lexer);
340 parse_bool (struct lexer *lexer, bool *b)
342 if (lex_match_id (lexer, "NO"))
344 else if (lex_match_id (lexer, "YES"))
348 lex_error_expecting (lexer, "YES", "NO");
355 parse_ctables_summary_function (struct lexer *lexer,
356 enum ctables_summary_function *f)
360 enum ctables_summary_function function;
361 struct substring name;
363 static struct pair names[] = {
364 #define S(ENUM, NAME) { ENUM, SS_LITERAL_INITIALIZER (NAME) },
367 /* The .COUNT suffix may be omitted. */
368 S(CTSF_ROWPCT_COUNT, "ROWPCT")
369 S(CTSF_COLPCT_COUNT, "COLPCT")
370 S(CTSF_TABLEPCT_COUNT, "TABLEPCT")
371 S(CTSF_SUBTABLEPCT_COUNT, "SUBTABLEPCT")
372 S(CTSF_LAYERPCT_COUNT, "LAYERPCT")
373 S(CTSF_LAYERROWPCT_COUNT, "LAYERROWPCT")
374 S(CTSF_LAYERCOLPCT_COUNT, "LAYERCOLPCT")
378 if (!lex_force_id (lexer))
381 for (size_t i = 0; i < sizeof names / sizeof *names; i++)
382 if (ss_equals_case (names[i].name, lex_tokss (lexer)))
384 *f = names[i].function;
388 lex_error (lexer, _("Expecting summary function name."));
393 ctables_axis_destroy (struct ctables_axis *axis)
402 for (size_t i = 0; i < axis->n_summaries; i++)
403 ctables_summary_uninit (&axis->summaries[i]);
404 free (axis->summaries);
409 ctables_axis_destroy (axis->subs[0]);
410 ctables_axis_destroy (axis->subs[1]);
416 static struct ctables_axis *
417 ctables_axis_new_nonterminal (enum ctables_axis_op op,
418 struct ctables_axis *sub0,
419 struct ctables_axis *sub1)
421 struct ctables_axis *axis = xmalloc (sizeof *axis);
422 *axis = (struct ctables_axis) { .op = op, .subs = { sub0, sub1 } };
426 struct ctables_axis_parse_ctx
429 struct dictionary *dict;
431 struct ctables_table *t;
434 static struct ctables_summary *
435 add_summary (struct ctables_axis *axis, size_t *allocated_summaries)
437 if (axis->n_summaries >= *allocated_summaries)
438 axis->summaries = x2nrealloc (axis->summaries, allocated_summaries,
439 sizeof *axis->summaries);
441 struct ctables_summary *s = &axis->summaries[axis->n_summaries++];
442 *s = (struct ctables_summary) { .function = CTSF_COUNT };
446 static struct ctables_axis *ctables_axis_parse_stack (
447 struct ctables_axis_parse_ctx *);
449 static struct ctables_axis *
450 ctables_axis_parse_primary (struct ctables_axis_parse_ctx *ctx)
452 if (lex_match (ctx->lexer, T_LPAREN))
454 struct ctables_axis *sub = ctables_axis_parse_stack (ctx);
455 if (!sub || !lex_force_match (ctx->lexer, T_RPAREN))
457 ctables_axis_destroy (sub);
463 if (!lex_force_id (ctx->lexer))
466 const struct mrset *mrset = NULL;
467 struct variable *var = NULL;
468 if (ss_starts_with (lex_tokss (ctx->lexer), ss_cstr ("$")))
470 mrset = dict_lookup_mrset (ctx->dict, lex_tokcstr (ctx->lexer));
473 lex_error (ctx->lexer, _("'%s' is not the name of a "
474 "multiple-response set in the active file "
476 lex_tokcstr (ctx->lexer));
479 lex_get (ctx->lexer);
483 var = parse_variable (ctx->lexer, ctx->dict);
488 struct ctables_axis *axis = xmalloc (sizeof *axis);
490 *axis = (struct ctables_axis) { .op = CTAO_MRSET, .mrset = mrset };
492 *axis = (struct ctables_axis) { .op = CTAO_VAR, .var = var };
494 /* XXX should figure out default measures by reading data */
495 axis->scale = (mrset ? false
496 : lex_match_phrase (ctx->lexer, "[S]") ? true
497 : lex_match_phrase (ctx->lexer, "[C]") ? false
498 : var_get_measure (var) == MEASURE_SCALE);
500 size_t allocated_summaries = 0;
501 if (lex_match (ctx->lexer, T_LBRACK))
505 struct ctables_summary *s = add_summary (axis, &allocated_summaries);
506 if (!parse_ctables_summary_function (ctx->lexer, &s->function))
508 if (lex_is_string (ctx->lexer))
510 s->label = ss_xstrdup (lex_tokss (ctx->lexer));
511 lex_get (ctx->lexer);
513 if (lex_token (ctx->lexer) == T_ID)
515 if (!parse_format_specifier (ctx->lexer, &s->format)
516 || !fmt_check_output (&s->format)
517 || !fmt_check_type_compat (&s->format, VAL_NUMERIC))
520 lex_match (ctx->lexer, T_COMMA);
522 while (!lex_match (ctx->lexer, T_RBRACK));
526 struct ctables_summary *s = add_summary (axis, &allocated_summaries);
527 s->function = axis->scale ? CTSF_MEAN : CTSF_COUNT;
528 s->label = xstrdup (axis->scale ? _("Mean") : _("Count"));
529 s->format = (struct fmt_spec) { .type = FMT_F, .w = 40 };
534 ctables_axis_destroy (axis);
538 static struct ctables_axis *
539 ctables_axis_parse_nest (struct ctables_axis_parse_ctx *ctx)
541 struct ctables_axis *lhs = ctables_axis_parse_primary (ctx);
545 while (lex_match (ctx->lexer, T_PLUS))
547 struct ctables_axis *rhs = ctables_axis_parse_primary (ctx);
551 lhs = ctables_axis_new_nonterminal (CTAO_NEST, lhs, rhs);
557 static struct ctables_axis *
558 ctables_axis_parse_stack (struct ctables_axis_parse_ctx *ctx)
560 struct ctables_axis *lhs = ctables_axis_parse_nest (ctx);
564 while (lex_match (ctx->lexer, T_PLUS))
566 struct ctables_axis *rhs = ctables_axis_parse_nest (ctx);
570 lhs = ctables_axis_new_nonterminal (CTAO_STACK, lhs, rhs);
577 ctables_axis_parse (struct lexer *lexer, struct dictionary *dict,
578 struct ctables *ct, struct ctables_table *t,
579 enum pivot_axis_type a)
581 if (lex_token (lexer) == T_BY
582 || lex_token (lexer) == T_SLASH
583 || lex_token (lexer) == T_ENDCMD)
586 struct ctables_axis_parse_ctx ctx = {
592 t->axes[a] = ctables_axis_parse_stack (&ctx);
593 return t->axes[a] != NULL;
597 cmd_ctables (struct lexer *lexer, struct dataset *ds)
599 size_t n_vars = dict_get_n_vars (dataset_dict (ds));
600 enum ctables_vlabel *vlabels = xnmalloc (n_vars, sizeof *vlabels);
601 for (size_t i = 0; n_vars; i++)
602 vlabels[i] = CTVL_DEFAULT;
604 struct ctables *ct = xmalloc (sizeof *ct);
605 *ct = (struct ctables) {
606 .look = pivot_table_look_unshare (pivot_table_look_ref (
607 pivot_table_look_get_default ())),
612 if (!lex_force_match (lexer, T_SLASH))
615 while (!lex_match_id (lexer, "TABLE"))
617 if (lex_match_id (lexer, "FORMAT"))
619 double widths[2] = { SYSMIS, SYSMIS };
620 double units_per_inch = 72.0;
622 while (lex_token (lexer) != T_SLASH)
624 if (lex_match_id (lexer, "MINCOLWIDTH"))
626 if (!parse_col_width (lexer, "MINCOLWIDTH", &widths[0]))
629 else if (lex_match_id (lexer, "MAXCOLWIDTH"))
631 if (!parse_col_width (lexer, "MAXCOLWIDTH", &widths[1]))
634 else if (lex_match_id (lexer, "UNITS"))
636 lex_match (lexer, T_EQUALS);
637 if (lex_match_id (lexer, "POINTS"))
638 units_per_inch = 72.0;
639 else if (lex_match_id (lexer, "INCHES"))
640 units_per_inch = 1.0;
641 else if (lex_match_id (lexer, "CM"))
642 units_per_inch = 2.54;
645 lex_error_expecting (lexer, "POINTS", "INCHES", "CM");
649 else if (lex_match_id (lexer, "EMPTY"))
654 lex_match (lexer, T_EQUALS);
655 if (lex_match_id (lexer, "ZERO"))
659 else if (lex_match_id (lexer, "BLANK"))
660 ct->zero = xstrdup ("");
661 else if (lex_force_string (lexer))
663 ct->zero = ss_xstrdup (lex_tokss (lexer));
669 else if (lex_match_id (lexer, "MISSING"))
671 lex_match (lexer, T_EQUALS);
672 if (!lex_force_string (lexer))
676 ct->missing = (strcmp (lex_tokcstr (lexer), ".")
677 ? ss_xstrdup (lex_tokss (lexer))
683 lex_error_expecting (lexer, "MINCOLWIDTH", "MAXCOLWIDTH",
684 "UNITS", "EMPTY", "MISSING");
689 if (widths[0] != SYSMIS && widths[1] != SYSMIS
690 && widths[0] > widths[1])
692 msg (SE, _("MINCOLWIDTH must not be greater than MAXCOLWIDTH."));
696 for (size_t i = 0; i < 2; i++)
697 if (widths[i] != SYSMIS)
699 int *wr = ct->look->width_ranges[TABLE_HORZ];
700 wr[i] = widths[i] / units_per_inch * 96.0;
705 else if (lex_match_id (lexer, "VLABELS"))
707 if (!lex_force_match_id (lexer, "VARIABLES"))
709 lex_match (lexer, T_EQUALS);
711 struct variable **vars;
713 if (!parse_variables (lexer, dataset_dict (ds), &vars, &n_vars,
717 if (!lex_force_match_id (lexer, "DISPLAY"))
722 lex_match (lexer, T_EQUALS);
724 enum ctables_vlabel vlabel;
725 if (lex_match_id (lexer, "DEFAULT"))
726 vlabel = CTVL_DEFAULT;
727 else if (lex_match_id (lexer, "NAME"))
729 else if (lex_match_id (lexer, "LABEL"))
731 else if (lex_match_id (lexer, "BOTH"))
733 else if (lex_match_id (lexer, "NONE"))
737 lex_error_expecting (lexer, "DEFAULT", "NAME", "LABEL",
743 for (size_t i = 0; i < n_vars; i++)
744 ct->vlabels[var_get_dict_index (vars[i])] = vlabel;
747 else if (lex_match_id (lexer, "MRSETS"))
749 if (!lex_force_match_id (lexer, "COUNTDUPLICATES"))
751 lex_match (lexer, T_EQUALS);
752 if (!parse_bool (lexer, &ct->mrsets_count_duplicates))
755 else if (lex_match_id (lexer, "SMISSING"))
757 if (lex_match_id (lexer, "VARIABLE"))
758 ct->smissing_listwise = false;
759 else if (lex_match_id (lexer, "LISTWISE"))
760 ct->smissing_listwise = true;
763 lex_error_expecting (lexer, "VARIABLE", "LISTWISE");
768 else if (lex_match_id (lexer, "WEIGHT"))
770 if (!lex_force_match_id (lexer, "VARIABLE"))
772 lex_match (lexer, T_EQUALS);
773 ct->base_weight = parse_variable (lexer, dataset_dict (ds));
774 if (!ct->base_weight)
777 else if (lex_match_id (lexer, "HIDESMALLCOUNTS"))
779 if (!lex_force_match_id (lexer, "COUNT"))
781 lex_match (lexer, T_EQUALS);
782 if (!lex_force_int_range (lexer, "HIDESMALLCOUNTS COUNT", 2, INT_MAX))
784 ct->hide_threshold = lex_integer (lexer);
789 lex_error_expecting (lexer, "FORMAT", "VLABELS", "MRSETS",
790 "SMISSING", "PCOMPUTE", "PPROPERTIES",
791 "WEIGHT", "HIDESMALLCOUNTS", "TABLE");
795 if (!lex_force_match (lexer, T_SLASH))
799 size_t allocated_tables = 0;
802 if (ct->n_tables >= allocated_tables)
803 ct->tables = x2nrealloc (ct->tables, &allocated_tables,
806 struct ctables_table *t = &ct->tables[ct->n_tables++];
807 *t = (struct ctables_table) {
808 .slabels_position = PIVOT_AXIS_COLUMN,
809 .slabels_visible = true,
810 .row_labels = CTLP_NORMAL,
811 .col_labels = CTLP_NORMAL,
815 lex_match (lexer, T_EQUALS);
816 if (!ctables_axis_parse (lexer, dataset_dict (ds), ct, t, PIVOT_AXIS_ROW))
819 if (lex_match (lexer, T_BY))
821 if (!ctables_axis_parse (lexer, dataset_dict (ds),
822 ct, t, PIVOT_AXIS_COLUMN))
825 if (lex_match (lexer, T_BY))
827 if (!ctables_axis_parse (lexer, dataset_dict (ds),
828 ct, t, PIVOT_AXIS_LAYER))
832 if (!lex_force_match (lexer, T_SLASH))
835 /* XXX Validate axes. */
836 while (!lex_match_id (lexer, "TABLE") && lex_token (lexer) != T_ENDCMD)
838 if (lex_match_id (lexer, "SLABELS"))
840 while (lex_token (lexer) != T_SLASH)
842 if (lex_match_id (lexer, "POSITION"))
844 lex_match (lexer, T_EQUALS);
845 if (lex_match_id (lexer, "COLUMN"))
846 t->slabels_position = PIVOT_AXIS_COLUMN;
847 else if (lex_match_id (lexer, "ROW"))
848 t->slabels_position = PIVOT_AXIS_ROW;
849 else if (lex_match_id (lexer, "LAYER"))
850 t->slabels_position = PIVOT_AXIS_LAYER;
853 lex_error_expecting (lexer, "COLUMN", "ROW",
858 else if (lex_match_id (lexer, "VISIBLE"))
860 lex_match (lexer, T_EQUALS);
861 if (!parse_bool (lexer, &t->slabels_visible))
866 lex_error_expecting (lexer, "POSITION", "VISIBLE");
871 else if (lex_match_id (lexer, "CLABELS"))
873 while (lex_token (lexer) != T_SLASH)
875 if (lex_match_id (lexer, "AUTO"))
876 t->row_labels = t->col_labels = CTLP_NORMAL;
877 else if (lex_match_id (lexer, "ROWLABELS"))
879 lex_match (lexer, T_EQUALS);
880 if (lex_match_id (lexer, "OPPOSITE"))
881 t->row_labels = CTLP_OPPOSITE;
882 else if (lex_match_id (lexer, "LAYER"))
883 t->row_labels = CTLP_LAYER;
886 lex_error_expecting (lexer, "OPPOSITE", "LAYER");
890 else if (lex_match_id (lexer, "COLLABELS"))
892 lex_match (lexer, T_EQUALS);
893 if (lex_match_id (lexer, "OPPOSITE"))
894 t->col_labels = CTLP_OPPOSITE;
895 else if (lex_match_id (lexer, "LAYER"))
896 t->col_labels = CTLP_LAYER;
899 lex_error_expecting (lexer, "OPPOSITE", "LAYER");
905 lex_error_expecting (lexer, "AUTO", "ROWLABELS",
911 else if (lex_match_id (lexer, "CRITERIA"))
913 if (!lex_force_match_id (lexer, "CILEVEL"))
915 lex_match (lexer, T_EQUALS);
917 if (!lex_force_num_range_halfopen (lexer, "CILEVEL", 0, 100))
919 t->cilevel = lex_number (lexer);
922 else if (lex_match_id (lexer, "TITLES"))
927 if (lex_match_id (lexer, "CAPTION"))
929 else if (lex_match_id (lexer, "CORNER"))
931 else if (lex_match_id (lexer, "TITLE"))
935 lex_error_expecting (lexer, "CAPTION", "CORNER", "TITLE");
938 lex_match (lexer, T_EQUALS);
940 struct string s = DS_EMPTY_INITIALIZER;
941 while (lex_is_string (lexer))
943 if (!ds_is_empty (&s))
944 ds_put_byte (&s, ' ');
945 ds_put_substring (&s, lex_tokss (lexer));
949 *textp = ds_steal_cstr (&s);
951 while (lex_token (lexer) != T_SLASH
952 && lex_token (lexer) != T_ENDCMD);
954 else if (lex_match_id (lexer, "SIGTEST"))
958 t->chisq = xmalloc (sizeof *t->chisq);
959 *t->chisq = (struct ctables_chisq) {
961 .include_mrsets = true,
968 if (lex_match_id (lexer, "TYPE"))
970 lex_match (lexer, T_EQUALS);
971 if (!lex_force_match_id (lexer, "CHISQUARE"))
974 else if (lex_match_id (lexer, "ALPHA"))
976 lex_match (lexer, T_EQUALS);
977 if (!lex_force_num_range_halfopen (lexer, "ALPHA", 0, 1))
979 t->chisq->alpha = lex_number (lexer);
982 else if (lex_match_id (lexer, "INCLUDEMRSETS"))
984 lex_match (lexer, T_EQUALS);
985 if (parse_bool (lexer, &t->chisq->include_mrsets))
988 else if (lex_match_id (lexer, "CATEGORIES"))
990 lex_match (lexer, T_EQUALS);
991 if (lex_match_id (lexer, "ALLVISIBLE"))
992 t->chisq->all_visible = true;
993 else if (lex_match_id (lexer, "SUBTOTALS"))
994 t->chisq->all_visible = false;
997 lex_error_expecting (lexer,
998 "ALLVISIBLE", "SUBTOTALS");
1004 lex_error_expecting (lexer, "TYPE", "ALPHA",
1005 "INCLUDEMRSETS", "CATEGORIES");
1009 while (lex_token (lexer) != T_SLASH
1010 && lex_token (lexer) != T_ENDCMD);
1012 else if (lex_match_id (lexer, "COMPARETEST"))
1016 t->pairwise = xmalloc (sizeof *t->pairwise);
1017 *t->pairwise = (struct ctables_pairwise) {
1019 .alpha = { .05, .05 },
1020 .adjust = BONFERRONI,
1021 .include_mrsets = true,
1022 .meansvariance_allcats = true,
1023 .all_visible = true,
1032 if (lex_match_id (lexer, "TYPE"))
1034 lex_match (lexer, T_EQUALS);
1035 if (lex_match_id (lexer, "PROP"))
1036 t->pairwise->type = PROP;
1037 else if (lex_match_id (lexer, "MEAN"))
1038 t->pairwise->type = MEAN;
1041 lex_error_expecting (lexer, "PROP", "MEAN");
1045 else if (lex_match_id (lexer, "ALPHA"))
1047 lex_match (lexer, T_EQUALS);
1049 if (!lex_force_num_range_open (lexer, "ALPHA", 0, 1))
1051 double a0 = lex_number (lexer);
1054 lex_match (lexer, T_COMMA);
1055 if (lex_is_number (lexer))
1057 if (!lex_force_num_range_open (lexer, "ALPHA", 0, 1))
1059 double a1 = lex_number (lexer);
1062 t->pairwise->alpha[0] = MIN (a0, a1);
1063 t->pairwise->alpha[1] = MAX (a0, a1);
1066 t->pairwise->alpha[0] = t->pairwise->alpha[1] = a0;
1068 else if (lex_match_id (lexer, "ADJUST"))
1070 lex_match (lexer, T_EQUALS);
1071 if (lex_match_id (lexer, "BONFERRONI"))
1072 t->pairwise->adjust = BONFERRONI;
1073 else if (lex_match_id (lexer, "BH"))
1074 t->pairwise->adjust = BH;
1075 else if (lex_match_id (lexer, "NONE"))
1076 t->pairwise->adjust = 0;
1079 lex_error_expecting (lexer, "BONFERRONI", "BH",
1084 else if (lex_match_id (lexer, "INCLUDEMRSETS"))
1086 lex_match (lexer, T_EQUALS);
1087 if (!parse_bool (lexer, &t->pairwise->include_mrsets))
1090 else if (lex_match_id (lexer, "MEANSVARIANCE"))
1092 lex_match (lexer, T_EQUALS);
1093 if (lex_match_id (lexer, "ALLCATS"))
1094 t->pairwise->meansvariance_allcats = true;
1095 else if (lex_match_id (lexer, "TESTEDCATS"))
1096 t->pairwise->meansvariance_allcats = false;
1099 lex_error_expecting (lexer, "ALLCATS", "TESTEDCATS");
1103 else if (lex_match_id (lexer, "CATEGORIES"))
1105 lex_match (lexer, T_EQUALS);
1106 if (lex_match_id (lexer, "ALLVISIBLE"))
1107 t->pairwise->all_visible = true;
1108 else if (lex_match_id (lexer, "SUBTOTALS"))
1109 t->pairwise->all_visible = false;
1112 lex_error_expecting (lexer, "ALLVISIBLE",
1117 else if (lex_match_id (lexer, "MERGE"))
1119 lex_match (lexer, T_EQUALS);
1120 if (!parse_bool (lexer, &t->pairwise->merge))
1123 else if (lex_match_id (lexer, "STYLE"))
1125 lex_match (lexer, T_EQUALS);
1126 if (lex_match_id (lexer, "APA"))
1127 t->pairwise->apa_style = true;
1128 else if (lex_match_id (lexer, "SIMPLE"))
1129 t->pairwise->apa_style = false;
1132 lex_error_expecting (lexer, "APA", "SIMPLE");
1136 else if (lex_match_id (lexer, "SHOWSIG"))
1138 lex_match (lexer, T_EQUALS);
1139 if (!parse_bool (lexer, &t->pairwise->show_sig))
1144 lex_error_expecting (lexer, "TYPE", "ALPHA", "ADJUST",
1145 "INCLUDEMRSETS", "MEANSVARIANCE",
1146 "CATEGORIES", "MERGE", "STYLE",
1151 while (lex_token (lexer) != T_SLASH
1152 && lex_token (lexer) != T_ENDCMD);
1156 lex_error_expecting (lexer, "TABLE", "SLABELS", "CLABELS",
1157 "CRITERIA", "CATEGORIES", "TITLES",
1158 "SIGTEST", "COMPARETEST");
1163 while (lex_token (lexer) != T_ENDCMD);