Change how checking for missing values works.
[pspp] / src / language / stats / oneway.c
1 /* PSPP - a program for statistical analysis.
2    Copyright (C) 1997-9, 2000, 2007, 2009, 2010, 2011, 2012, 2013, 2014,
3    2020 Free Software Foundation, Inc.
4
5    This program is free software: you can redistribute it and/or modify
6    it under the terms of the GNU General Public License as published by
7    the Free Software Foundation, either version 3 of the License, or
8    (at your option) any later version.
9
10    This program is distributed in the hope that it will be useful,
11    but WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13    GNU General Public License for more details.
14
15    You should have received a copy of the GNU General Public License
16    along with this program.  If not, see <http://www.gnu.org/licenses/>. */
17
18 #include <config.h>
19
20 #include <float.h>
21 #include <gsl/gsl_cdf.h>
22 #include <gsl/gsl_matrix.h>
23 #include <math.h>
24
25 #include "data/case.h"
26 #include "data/casegrouper.h"
27 #include "data/casereader.h"
28 #include "data/dataset.h"
29 #include "data/dictionary.h"
30 #include "data/format.h"
31 #include "data/value.h"
32 #include "language/command.h"
33 #include "language/dictionary/split-file.h"
34 #include "language/lexer/lexer.h"
35 #include "language/lexer/value-parser.h"
36 #include "language/lexer/variable-parser.h"
37 #include "libpspp/ll.h"
38 #include "libpspp/message.h"
39 #include "libpspp/misc.h"
40 #include "libpspp/taint.h"
41 #include "linreg/sweep.h"
42 #include "tukey/tukey.h"
43 #include "math/categoricals.h"
44 #include "math/interaction.h"
45 #include "math/covariance.h"
46 #include "math/levene.h"
47 #include "math/moments.h"
48 #include "output/pivot-table.h"
49
50 #include "gettext.h"
51 #define _(msgid) gettext (msgid)
52 #define N_(msgid) msgid
53
54 /* Workspace variable for each dependent variable */
55 struct per_var_ws
56 {
57   struct interaction *iact;
58   struct categoricals *cat;
59   struct covariance *cov;
60   struct levene *nl;
61
62   double n;
63
64   double sst;
65   double sse;
66   double ssa;
67
68   int n_groups;
69
70   double mse;
71 };
72
73 /* Per category data */
74 struct descriptive_data
75 {
76   const struct variable *var;
77   struct moments1 *mom;
78
79   double minimum;
80   double maximum;
81 };
82
83 enum missing_type
84   {
85     MISS_LISTWISE,
86     MISS_ANALYSIS,
87   };
88
89 enum statistics
90   {
91     STATS_DESCRIPTIVES = 0x0001,
92     STATS_HOMOGENEITY = 0x0002
93   };
94
95 struct coeff_node
96 {
97   struct ll ll;
98   double coeff;
99 };
100
101
102 struct contrasts_node
103 {
104   struct ll ll;
105   struct ll_list coefficient_list;
106 };
107
108
109 struct oneway_spec;
110
111 typedef double df_func (const struct per_var_ws *pvw, const struct moments1 *mom_i, const struct moments1 *mom_j);
112 typedef double ts_func (int k, const struct moments1 *mom_i, const struct moments1 *mom_j, double std_err);
113 typedef double p1tail_func (double ts, double df1, double df2);
114
115 typedef double pinv_func (double std_err, double alpha, double df, int k, const struct moments1 *mom_i, const struct moments1 *mom_j);
116
117
118 struct posthoc
119 {
120   const char *syntax;
121   const char *label;
122
123   df_func *dff;
124   ts_func *tsf;
125   p1tail_func *p1f;
126
127   pinv_func *pinv;
128 };
129
130 struct oneway_spec
131 {
132   size_t n_vars;
133   const struct variable **vars;
134
135   const struct variable *indep_var;
136
137   enum statistics stats;
138
139   enum missing_type missing_type;
140   enum mv_class exclude;
141
142   /* List of contrasts */
143   struct ll_list contrast_list;
144
145   /* The weight variable */
146   const struct variable *wv;
147   const struct fmt_spec *wfmt;
148
149   /* The confidence level for multiple comparisons */
150   double alpha;
151
152   int *posthoc;
153   int n_posthoc;
154 };
155
156 static double
157 df_common (const struct per_var_ws *pvw, const struct moments1 *mom_i UNUSED, const struct moments1 *mom_j UNUSED)
158 {
159   return  pvw->n - pvw->n_groups;
160 }
161
162 static double
163 df_individual (const struct per_var_ws *pvw UNUSED, const struct moments1 *mom_i, const struct moments1 *mom_j)
164 {
165   double n_i, var_i;
166   double n_j, var_j;
167   double nom,denom;
168
169   moments1_calculate (mom_i, &n_i, NULL, &var_i, 0, 0);
170   moments1_calculate (mom_j, &n_j, NULL, &var_j, 0, 0);
171
172   if (n_i <= 1.0 || n_j <= 1.0)
173     return SYSMIS;
174
175   nom = pow2 (var_i/n_i + var_j/n_j);
176   denom = pow2 (var_i/n_i) / (n_i - 1) + pow2 (var_j/n_j) / (n_j - 1);
177
178   return nom / denom;
179 }
180
181 static double lsd_pinv (double std_err, double alpha, double df, int k UNUSED, const struct moments1 *mom_i UNUSED, const struct moments1 *mom_j UNUSED)
182 {
183   return std_err * gsl_cdf_tdist_Pinv (1.0 - alpha / 2.0, df);
184 }
185
186 static double bonferroni_pinv (double std_err, double alpha, double df, int k, const struct moments1 *mom_i UNUSED, const struct moments1 *mom_j UNUSED)
187 {
188   const int m = k * (k - 1) / 2;
189   return std_err * gsl_cdf_tdist_Pinv (1.0 - alpha / (2.0 * m), df);
190 }
191
192 static double sidak_pinv (double std_err, double alpha, double df, int k, const struct moments1 *mom_i UNUSED, const struct moments1 *mom_j UNUSED)
193 {
194   const double m = k * (k - 1) / 2;
195   double lp = 1.0 - exp (log (1.0 - alpha) / m) ;
196   return std_err * gsl_cdf_tdist_Pinv (1.0 - lp / 2.0, df);
197 }
198
199 static double tukey_pinv (double std_err, double alpha, double df, int k, const struct moments1 *mom_i UNUSED, const struct moments1 *mom_j UNUSED)
200 {
201   if (k < 2 || df < 2)
202     return SYSMIS;
203
204   return std_err / sqrt (2.0)  * qtukey (1 - alpha, 1.0, k, df, 1, 0);
205 }
206
207 static double scheffe_pinv (double std_err, double alpha, double df, int k, const struct moments1 *mom_i UNUSED, const struct moments1 *mom_j UNUSED)
208 {
209   double x = (k - 1) * gsl_cdf_fdist_Pinv (1.0 - alpha, k - 1, df);
210   return std_err * sqrt (x);
211 }
212
213 static double gh_pinv (double std_err UNUSED, double alpha, double df, int k, const struct moments1 *mom_i, const struct moments1 *mom_j)
214 {
215   double n_i, mean_i, var_i;
216   double n_j, mean_j, var_j;
217   double m;
218
219   moments1_calculate (mom_i, &n_i, &mean_i, &var_i, 0, 0);
220   moments1_calculate (mom_j, &n_j, &mean_j, &var_j, 0, 0);
221
222   m = sqrt ((var_i/n_i + var_j/n_j) / 2.0);
223
224   if (k < 2 || df < 2)
225     return SYSMIS;
226
227   return m * qtukey (1 - alpha, 1.0, k, df, 1, 0);
228 }
229
230
231 static double
232 multiple_comparison_sig (double std_err,
233                                        const struct per_var_ws *pvw,
234                                        const struct descriptive_data *dd_i, const struct descriptive_data *dd_j,
235                                        const struct posthoc *ph)
236 {
237   int k = pvw->n_groups;
238   double df = ph->dff (pvw, dd_i->mom, dd_j->mom);
239   double ts = ph->tsf (k, dd_i->mom, dd_j->mom, std_err);
240   if (df == SYSMIS)
241     return SYSMIS;
242   return  ph->p1f (ts, k - 1, df);
243 }
244
245 static double
246 mc_half_range (const struct oneway_spec *cmd, const struct per_var_ws *pvw, double std_err, const struct descriptive_data *dd_i, const struct descriptive_data *dd_j, const struct posthoc *ph)
247 {
248   int k = pvw->n_groups;
249   double df = ph->dff (pvw, dd_i->mom, dd_j->mom);
250   if (df == SYSMIS)
251     return SYSMIS;
252
253   return ph->pinv (std_err, cmd->alpha, df, k, dd_i->mom, dd_j->mom);
254 }
255
256 static double tukey_1tailsig (double ts, double df1, double df2)
257 {
258   double twotailedsig;
259
260   if (df2 < 2 || df1 < 1)
261     return SYSMIS;
262
263   twotailedsig = 1.0 - ptukey (ts, 1.0, df1 + 1, df2, 1, 0);
264
265   return twotailedsig / 2.0;
266 }
267
268 static double lsd_1tailsig (double ts, double df1 UNUSED, double df2)
269 {
270   return ts < 0 ? gsl_cdf_tdist_P (ts, df2) : gsl_cdf_tdist_Q (ts, df2);
271 }
272
273 static double sidak_1tailsig (double ts, double df1, double df2)
274 {
275   double ex = (df1 + 1.0) * df1 / 2.0;
276   double lsd_sig = 2 * lsd_1tailsig (ts, df1, df2);
277
278   return 0.5 * (1.0 - pow (1.0 - lsd_sig, ex));
279 }
280
281 static double bonferroni_1tailsig (double ts, double df1, double df2)
282 {
283   const int m = (df1 + 1) * df1 / 2;
284
285   double p = ts < 0 ? gsl_cdf_tdist_P (ts, df2) : gsl_cdf_tdist_Q (ts, df2);
286   p *= m;
287
288   return p > 0.5 ? 0.5 : p;
289 }
290
291 static double scheffe_1tailsig (double ts, double df1, double df2)
292 {
293   return 0.5 * gsl_cdf_fdist_Q (ts, df1, df2);
294 }
295
296
297 static double tukey_test_stat (int k UNUSED, const struct moments1 *mom_i, const struct moments1 *mom_j, double std_err)
298 {
299   double ts;
300   double n_i, mean_i, var_i;
301   double n_j, mean_j, var_j;
302
303   moments1_calculate (mom_i, &n_i, &mean_i, &var_i, 0, 0);
304   moments1_calculate (mom_j, &n_j, &mean_j, &var_j, 0, 0);
305
306   ts =  (mean_i - mean_j) / std_err;
307   ts = fabs (ts) * sqrt (2.0);
308
309   return ts;
310 }
311
312 static double lsd_test_stat (int k UNUSED, const struct moments1 *mom_i, const struct moments1 *mom_j, double std_err)
313 {
314   double n_i, mean_i, var_i;
315   double n_j, mean_j, var_j;
316
317   moments1_calculate (mom_i, &n_i, &mean_i, &var_i, 0, 0);
318   moments1_calculate (mom_j, &n_j, &mean_j, &var_j, 0, 0);
319
320   return (mean_i - mean_j) / std_err;
321 }
322
323 static double scheffe_test_stat (int k, const struct moments1 *mom_i, const struct moments1 *mom_j, double std_err)
324 {
325   double t;
326   double n_i, mean_i, var_i;
327   double n_j, mean_j, var_j;
328
329   moments1_calculate (mom_i, &n_i, &mean_i, &var_i, 0, 0);
330   moments1_calculate (mom_j, &n_j, &mean_j, &var_j, 0, 0);
331
332   t = (mean_i - mean_j) / std_err;
333   t = pow2 (t);
334   t /= k - 1;
335
336   return t;
337 }
338
339 static double gh_test_stat (int k UNUSED, const struct moments1 *mom_i, const struct moments1 *mom_j, double std_err UNUSED)
340 {
341   double ts;
342   double thing;
343   double n_i, mean_i, var_i;
344   double n_j, mean_j, var_j;
345
346   moments1_calculate (mom_i, &n_i, &mean_i, &var_i, 0, 0);
347   moments1_calculate (mom_j, &n_j, &mean_j, &var_j, 0, 0);
348
349   thing = var_i / n_i + var_j / n_j;
350   thing /= 2.0;
351   thing = sqrt (thing);
352
353   ts = (mean_i - mean_j) / thing;
354
355   return fabs (ts);
356 }
357
358
359
360 static const struct posthoc ph_tests [] =
361   {
362     { "LSD",        N_("LSD"),          df_common, lsd_test_stat,     lsd_1tailsig,          lsd_pinv},
363     { "TUKEY",      N_("Tukey HSD"),    df_common, tukey_test_stat,   tukey_1tailsig,        tukey_pinv},
364     { "BONFERRONI", N_("Bonferroni"),   df_common, lsd_test_stat,     bonferroni_1tailsig,   bonferroni_pinv},
365     { "SCHEFFE",    N_("Scheffé"),      df_common, scheffe_test_stat, scheffe_1tailsig,      scheffe_pinv},
366     { "GH",         N_("Games-Howell"), df_individual, gh_test_stat,  tukey_1tailsig,        gh_pinv},
367     { "SIDAK",      N_("Å idák"),        df_common, lsd_test_stat,     sidak_1tailsig,        sidak_pinv}
368   };
369
370
371 struct oneway_workspace
372 {
373   /* The number of distinct values of the independent variable, when all
374      missing values are disregarded */
375   int actual_number_of_groups;
376
377   struct per_var_ws *vws;
378
379   /* An array of descriptive data.  One for each dependent variable */
380   struct descriptive_data **dd_total;
381 };
382
383 /* Routines to show the output tables */
384 static void show_anova_table (const struct oneway_spec *, const struct oneway_workspace *);
385 static void show_descriptives (const struct oneway_spec *, const struct oneway_workspace *);
386 static void show_homogeneity (const struct oneway_spec *, const struct oneway_workspace *);
387
388 static void output_oneway (const struct oneway_spec *, struct oneway_workspace *ws);
389 static void run_oneway (const struct oneway_spec *cmd, struct casereader *input, const struct dataset *ds);
390
391
392 static void
393 destroy_coeff_list (struct contrasts_node *coeff_list)
394 {
395   struct coeff_node *cn = NULL;
396   struct coeff_node *cnx = NULL;
397   struct ll_list *cl = &coeff_list->coefficient_list;
398
399   ll_for_each_safe (cn, cnx, struct coeff_node, ll, cl)
400     {
401       free (cn);
402     }
403
404   free (coeff_list);
405 }
406
407 static void
408 oneway_cleanup (struct oneway_spec *cmd)
409 {
410   struct contrasts_node *coeff_list  = NULL;
411   struct contrasts_node *coeff_next  = NULL;
412   ll_for_each_safe (coeff_list, coeff_next, struct contrasts_node, ll, &cmd->contrast_list)
413     {
414       destroy_coeff_list (coeff_list);
415     }
416
417   free (cmd->posthoc);
418 }
419
420
421
422 int
423 cmd_oneway (struct lexer *lexer, struct dataset *ds)
424 {
425   const struct dictionary *dict = dataset_dict (ds);
426   struct oneway_spec oneway ;
427   oneway.n_vars = 0;
428   oneway.vars = NULL;
429   oneway.indep_var = NULL;
430   oneway.stats = 0;
431   oneway.missing_type = MISS_ANALYSIS;
432   oneway.exclude = MV_ANY;
433   oneway.wv = dict_get_weight (dict);
434   oneway.wfmt = dict_get_weight_format (dict);
435   oneway.alpha = 0.05;
436   oneway.posthoc = NULL;
437   oneway.n_posthoc = 0;
438
439   ll_init (&oneway.contrast_list);
440
441
442   if (lex_match (lexer, T_SLASH))
443     {
444       if (!lex_force_match_id (lexer, "VARIABLES"))
445         {
446           goto error;
447         }
448       lex_match (lexer, T_EQUALS);
449     }
450
451   if (!parse_variables_const (lexer, dict,
452                               &oneway.vars, &oneway.n_vars,
453                               PV_NO_DUPLICATE | PV_NUMERIC))
454     goto error;
455
456   if (!lex_force_match (lexer, T_BY))
457     goto error;
458
459   oneway.indep_var = parse_variable_const (lexer, dict);
460   if (oneway.indep_var == NULL)
461     goto error;
462
463   while (lex_token (lexer) != T_ENDCMD)
464     {
465       lex_match (lexer, T_SLASH);
466
467       if (lex_match_id (lexer, "STATISTICS"))
468         {
469           lex_match (lexer, T_EQUALS);
470           while (lex_token (lexer) != T_ENDCMD && lex_token (lexer) != T_SLASH)
471             {
472               if (lex_match_id (lexer, "DESCRIPTIVES"))
473                 {
474                   oneway.stats |= STATS_DESCRIPTIVES;
475                 }
476               else if (lex_match_id (lexer, "HOMOGENEITY"))
477                 {
478                   oneway.stats |= STATS_HOMOGENEITY;
479                 }
480               else
481                 {
482                   lex_error (lexer, NULL);
483                   goto error;
484                 }
485             }
486         }
487       else if (lex_match_id (lexer, "POSTHOC"))
488         {
489           lex_match (lexer, T_EQUALS);
490           while (lex_token (lexer) != T_ENDCMD && lex_token (lexer) != T_SLASH)
491             {
492               int p;
493               bool method = false;
494               for (p = 0 ; p < sizeof (ph_tests) / sizeof (struct posthoc); ++p)
495                 {
496                   if (lex_match_id (lexer, ph_tests[p].syntax))
497                     {
498                       oneway.n_posthoc++;
499                       oneway.posthoc = xrealloc (oneway.posthoc, sizeof (*oneway.posthoc) * oneway.n_posthoc);
500                       oneway.posthoc[oneway.n_posthoc - 1] = p;
501                       method = true;
502                       break;
503                     }
504                 }
505               if (method == false)
506                 {
507                   if (lex_match_id (lexer, "ALPHA"))
508                     {
509                       if (!lex_force_match (lexer, T_LPAREN))
510                         goto error;
511                       if (! lex_force_num (lexer))
512                         goto error;
513                       oneway.alpha = lex_number (lexer);
514                       lex_get (lexer);
515                       if (!lex_force_match (lexer, T_RPAREN))
516                         goto error;
517                     }
518                   else
519                     {
520                       msg (SE, _("The post hoc analysis method %s is not supported."), lex_tokcstr (lexer));
521                       lex_error (lexer, NULL);
522                       goto error;
523                     }
524                 }
525             }
526         }
527       else if (lex_match_id (lexer, "CONTRAST"))
528         {
529           struct contrasts_node *cl = XZALLOC (struct contrasts_node);
530
531           struct ll_list *coefficient_list = &cl->coefficient_list;
532           lex_match (lexer, T_EQUALS);
533
534           ll_init (coefficient_list);
535
536           while (lex_token (lexer) != T_ENDCMD && lex_token (lexer) != T_SLASH)
537             {
538               if (lex_is_number (lexer))
539                 {
540                   struct coeff_node *cc = xmalloc (sizeof *cc);
541                   cc->coeff = lex_number (lexer);
542
543                   ll_push_tail (coefficient_list, &cc->ll);
544                   lex_get (lexer);
545                 }
546               else
547                 {
548                   destroy_coeff_list (cl);
549                   lex_error (lexer, NULL);
550                   goto error;
551                 }
552             }
553
554           if (ll_count (coefficient_list) <= 0)
555             {
556               destroy_coeff_list (cl);
557               goto error;
558             }
559
560           ll_push_tail (&oneway.contrast_list, &cl->ll);
561         }
562       else if (lex_match_id (lexer, "MISSING"))
563         {
564           lex_match (lexer, T_EQUALS);
565           while (lex_token (lexer) != T_ENDCMD && lex_token (lexer) != T_SLASH)
566             {
567               if (lex_match_id (lexer, "INCLUDE"))
568                 {
569                   oneway.exclude = MV_SYSTEM;
570                 }
571               else if (lex_match_id (lexer, "EXCLUDE"))
572                 {
573                   oneway.exclude = MV_ANY;
574                 }
575               else if (lex_match_id (lexer, "LISTWISE"))
576                 {
577                   oneway.missing_type = MISS_LISTWISE;
578                 }
579               else if (lex_match_id (lexer, "ANALYSIS"))
580                 {
581                   oneway.missing_type = MISS_ANALYSIS;
582                 }
583               else
584                 {
585                   lex_error (lexer, NULL);
586                   goto error;
587                 }
588             }
589         }
590       else
591         {
592           lex_error (lexer, NULL);
593           goto error;
594         }
595     }
596
597
598   {
599     struct casegrouper *grouper;
600     struct casereader *group;
601     bool ok;
602
603     grouper = casegrouper_create_splits (proc_open (ds), dict);
604     while (casegrouper_get_next_group (grouper, &group))
605       run_oneway (&oneway, group, ds);
606     ok = casegrouper_destroy (grouper);
607     ok = proc_commit (ds) && ok;
608   }
609
610   oneway_cleanup (&oneway);
611   free (oneway.vars);
612   return CMD_SUCCESS;
613
614  error:
615   oneway_cleanup (&oneway);
616   free (oneway.vars);
617   return CMD_FAILURE;
618 }
619
620
621 \f
622
623
624 static struct descriptive_data *
625 dd_create (const struct variable *var)
626 {
627   struct descriptive_data *dd = xmalloc (sizeof *dd);
628
629   dd->mom = moments1_create (MOMENT_VARIANCE);
630   dd->minimum = DBL_MAX;
631   dd->maximum = -DBL_MAX;
632   dd->var = var;
633
634   return dd;
635 }
636
637 static void
638 dd_destroy (struct descriptive_data *dd)
639 {
640   moments1_destroy (dd->mom);
641   free (dd);
642 }
643
644 static void *
645 makeit (const void *aux1, void *aux2 UNUSED)
646 {
647   const struct variable *var = aux1;
648
649   struct descriptive_data *dd = dd_create (var);
650
651   return dd;
652 }
653
654 static void
655 killit (const void *aux1 UNUSED, void *aux2 UNUSED, void *user_data)
656 {
657   struct descriptive_data *dd = user_data;
658
659   dd_destroy (dd);
660 }
661
662
663 static void
664 updateit (const void *aux1, void *aux2, void *user_data,
665           const struct ccase *c, double weight)
666 {
667   struct descriptive_data *dd = user_data;
668
669   const struct variable *varp = aux1;
670
671   const union value *valx = case_data (c, varp);
672
673   struct descriptive_data *dd_total = aux2;
674
675   moments1_add (dd->mom, valx->f, weight);
676   if (valx->f < dd->minimum)
677     dd->minimum = valx->f;
678
679   if (valx->f > dd->maximum)
680     dd->maximum = valx->f;
681
682   {
683     const struct variable *var = dd_total->var;
684     const union value *val = case_data (c, var);
685
686     moments1_add (dd_total->mom,
687                   val->f,
688                   weight);
689
690     if (val->f < dd_total->minimum)
691       dd_total->minimum = val->f;
692
693     if (val->f > dd_total->maximum)
694       dd_total->maximum = val->f;
695   }
696 }
697
698 static void
699 run_oneway (const struct oneway_spec *cmd,
700             struct casereader *input,
701             const struct dataset *ds)
702 {
703   int v;
704   struct taint *taint;
705   struct dictionary *dict = dataset_dict (ds);
706   struct casereader *reader;
707   struct ccase *c;
708
709   struct oneway_workspace ws;
710
711   ws.actual_number_of_groups = 0;
712   ws.vws = xcalloc (cmd->n_vars, sizeof (*ws.vws));
713   ws.dd_total = XCALLOC (cmd->n_vars, struct descriptive_data*);
714
715   for (v = 0 ; v < cmd->n_vars; ++v)
716     ws.dd_total[v] = dd_create (cmd->vars[v]);
717
718   for (v = 0; v < cmd->n_vars; ++v)
719     {
720       static const struct payload payload =
721         {
722           .create = makeit,
723           .update = updateit,
724           .calculate = NULL,
725           .destroy = killit
726         };
727
728       ws.vws[v].iact = interaction_create (cmd->indep_var);
729       ws.vws[v].cat = categoricals_create (&ws.vws[v].iact, 1, cmd->wv,
730                                            cmd->exclude);
731
732       categoricals_set_payload (ws.vws[v].cat, &payload,
733                                 CONST_CAST (struct variable *, cmd->vars[v]),
734                                 ws.dd_total[v]);
735
736
737       ws.vws[v].cov = covariance_2pass_create (1, &cmd->vars[v],
738                                                ws.vws[v].cat,
739                                                cmd->wv, cmd->exclude, true);
740       ws.vws[v].nl = levene_create (var_get_width (cmd->indep_var), NULL);
741     }
742
743   c = casereader_peek (input, 0);
744   if (c == NULL)
745     {
746       casereader_destroy (input);
747       goto finish;
748     }
749   output_split_file_values (ds, c);
750   case_unref (c);
751
752   taint = taint_clone (casereader_get_taint (input));
753
754   input = casereader_create_filter_missing (input, &cmd->indep_var, 1,
755                                             cmd->exclude, NULL, NULL);
756   if (cmd->missing_type == MISS_LISTWISE)
757     input = casereader_create_filter_missing (input, cmd->vars, cmd->n_vars,
758                                               cmd->exclude, NULL, NULL);
759   input = casereader_create_filter_weight (input, dict, NULL, NULL);
760
761   reader = casereader_clone (input);
762   for (; (c = casereader_read (reader)) != NULL; case_unref (c))
763     {
764       int i;
765       double w = dict_get_case_weight (dict, c, NULL);
766
767       for (i = 0; i < cmd->n_vars; ++i)
768         {
769           struct per_var_ws *pvw = &ws.vws[i];
770           const struct variable *v = cmd->vars[i];
771           const union value *val = case_data (c, v);
772
773           if (MISS_ANALYSIS == cmd->missing_type)
774             {
775               if (var_is_value_missing (v, val) & cmd->exclude)
776                 continue;
777             }
778
779           covariance_accumulate_pass1 (pvw->cov, c);
780           levene_pass_one (pvw->nl, val->f, w, case_data (c, cmd->indep_var));
781         }
782     }
783   casereader_destroy (reader);
784
785   reader = casereader_clone (input);
786   for (; (c = casereader_read (reader)); case_unref (c))
787     {
788       int i;
789       double w = dict_get_case_weight (dict, c, NULL);
790       for (i = 0; i < cmd->n_vars; ++i)
791         {
792           struct per_var_ws *pvw = &ws.vws[i];
793           const struct variable *v = cmd->vars[i];
794           const union value *val = case_data (c, v);
795
796           if (MISS_ANALYSIS == cmd->missing_type)
797             {
798               if (var_is_value_missing (v, val) & cmd->exclude)
799                 continue;
800             }
801
802           covariance_accumulate_pass2 (pvw->cov, c);
803           levene_pass_two (pvw->nl, val->f, w, case_data (c, cmd->indep_var));
804         }
805     }
806   casereader_destroy (reader);
807
808   reader = casereader_clone (input);
809   for (; (c = casereader_read (reader)); case_unref (c))
810     {
811       int i;
812       double w = dict_get_case_weight (dict, c, NULL);
813
814       for (i = 0; i < cmd->n_vars; ++i)
815         {
816           struct per_var_ws *pvw = &ws.vws[i];
817           const struct variable *v = cmd->vars[i];
818           const union value *val = case_data (c, v);
819
820           if (MISS_ANALYSIS == cmd->missing_type)
821             {
822               if (var_is_value_missing (v, val) & cmd->exclude)
823                 continue;
824             }
825
826           levene_pass_three (pvw->nl, val->f, w, case_data (c, cmd->indep_var));
827         }
828     }
829   casereader_destroy (reader);
830
831
832   for (v = 0; v < cmd->n_vars; ++v)
833     {
834       const gsl_matrix *ucm;
835       gsl_matrix *cm;
836       struct per_var_ws *pvw = &ws.vws[v];
837       const struct categoricals *cats = covariance_get_categoricals (pvw->cov);
838       const bool ok = categoricals_sane (cats);
839
840       if (! ok)
841         {
842           msg (MW,
843                _("Dependent variable %s has no non-missing values.  No analysis for this variable will be done."),
844                var_get_name (cmd->vars[v]));
845           continue;
846         }
847
848       ucm = covariance_calculate_unnormalized (pvw->cov);
849
850       cm = gsl_matrix_alloc (ucm->size1, ucm->size2);
851       gsl_matrix_memcpy (cm, ucm);
852
853       moments1_calculate (ws.dd_total[v]->mom, &pvw->n, NULL, NULL, NULL, NULL);
854
855       pvw->sst = gsl_matrix_get (cm, 0, 0);
856
857       reg_sweep (cm, 0);
858
859       pvw->sse = gsl_matrix_get (cm, 0, 0);
860       gsl_matrix_free (cm);
861
862       pvw->ssa = pvw->sst - pvw->sse;
863
864       pvw->n_groups = categoricals_n_total (cats);
865
866       pvw->mse = (pvw->sst - pvw->ssa) / (pvw->n - pvw->n_groups);
867     }
868
869   for (v = 0; v < cmd->n_vars; ++v)
870     {
871       const struct categoricals *cats = covariance_get_categoricals (ws.vws[v].cov);
872
873       if (! categoricals_is_complete (cats))
874         {
875           continue;
876         }
877
878       if (categoricals_n_total (cats) > ws.actual_number_of_groups)
879         ws.actual_number_of_groups = categoricals_n_total (cats);
880     }
881
882   casereader_destroy (input);
883
884   if (!taint_has_tainted_successor (taint))
885     output_oneway (cmd, &ws);
886
887   taint_destroy (taint);
888
889  finish:
890
891   for (v = 0; v < cmd->n_vars; ++v)
892     {
893       covariance_destroy (ws.vws[v].cov);
894       levene_destroy (ws.vws[v].nl);
895       dd_destroy (ws.dd_total[v]);
896       interaction_destroy (ws.vws[v].iact);
897     }
898
899   free (ws.vws);
900   free (ws.dd_total);
901 }
902
903 static void show_contrast_coeffs (const struct oneway_spec *cmd, const struct oneway_workspace *ws);
904 static void show_contrast_tests (const struct oneway_spec *cmd, const struct oneway_workspace *ws);
905 static void show_comparisons (const struct oneway_spec *cmd, const struct oneway_workspace *ws, int depvar);
906
907 static void
908 output_oneway (const struct oneway_spec *cmd, struct oneway_workspace *ws)
909 {
910   size_t i = 0;
911
912   /* Check the sanity of the given contrast values */
913   struct contrasts_node *coeff_list  = NULL;
914   struct contrasts_node *coeff_next  = NULL;
915   ll_for_each_safe (coeff_list, coeff_next, struct contrasts_node, ll, &cmd->contrast_list)
916     {
917       struct coeff_node *cn = NULL;
918       double sum = 0;
919       struct ll_list *cl = &coeff_list->coefficient_list;
920       ++i;
921
922       if (ll_count (cl) != ws->actual_number_of_groups)
923         {
924           msg (SW,
925                _("In contrast list %zu, the number of coefficients (%zu) does not equal the number of groups (%d). This contrast list will be ignored."),
926                i, ll_count (cl), ws->actual_number_of_groups);
927
928           ll_remove (&coeff_list->ll);
929           destroy_coeff_list (coeff_list);
930           continue;
931         }
932
933       ll_for_each (cn, struct coeff_node, ll, cl)
934         sum += cn->coeff;
935
936       if (sum != 0.0)
937         msg (SW, _("Coefficients for contrast %zu do not total zero"), i);
938     }
939
940   if (cmd->stats & STATS_DESCRIPTIVES)
941     show_descriptives (cmd, ws);
942
943   if (cmd->stats & STATS_HOMOGENEITY)
944     show_homogeneity (cmd, ws);
945
946   show_anova_table (cmd, ws);
947
948   if (ll_count (&cmd->contrast_list) > 0)
949     {
950       show_contrast_coeffs (cmd, ws);
951       show_contrast_tests (cmd, ws);
952     }
953
954   if (cmd->posthoc)
955     {
956       int v;
957       for (v = 0 ; v < cmd->n_vars; ++v)
958         {
959           const struct categoricals *cats = covariance_get_categoricals (ws->vws[v].cov);
960
961           if (categoricals_is_complete (cats))
962             show_comparisons (cmd, ws, v);
963         }
964     }
965 }
966
967
968 /* Show the ANOVA table */
969 static void
970 show_anova_table (const struct oneway_spec *cmd, const struct oneway_workspace *ws)
971 {
972   struct pivot_table *table = pivot_table_create (N_("ANOVA"));
973
974   pivot_dimension_create (table, PIVOT_AXIS_COLUMN, N_("Statistics"),
975                           N_("Sum of Squares"), PIVOT_RC_OTHER,
976                           N_("df"), PIVOT_RC_INTEGER,
977                           N_("Mean Square"), PIVOT_RC_OTHER,
978                           N_("F"), PIVOT_RC_OTHER,
979                           N_("Sig."), PIVOT_RC_SIGNIFICANCE);
980
981   pivot_dimension_create (table, PIVOT_AXIS_ROW, N_("Type"),
982                           N_("Between Groups"), N_("Within Groups"),
983                           N_("Total"));
984
985   struct pivot_dimension *variables = pivot_dimension_create (
986     table, PIVOT_AXIS_ROW, N_("Variables"));
987
988   for (size_t i = 0; i < cmd->n_vars; ++i)
989     {
990       int var_idx = pivot_category_create_leaf (
991         variables->root, pivot_value_new_variable (cmd->vars[i]));
992
993       const struct per_var_ws *pvw = &ws->vws[i];
994
995       double n;
996       moments1_calculate (ws->dd_total[i]->mom, &n, NULL, NULL, NULL, NULL);
997
998       double df1 = pvw->n_groups - 1;
999       double df2 = n - pvw->n_groups;
1000       double msa = pvw->ssa / df1;
1001       double F = msa / pvw->mse ;
1002
1003       struct entry
1004         {
1005           int stat_idx;
1006           int type_idx;
1007           double x;
1008         }
1009       entries[] = {
1010         /* Sums of Squares. */
1011         { 0, 0, pvw->ssa },
1012         { 0, 1, pvw->sse },
1013         { 0, 2, pvw->sst },
1014         /* Degrees of Freedom. */
1015         { 1, 0, df1 },
1016         { 1, 1, df2 },
1017         { 1, 2, n - 1 },
1018         /* Mean Squares. */
1019         { 2, 0, msa },
1020         { 2, 1, pvw->mse },
1021         /* F. */
1022         { 3, 0, F },
1023         /* Significance. */
1024         { 4, 0, gsl_cdf_fdist_Q (F, df1, df2) },
1025       };
1026       for (size_t j = 0; j < sizeof entries / sizeof *entries; j++)
1027         {
1028           const struct entry *e = &entries[j];
1029           pivot_table_put3 (table, e->stat_idx, e->type_idx, var_idx,
1030                             pivot_value_new_number (e->x));
1031         }
1032     }
1033
1034   pivot_table_submit (table);
1035 }
1036
1037 /* Show the descriptives table */
1038 static void
1039 show_descriptives (const struct oneway_spec *cmd, const struct oneway_workspace *ws)
1040 {
1041   if (!cmd->n_vars)
1042     return;
1043   const struct categoricals *cats = covariance_get_categoricals (
1044     ws->vws[0].cov);
1045
1046   struct pivot_table *table = pivot_table_create (N_("Descriptives"));
1047   pivot_table_set_weight_format (table, cmd->wfmt);
1048
1049   const double confidence = 0.95;
1050
1051   struct pivot_dimension *statistics = pivot_dimension_create (
1052     table, PIVOT_AXIS_COLUMN, N_("Statistics"),
1053     N_("N"), PIVOT_RC_COUNT, N_("Mean"), N_("Std. Deviation"),
1054     N_("Std. Error"));
1055   struct pivot_category *interval = pivot_category_create_group__ (
1056     statistics->root,
1057     pivot_value_new_text_format (N_("%g%% Confidence Interval for Mean"),
1058                                  confidence * 100.0));
1059   pivot_category_create_leaves (interval, N_("Lower Bound"),
1060                                 N_("Upper Bound"));
1061   pivot_category_create_leaves (statistics->root,
1062                                 N_("Minimum"), N_("Maximum"));
1063
1064   struct pivot_dimension *indep_var = pivot_dimension_create__ (
1065     table, PIVOT_AXIS_ROW, pivot_value_new_variable (cmd->indep_var));
1066   indep_var->root->show_label = true;
1067   size_t n;
1068   union value *values = categoricals_get_var_values (cats, cmd->indep_var, &n);
1069   for (size_t j = 0; j < n; j++)
1070     pivot_category_create_leaf (
1071       indep_var->root, pivot_value_new_var_value (cmd->indep_var, &values[j]));
1072   pivot_category_create_leaf (
1073     indep_var->root, pivot_value_new_text_format (N_("Total")));
1074
1075   struct pivot_dimension *dep_var = pivot_dimension_create (
1076     table, PIVOT_AXIS_ROW, N_("Dependent Variable"));
1077
1078   const double q = (1.0 - confidence) / 2.0;
1079   for (int v = 0; v < cmd->n_vars; ++v)
1080     {
1081       int dep_var_idx = pivot_category_create_leaf (
1082         dep_var->root, pivot_value_new_variable (cmd->vars[v]));
1083
1084       struct per_var_ws *pvw = &ws->vws[v];
1085       const struct categoricals *cats = covariance_get_categoricals (pvw->cov);
1086
1087       int count;
1088       for (count = 0; count < categoricals_n_total (cats); ++count)
1089         {
1090           const struct descriptive_data *dd
1091             = categoricals_get_user_data_by_category (cats, count);
1092
1093           double n, mean, variance;
1094           moments1_calculate (dd->mom, &n, &mean, &variance, NULL, NULL);
1095
1096           double std_dev = sqrt (variance);
1097           double std_error = std_dev / sqrt (n) ;
1098           double T = gsl_cdf_tdist_Qinv (q, n - 1);
1099
1100           double entries[] = {
1101             n,
1102             mean,
1103             std_dev,
1104             std_error,
1105             mean - T * std_error,
1106             mean + T * std_error,
1107             dd->minimum,
1108             dd->maximum,
1109           };
1110           for (size_t i = 0; i < sizeof entries / sizeof *entries; i++)
1111             pivot_table_put3 (table, i, count, dep_var_idx,
1112                               pivot_value_new_number (entries[i]));
1113         }
1114
1115       if (categoricals_is_complete (cats))
1116         {
1117           double n, mean, variance;
1118           moments1_calculate (ws->dd_total[v]->mom, &n, &mean, &variance,
1119                               NULL, NULL);
1120
1121           double std_dev = sqrt (variance);
1122           double std_error = std_dev / sqrt (n) ;
1123           double T = gsl_cdf_tdist_Qinv (q, n - 1);
1124
1125           double entries[] = {
1126             n,
1127             mean,
1128             std_dev,
1129             std_error,
1130             mean - T * std_error,
1131             mean + T * std_error,
1132             ws->dd_total[v]->minimum,
1133             ws->dd_total[v]->maximum,
1134           };
1135           for (size_t i = 0; i < sizeof entries / sizeof *entries; i++)
1136             pivot_table_put3 (table, i, count, dep_var_idx,
1137                               pivot_value_new_number (entries[i]));
1138         }
1139     }
1140
1141   pivot_table_submit (table);
1142 }
1143
1144 /* Show the homogeneity table */
1145 static void
1146 show_homogeneity (const struct oneway_spec *cmd, const struct oneway_workspace *ws)
1147 {
1148   struct pivot_table *table = pivot_table_create (
1149     N_("Test of Homogeneity of Variances"));
1150
1151   pivot_dimension_create (table, PIVOT_AXIS_COLUMN, N_("Statistics"),
1152                           N_("Levene Statistic"), PIVOT_RC_OTHER,
1153                           N_("df1"), PIVOT_RC_INTEGER,
1154                           N_("df2"), PIVOT_RC_INTEGER,
1155                           N_("Sig."), PIVOT_RC_SIGNIFICANCE);
1156
1157   struct pivot_dimension *variables = pivot_dimension_create (
1158     table, PIVOT_AXIS_ROW, N_("Variables"));
1159
1160   for (int v = 0; v < cmd->n_vars; ++v)
1161     {
1162       int var_idx = pivot_category_create_leaf (
1163         variables->root, pivot_value_new_variable (cmd->vars[v]));
1164
1165       double n;
1166       moments1_calculate (ws->dd_total[v]->mom, &n, NULL, NULL, NULL, NULL);
1167
1168       const struct per_var_ws *pvw = &ws->vws[v];
1169       double df1 = pvw->n_groups - 1;
1170       double df2 = n - pvw->n_groups;
1171       double F = levene_calculate (pvw->nl);
1172
1173       double entries[] =
1174         {
1175           F,
1176           df1,
1177           df2,
1178           gsl_cdf_fdist_Q (F, df1, df2),
1179         };
1180       for (size_t i = 0; i < sizeof entries / sizeof *entries; i++)
1181         pivot_table_put2 (table, i, var_idx,
1182                           pivot_value_new_number (entries[i]));
1183     }
1184
1185   pivot_table_submit (table);
1186 }
1187
1188
1189 /* Show the contrast coefficients table */
1190 static void
1191 show_contrast_coeffs (const struct oneway_spec *cmd, const struct oneway_workspace *ws)
1192 {
1193   struct pivot_table *table = pivot_table_create (N_("Contrast Coefficients"));
1194
1195   struct pivot_dimension *indep_var = pivot_dimension_create__ (
1196     table, PIVOT_AXIS_COLUMN, pivot_value_new_variable (cmd->indep_var));
1197   indep_var->root->show_label = true;
1198
1199   struct pivot_dimension *contrast = pivot_dimension_create (
1200     table, PIVOT_AXIS_ROW, N_("Contrast"));
1201   contrast->root->show_label = true;
1202
1203   const struct covariance *cov = ws->vws[0].cov;
1204
1205   const struct contrasts_node *cn;
1206   int c_num = 1;
1207   ll_for_each (cn, struct contrasts_node, ll, &cmd->contrast_list)
1208     {
1209       int contrast_idx = pivot_category_create_leaf (
1210         contrast->root, pivot_value_new_integer (c_num++));
1211
1212       const struct coeff_node *coeffn;
1213       int indep_idx = 0;
1214       ll_for_each (coeffn, struct coeff_node, ll, &cn->coefficient_list)
1215         {
1216           const struct categoricals *cats = covariance_get_categoricals (cov);
1217           const struct ccase *gcc = categoricals_get_case_by_category (
1218             cats, indep_idx);
1219
1220           if (!contrast_idx)
1221             pivot_category_create_leaf (
1222               indep_var->root, pivot_value_new_var_value (
1223                 cmd->indep_var, case_data (gcc, cmd->indep_var)));
1224
1225           pivot_table_put2 (table, indep_idx++, contrast_idx,
1226                             pivot_value_new_integer (coeffn->coeff));
1227         }
1228     }
1229
1230   pivot_table_submit (table);
1231 }
1232
1233 /* Show the results of the contrast tests */
1234 static void
1235 show_contrast_tests (const struct oneway_spec *cmd, const struct oneway_workspace *ws)
1236 {
1237   struct pivot_table *table = pivot_table_create (N_("Contrast Tests"));
1238
1239   pivot_dimension_create (table, PIVOT_AXIS_COLUMN, N_("Statistics"),
1240                           N_("Value of Contrast"), PIVOT_RC_OTHER,
1241                           N_("Std. Error"), PIVOT_RC_OTHER,
1242                           N_("t"), PIVOT_RC_OTHER,
1243                           N_("df"), PIVOT_RC_OTHER,
1244                           N_("Sig. (2-tailed)"), PIVOT_RC_SIGNIFICANCE);
1245
1246   struct pivot_dimension *contrasts = pivot_dimension_create (
1247     table, PIVOT_AXIS_ROW, N_("Contrast"));
1248   contrasts->root->show_label = true;
1249   int n_contrasts = ll_count (&cmd->contrast_list);
1250   for (int i = 1; i <= n_contrasts; i++)
1251     pivot_category_create_leaf (contrasts->root, pivot_value_new_integer (i));
1252
1253   pivot_dimension_create (table, PIVOT_AXIS_ROW, N_("Assumption"),
1254                           N_("Assume equal variances"),
1255                           N_("Does not assume equal variances"));
1256
1257   struct pivot_dimension *variables = pivot_dimension_create (
1258     table, PIVOT_AXIS_ROW, N_("Dependent Variable"));
1259
1260   for (int v = 0; v < cmd->n_vars; ++v)
1261     {
1262       const struct per_var_ws *pvw = &ws->vws[v];
1263       const struct categoricals *cats = covariance_get_categoricals (pvw->cov);
1264       if (!categoricals_is_complete (cats))
1265         continue;
1266
1267       int var_idx = pivot_category_create_leaf (
1268         variables->root, pivot_value_new_variable (cmd->vars[v]));
1269
1270       struct contrasts_node *cn;
1271       int contrast_idx = 0;
1272       ll_for_each (cn, struct contrasts_node, ll, &cmd->contrast_list)
1273         {
1274
1275           /* Note: The calculation of the degrees of freedom in the
1276              "variances not equal" case is painfull!!
1277              The following formula may help to understand it:
1278              \frac{\left (\sum_{i=1}^k{c_i^2\frac{s_i^2}{n_i}}\right)^2}
1279              {
1280              \sum_{i=1}^k\left (
1281              \frac{\left (c_i^2\frac{s_i^2}{n_i}\right)^2}  {n_i-1}
1282              \right)
1283              }
1284           */
1285
1286           double grand_n;
1287           moments1_calculate (ws->dd_total[v]->mom, &grand_n, NULL, NULL,
1288                               NULL, NULL);
1289           double df = grand_n - pvw->n_groups;
1290
1291           double contrast_value = 0.0;
1292           double coef_msq = 0.0;
1293           double sec_vneq = 0.0;
1294           double df_denominator = 0.0;
1295           double df_numerator = 0.0;
1296           struct coeff_node *coeffn;
1297           int ci = 0;
1298           ll_for_each (coeffn, struct coeff_node, ll, &cn->coefficient_list)
1299             {
1300               const struct descriptive_data *dd
1301                 = categoricals_get_user_data_by_category (cats, ci);
1302               const double coef = coeffn->coeff;
1303
1304               double n, mean, variance;
1305               moments1_calculate (dd->mom, &n, &mean, &variance, NULL, NULL);
1306
1307               double winv = variance / n;
1308               contrast_value += coef * mean;
1309               coef_msq += pow2 (coef) / n;
1310               sec_vneq += pow2 (coef) * variance / n;
1311               df_numerator += pow2 (coef) * winv;
1312               df_denominator += pow2(pow2 (coef) * winv) / (n - 1);
1313
1314               ci++;
1315             }
1316           sec_vneq = sqrt (sec_vneq);
1317           df_numerator = pow2 (df_numerator);
1318
1319           double std_error_contrast = sqrt (pvw->mse * coef_msq);
1320           double T = contrast_value / std_error_contrast;
1321           double T_ne = contrast_value / sec_vneq;
1322           double df_ne = df_numerator / df_denominator;
1323
1324           struct entry
1325             {
1326               int stat_idx;
1327               int assumption_idx;
1328               double x;
1329             }
1330           entries[] =
1331             {
1332               /* Assume equal. */
1333               { 0, 0, contrast_value },
1334               { 1, 0, std_error_contrast },
1335               { 2, 0, T },
1336               { 3, 0, df },
1337               { 4, 0, 2 * gsl_cdf_tdist_Q (fabs(T), df) },
1338               /* Do not assume equal. */
1339               { 0, 1, contrast_value },
1340               { 1, 1, sec_vneq },
1341               { 2, 1, T_ne },
1342               { 3, 1, df_ne },
1343               { 4, 1, 2 * gsl_cdf_tdist_Q (fabs(T_ne), df_ne) },
1344             };
1345
1346           for (size_t i = 0; i < sizeof entries / sizeof *entries; i++)
1347             {
1348               const struct entry *e = &entries[i];
1349               pivot_table_put4 (
1350                 table, e->stat_idx, contrast_idx, e->assumption_idx, var_idx,
1351                 pivot_value_new_number (e->x));
1352             }
1353
1354           contrast_idx++;
1355         }
1356     }
1357
1358   pivot_table_submit (table);
1359 }
1360
1361 static void
1362 show_comparisons (const struct oneway_spec *cmd, const struct oneway_workspace *ws, int v)
1363 {
1364   struct pivot_table *table = pivot_table_create__ (
1365     pivot_value_new_user_text_nocopy (xasprintf (
1366                                         _("Multiple Comparisons (%s)"),
1367                                         var_to_string (cmd->vars[v]))),
1368     "Multiple Comparisons");
1369
1370   struct pivot_dimension *statistics = pivot_dimension_create (
1371     table, PIVOT_AXIS_COLUMN, N_("Statistics"),
1372     N_("Mean Difference (I - J)"), PIVOT_RC_OTHER,
1373     N_("Std. Error"), PIVOT_RC_OTHER,
1374     N_("Sig."), PIVOT_RC_SIGNIFICANCE);
1375   struct pivot_category *interval = pivot_category_create_group__ (
1376     statistics->root,
1377     pivot_value_new_text_format (N_("%g%% Confidence Interval"),
1378                                  (1 - cmd->alpha) * 100.0));
1379   pivot_category_create_leaves (interval,
1380                                 N_("Lower Bound"), PIVOT_RC_OTHER,
1381                                 N_("Upper Bound"), PIVOT_RC_OTHER);
1382
1383   struct pivot_dimension *j_family = pivot_dimension_create (
1384     table, PIVOT_AXIS_ROW, N_("(J) Family"));
1385   j_family->root->show_label = true;
1386
1387   struct pivot_dimension *i_family = pivot_dimension_create (
1388     table, PIVOT_AXIS_ROW, N_("(J) Family"));
1389   i_family->root->show_label = true;
1390
1391   const struct per_var_ws *pvw = &ws->vws[v];
1392   const struct categoricals *cat = pvw->cat;
1393   for (int i = 0; i < pvw->n_groups; i++)
1394     {
1395       const struct ccase *gcc = categoricals_get_case_by_category (cat, i);
1396       for (int j = 0; j < 2; j++)
1397         pivot_category_create_leaf (
1398           j ? j_family->root : i_family->root,
1399           pivot_value_new_var_value (cmd->indep_var,
1400                                      case_data (gcc, cmd->indep_var)));
1401     }
1402
1403   struct pivot_dimension *test = pivot_dimension_create (
1404     table, PIVOT_AXIS_ROW, N_("Test"));
1405
1406   for (int p = 0; p < cmd->n_posthoc; ++p)
1407     {
1408       const struct posthoc *ph = &ph_tests[cmd->posthoc[p]];
1409
1410       int test_idx = pivot_category_create_leaf (
1411         test->root, pivot_value_new_text (ph->label));
1412
1413       for (int i = 0; i < pvw->n_groups ; ++i)
1414         {
1415           struct descriptive_data *dd_i
1416             = categoricals_get_user_data_by_category (cat, i);
1417           double weight_i, mean_i, var_i;
1418           moments1_calculate (dd_i->mom, &weight_i, &mean_i, &var_i, 0, 0);
1419
1420           for (int j = 0 ; j < pvw->n_groups; ++j)
1421             {
1422               if (j == i)
1423                 continue;
1424
1425               struct descriptive_data *dd_j
1426                 = categoricals_get_user_data_by_category (cat, j);
1427               double weight_j, mean_j, var_j;
1428               moments1_calculate (dd_j->mom, &weight_j, &mean_j, &var_j, 0, 0);
1429
1430               double std_err = pvw->mse;
1431               std_err *= weight_i + weight_j;
1432               std_err /= weight_i * weight_j;
1433               std_err = sqrt (std_err);
1434
1435               double sig = 2 * multiple_comparison_sig (std_err, pvw,
1436                                                         dd_i, dd_j, ph);
1437               double half_range = mc_half_range (cmd, pvw, std_err,
1438                                                  dd_i, dd_j, ph);
1439               double entries[] = {
1440                 mean_i - mean_j,
1441                 std_err,
1442                 sig,
1443                 (mean_i - mean_j) - half_range,
1444                 (mean_i - mean_j) + half_range,
1445               };
1446               for (size_t k = 0; k < sizeof entries / sizeof *entries; k++)
1447                 pivot_table_put4 (table, k, j, i, test_idx,
1448                                   pivot_value_new_number (entries[k]));
1449             }
1450         }
1451     }
1452
1453   pivot_table_submit (table);
1454 }