split-file: New function output_split_file_values_peek().
[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                       lex_error (lexer,
521                                  _("The post hoc analysis method %s is not supported."),
522                                  lex_tokcstr (lexer));
523                       goto error;
524                     }
525                 }
526             }
527         }
528       else if (lex_match_id (lexer, "CONTRAST"))
529         {
530           struct contrasts_node *cl = XZALLOC (struct contrasts_node);
531
532           struct ll_list *coefficient_list = &cl->coefficient_list;
533           lex_match (lexer, T_EQUALS);
534
535           ll_init (coefficient_list);
536
537           while (lex_token (lexer) != T_ENDCMD && lex_token (lexer) != T_SLASH)
538             {
539               if (lex_is_number (lexer))
540                 {
541                   struct coeff_node *cc = xmalloc (sizeof *cc);
542                   cc->coeff = lex_number (lexer);
543
544                   ll_push_tail (coefficient_list, &cc->ll);
545                   lex_get (lexer);
546                 }
547               else
548                 {
549                   destroy_coeff_list (cl);
550                   lex_error (lexer, NULL);
551                   goto error;
552                 }
553             }
554
555           if (ll_count (coefficient_list) <= 0)
556             {
557               destroy_coeff_list (cl);
558               goto error;
559             }
560
561           ll_push_tail (&oneway.contrast_list, &cl->ll);
562         }
563       else if (lex_match_id (lexer, "MISSING"))
564         {
565           lex_match (lexer, T_EQUALS);
566           while (lex_token (lexer) != T_ENDCMD && lex_token (lexer) != T_SLASH)
567             {
568               if (lex_match_id (lexer, "INCLUDE"))
569                 {
570                   oneway.exclude = MV_SYSTEM;
571                 }
572               else if (lex_match_id (lexer, "EXCLUDE"))
573                 {
574                   oneway.exclude = MV_ANY;
575                 }
576               else if (lex_match_id (lexer, "LISTWISE"))
577                 {
578                   oneway.missing_type = MISS_LISTWISE;
579                 }
580               else if (lex_match_id (lexer, "ANALYSIS"))
581                 {
582                   oneway.missing_type = MISS_ANALYSIS;
583                 }
584               else
585                 {
586                   lex_error (lexer, NULL);
587                   goto error;
588                 }
589             }
590         }
591       else
592         {
593           lex_error (lexer, NULL);
594           goto error;
595         }
596     }
597
598
599   {
600     struct casegrouper *grouper;
601     struct casereader *group;
602     bool ok;
603
604     grouper = casegrouper_create_splits (proc_open (ds), dict);
605     while (casegrouper_get_next_group (grouper, &group))
606       run_oneway (&oneway, group, ds);
607     ok = casegrouper_destroy (grouper);
608     ok = proc_commit (ds) && ok;
609   }
610
611   oneway_cleanup (&oneway);
612   free (oneway.vars);
613   return CMD_SUCCESS;
614
615  error:
616   oneway_cleanup (&oneway);
617   free (oneway.vars);
618   return CMD_FAILURE;
619 }
620
621
622 \f
623
624
625 static struct descriptive_data *
626 dd_create (const struct variable *var)
627 {
628   struct descriptive_data *dd = xmalloc (sizeof *dd);
629
630   dd->mom = moments1_create (MOMENT_VARIANCE);
631   dd->minimum = DBL_MAX;
632   dd->maximum = -DBL_MAX;
633   dd->var = var;
634
635   return dd;
636 }
637
638 static void
639 dd_destroy (struct descriptive_data *dd)
640 {
641   moments1_destroy (dd->mom);
642   free (dd);
643 }
644
645 static void *
646 makeit (const void *aux1, void *aux2 UNUSED)
647 {
648   const struct variable *var = aux1;
649
650   struct descriptive_data *dd = dd_create (var);
651
652   return dd;
653 }
654
655 static void
656 killit (const void *aux1 UNUSED, void *aux2 UNUSED, void *user_data)
657 {
658   struct descriptive_data *dd = user_data;
659
660   dd_destroy (dd);
661 }
662
663
664 static void
665 updateit (const void *aux1, void *aux2, void *user_data,
666           const struct ccase *c, double weight)
667 {
668   struct descriptive_data *dd = user_data;
669
670   const struct variable *varp = aux1;
671
672   const union value *valx = case_data (c, varp);
673
674   struct descriptive_data *dd_total = aux2;
675
676   moments1_add (dd->mom, valx->f, weight);
677   if (valx->f < dd->minimum)
678     dd->minimum = valx->f;
679
680   if (valx->f > dd->maximum)
681     dd->maximum = valx->f;
682
683   {
684     const struct variable *var = dd_total->var;
685     const union value *val = case_data (c, var);
686
687     moments1_add (dd_total->mom,
688                   val->f,
689                   weight);
690
691     if (val->f < dd_total->minimum)
692       dd_total->minimum = val->f;
693
694     if (val->f > dd_total->maximum)
695       dd_total->maximum = val->f;
696   }
697 }
698
699 static void
700 run_oneway (const struct oneway_spec *cmd,
701             struct casereader *input,
702             const struct dataset *ds)
703 {
704   int v;
705   struct taint *taint;
706   struct dictionary *dict = dataset_dict (ds);
707   struct casereader *reader;
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   output_split_file_values_peek (ds, input);
744
745   taint = taint_clone (casereader_get_taint (input));
746
747   input = casereader_create_filter_missing (input, &cmd->indep_var, 1,
748                                             cmd->exclude, NULL, NULL);
749   if (cmd->missing_type == MISS_LISTWISE)
750     input = casereader_create_filter_missing (input, cmd->vars, cmd->n_vars,
751                                               cmd->exclude, NULL, NULL);
752   input = casereader_create_filter_weight (input, dict, NULL, NULL);
753
754   reader = casereader_clone (input);
755   struct ccase *c;
756   for (; (c = casereader_read (reader)) != NULL; case_unref (c))
757     {
758       int i;
759       double w = dict_get_case_weight (dict, c, NULL);
760
761       for (i = 0; i < cmd->n_vars; ++i)
762         {
763           struct per_var_ws *pvw = &ws.vws[i];
764           const struct variable *v = cmd->vars[i];
765           const union value *val = case_data (c, v);
766
767           if (MISS_ANALYSIS == cmd->missing_type)
768             {
769               if (var_is_value_missing (v, val) & cmd->exclude)
770                 continue;
771             }
772
773           covariance_accumulate_pass1 (pvw->cov, c);
774           levene_pass_one (pvw->nl, val->f, w, case_data (c, cmd->indep_var));
775         }
776     }
777   casereader_destroy (reader);
778
779   reader = casereader_clone (input);
780   for (; (c = casereader_read (reader)); case_unref (c))
781     {
782       int i;
783       double w = dict_get_case_weight (dict, c, NULL);
784       for (i = 0; i < cmd->n_vars; ++i)
785         {
786           struct per_var_ws *pvw = &ws.vws[i];
787           const struct variable *v = cmd->vars[i];
788           const union value *val = case_data (c, v);
789
790           if (MISS_ANALYSIS == cmd->missing_type)
791             {
792               if (var_is_value_missing (v, val) & cmd->exclude)
793                 continue;
794             }
795
796           covariance_accumulate_pass2 (pvw->cov, c);
797           levene_pass_two (pvw->nl, val->f, w, case_data (c, cmd->indep_var));
798         }
799     }
800   casereader_destroy (reader);
801
802   reader = casereader_clone (input);
803   for (; (c = casereader_read (reader)); case_unref (c))
804     {
805       int i;
806       double w = dict_get_case_weight (dict, c, NULL);
807
808       for (i = 0; i < cmd->n_vars; ++i)
809         {
810           struct per_var_ws *pvw = &ws.vws[i];
811           const struct variable *v = cmd->vars[i];
812           const union value *val = case_data (c, v);
813
814           if (MISS_ANALYSIS == cmd->missing_type)
815             {
816               if (var_is_value_missing (v, val) & cmd->exclude)
817                 continue;
818             }
819
820           levene_pass_three (pvw->nl, val->f, w, case_data (c, cmd->indep_var));
821         }
822     }
823   casereader_destroy (reader);
824
825
826   for (v = 0; v < cmd->n_vars; ++v)
827     {
828       const gsl_matrix *ucm;
829       gsl_matrix *cm;
830       struct per_var_ws *pvw = &ws.vws[v];
831       const struct categoricals *cats = covariance_get_categoricals (pvw->cov);
832       const bool ok = categoricals_sane (cats);
833
834       if (! ok)
835         {
836           msg (MW,
837                _("Dependent variable %s has no non-missing values.  No analysis for this variable will be done."),
838                var_get_name (cmd->vars[v]));
839           continue;
840         }
841
842       ucm = covariance_calculate_unnormalized (pvw->cov);
843
844       cm = gsl_matrix_alloc (ucm->size1, ucm->size2);
845       gsl_matrix_memcpy (cm, ucm);
846
847       moments1_calculate (ws.dd_total[v]->mom, &pvw->n, NULL, NULL, NULL, NULL);
848
849       pvw->sst = gsl_matrix_get (cm, 0, 0);
850
851       reg_sweep (cm, 0);
852
853       pvw->sse = gsl_matrix_get (cm, 0, 0);
854       gsl_matrix_free (cm);
855
856       pvw->ssa = pvw->sst - pvw->sse;
857
858       pvw->n_groups = categoricals_n_total (cats);
859
860       pvw->mse = (pvw->sst - pvw->ssa) / (pvw->n - pvw->n_groups);
861     }
862
863   for (v = 0; v < cmd->n_vars; ++v)
864     {
865       const struct categoricals *cats = covariance_get_categoricals (ws.vws[v].cov);
866
867       if (! categoricals_is_complete (cats))
868         {
869           continue;
870         }
871
872       if (categoricals_n_total (cats) > ws.actual_number_of_groups)
873         ws.actual_number_of_groups = categoricals_n_total (cats);
874     }
875
876   casereader_destroy (input);
877
878   if (!taint_has_tainted_successor (taint))
879     output_oneway (cmd, &ws);
880
881   taint_destroy (taint);
882
883   for (v = 0; v < cmd->n_vars; ++v)
884     {
885       covariance_destroy (ws.vws[v].cov);
886       levene_destroy (ws.vws[v].nl);
887       dd_destroy (ws.dd_total[v]);
888       interaction_destroy (ws.vws[v].iact);
889     }
890
891   free (ws.vws);
892   free (ws.dd_total);
893 }
894
895 static void show_contrast_coeffs (const struct oneway_spec *cmd, const struct oneway_workspace *ws);
896 static void show_contrast_tests (const struct oneway_spec *cmd, const struct oneway_workspace *ws);
897 static void show_comparisons (const struct oneway_spec *cmd, const struct oneway_workspace *ws, int depvar);
898
899 static void
900 output_oneway (const struct oneway_spec *cmd, struct oneway_workspace *ws)
901 {
902   size_t i = 0;
903
904   /* Check the sanity of the given contrast values */
905   struct contrasts_node *coeff_list  = NULL;
906   struct contrasts_node *coeff_next  = NULL;
907   ll_for_each_safe (coeff_list, coeff_next, struct contrasts_node, ll, &cmd->contrast_list)
908     {
909       struct coeff_node *cn = NULL;
910       double sum = 0;
911       struct ll_list *cl = &coeff_list->coefficient_list;
912       ++i;
913
914       if (ll_count (cl) != ws->actual_number_of_groups)
915         {
916           msg (SW,
917                _("In contrast list %zu, the number of coefficients (%zu) does not equal the number of groups (%d). This contrast list will be ignored."),
918                i, ll_count (cl), ws->actual_number_of_groups);
919
920           ll_remove (&coeff_list->ll);
921           destroy_coeff_list (coeff_list);
922           continue;
923         }
924
925       ll_for_each (cn, struct coeff_node, ll, cl)
926         sum += cn->coeff;
927
928       if (sum != 0.0)
929         msg (SW, _("Coefficients for contrast %zu do not total zero"), i);
930     }
931
932   if (cmd->stats & STATS_DESCRIPTIVES)
933     show_descriptives (cmd, ws);
934
935   if (cmd->stats & STATS_HOMOGENEITY)
936     show_homogeneity (cmd, ws);
937
938   show_anova_table (cmd, ws);
939
940   if (ll_count (&cmd->contrast_list) > 0)
941     {
942       show_contrast_coeffs (cmd, ws);
943       show_contrast_tests (cmd, ws);
944     }
945
946   if (cmd->posthoc)
947     {
948       int v;
949       for (v = 0 ; v < cmd->n_vars; ++v)
950         {
951           const struct categoricals *cats = covariance_get_categoricals (ws->vws[v].cov);
952
953           if (categoricals_is_complete (cats))
954             show_comparisons (cmd, ws, v);
955         }
956     }
957 }
958
959
960 /* Show the ANOVA table */
961 static void
962 show_anova_table (const struct oneway_spec *cmd, const struct oneway_workspace *ws)
963 {
964   struct pivot_table *table = pivot_table_create (N_("ANOVA"));
965
966   pivot_dimension_create (table, PIVOT_AXIS_COLUMN, N_("Statistics"),
967                           N_("Sum of Squares"), PIVOT_RC_OTHER,
968                           N_("df"), PIVOT_RC_INTEGER,
969                           N_("Mean Square"), PIVOT_RC_OTHER,
970                           N_("F"), PIVOT_RC_OTHER,
971                           N_("Sig."), PIVOT_RC_SIGNIFICANCE);
972
973   pivot_dimension_create (table, PIVOT_AXIS_ROW, N_("Type"),
974                           N_("Between Groups"), N_("Within Groups"),
975                           N_("Total"));
976
977   struct pivot_dimension *variables = pivot_dimension_create (
978     table, PIVOT_AXIS_ROW, N_("Variables"));
979
980   for (size_t i = 0; i < cmd->n_vars; ++i)
981     {
982       int var_idx = pivot_category_create_leaf (
983         variables->root, pivot_value_new_variable (cmd->vars[i]));
984
985       const struct per_var_ws *pvw = &ws->vws[i];
986
987       double n;
988       moments1_calculate (ws->dd_total[i]->mom, &n, NULL, NULL, NULL, NULL);
989
990       double df1 = pvw->n_groups - 1;
991       double df2 = n - pvw->n_groups;
992       double msa = pvw->ssa / df1;
993       double F = msa / pvw->mse ;
994
995       struct entry
996         {
997           int stat_idx;
998           int type_idx;
999           double x;
1000         }
1001       entries[] = {
1002         /* Sums of Squares. */
1003         { 0, 0, pvw->ssa },
1004         { 0, 1, pvw->sse },
1005         { 0, 2, pvw->sst },
1006         /* Degrees of Freedom. */
1007         { 1, 0, df1 },
1008         { 1, 1, df2 },
1009         { 1, 2, n - 1 },
1010         /* Mean Squares. */
1011         { 2, 0, msa },
1012         { 2, 1, pvw->mse },
1013         /* F. */
1014         { 3, 0, F },
1015         /* Significance. */
1016         { 4, 0, gsl_cdf_fdist_Q (F, df1, df2) },
1017       };
1018       for (size_t j = 0; j < sizeof entries / sizeof *entries; j++)
1019         {
1020           const struct entry *e = &entries[j];
1021           pivot_table_put3 (table, e->stat_idx, e->type_idx, var_idx,
1022                             pivot_value_new_number (e->x));
1023         }
1024     }
1025
1026   pivot_table_submit (table);
1027 }
1028
1029 /* Show the descriptives table */
1030 static void
1031 show_descriptives (const struct oneway_spec *cmd, const struct oneway_workspace *ws)
1032 {
1033   if (!cmd->n_vars)
1034     return;
1035   const struct categoricals *cats = covariance_get_categoricals (
1036     ws->vws[0].cov);
1037
1038   struct pivot_table *table = pivot_table_create (N_("Descriptives"));
1039   pivot_table_set_weight_format (table, cmd->wfmt);
1040
1041   const double confidence = 0.95;
1042
1043   struct pivot_dimension *statistics = pivot_dimension_create (
1044     table, PIVOT_AXIS_COLUMN, N_("Statistics"),
1045     N_("N"), PIVOT_RC_COUNT, N_("Mean"), N_("Std. Deviation"),
1046     N_("Std. Error"));
1047   struct pivot_category *interval = pivot_category_create_group__ (
1048     statistics->root,
1049     pivot_value_new_text_format (N_("%g%% Confidence Interval for Mean"),
1050                                  confidence * 100.0));
1051   pivot_category_create_leaves (interval, N_("Lower Bound"),
1052                                 N_("Upper Bound"));
1053   pivot_category_create_leaves (statistics->root,
1054                                 N_("Minimum"), N_("Maximum"));
1055
1056   struct pivot_dimension *indep_var = pivot_dimension_create__ (
1057     table, PIVOT_AXIS_ROW, pivot_value_new_variable (cmd->indep_var));
1058   indep_var->root->show_label = true;
1059   size_t n;
1060   union value *values = categoricals_get_var_values (cats, cmd->indep_var, &n);
1061   for (size_t j = 0; j < n; j++)
1062     pivot_category_create_leaf (
1063       indep_var->root, pivot_value_new_var_value (cmd->indep_var, &values[j]));
1064   pivot_category_create_leaf (
1065     indep_var->root, pivot_value_new_text_format (N_("Total")));
1066
1067   struct pivot_dimension *dep_var = pivot_dimension_create (
1068     table, PIVOT_AXIS_ROW, N_("Dependent Variable"));
1069
1070   const double q = (1.0 - confidence) / 2.0;
1071   for (int v = 0; v < cmd->n_vars; ++v)
1072     {
1073       int dep_var_idx = pivot_category_create_leaf (
1074         dep_var->root, pivot_value_new_variable (cmd->vars[v]));
1075
1076       struct per_var_ws *pvw = &ws->vws[v];
1077       const struct categoricals *cats = covariance_get_categoricals (pvw->cov);
1078
1079       int count;
1080       for (count = 0; count < categoricals_n_total (cats); ++count)
1081         {
1082           const struct descriptive_data *dd
1083             = categoricals_get_user_data_by_category (cats, count);
1084
1085           double n, mean, variance;
1086           moments1_calculate (dd->mom, &n, &mean, &variance, NULL, NULL);
1087
1088           double std_dev = sqrt (variance);
1089           double std_error = std_dev / sqrt (n) ;
1090           double T = gsl_cdf_tdist_Qinv (q, n - 1);
1091
1092           double entries[] = {
1093             n,
1094             mean,
1095             std_dev,
1096             std_error,
1097             mean - T * std_error,
1098             mean + T * std_error,
1099             dd->minimum,
1100             dd->maximum,
1101           };
1102           for (size_t i = 0; i < sizeof entries / sizeof *entries; i++)
1103             pivot_table_put3 (table, i, count, dep_var_idx,
1104                               pivot_value_new_number (entries[i]));
1105         }
1106
1107       if (categoricals_is_complete (cats))
1108         {
1109           double n, mean, variance;
1110           moments1_calculate (ws->dd_total[v]->mom, &n, &mean, &variance,
1111                               NULL, NULL);
1112
1113           double std_dev = sqrt (variance);
1114           double std_error = std_dev / sqrt (n) ;
1115           double T = gsl_cdf_tdist_Qinv (q, n - 1);
1116
1117           double entries[] = {
1118             n,
1119             mean,
1120             std_dev,
1121             std_error,
1122             mean - T * std_error,
1123             mean + T * std_error,
1124             ws->dd_total[v]->minimum,
1125             ws->dd_total[v]->maximum,
1126           };
1127           for (size_t i = 0; i < sizeof entries / sizeof *entries; i++)
1128             pivot_table_put3 (table, i, count, dep_var_idx,
1129                               pivot_value_new_number (entries[i]));
1130         }
1131     }
1132
1133   pivot_table_submit (table);
1134 }
1135
1136 /* Show the homogeneity table */
1137 static void
1138 show_homogeneity (const struct oneway_spec *cmd, const struct oneway_workspace *ws)
1139 {
1140   struct pivot_table *table = pivot_table_create (
1141     N_("Test of Homogeneity of Variances"));
1142
1143   pivot_dimension_create (table, PIVOT_AXIS_COLUMN, N_("Statistics"),
1144                           N_("Levene Statistic"), PIVOT_RC_OTHER,
1145                           N_("df1"), PIVOT_RC_INTEGER,
1146                           N_("df2"), PIVOT_RC_INTEGER,
1147                           N_("Sig."), PIVOT_RC_SIGNIFICANCE);
1148
1149   struct pivot_dimension *variables = pivot_dimension_create (
1150     table, PIVOT_AXIS_ROW, N_("Variables"));
1151
1152   for (int v = 0; v < cmd->n_vars; ++v)
1153     {
1154       int var_idx = pivot_category_create_leaf (
1155         variables->root, pivot_value_new_variable (cmd->vars[v]));
1156
1157       double n;
1158       moments1_calculate (ws->dd_total[v]->mom, &n, NULL, NULL, NULL, NULL);
1159
1160       const struct per_var_ws *pvw = &ws->vws[v];
1161       double df1 = pvw->n_groups - 1;
1162       double df2 = n - pvw->n_groups;
1163       double F = levene_calculate (pvw->nl);
1164
1165       double entries[] =
1166         {
1167           F,
1168           df1,
1169           df2,
1170           gsl_cdf_fdist_Q (F, df1, df2),
1171         };
1172       for (size_t i = 0; i < sizeof entries / sizeof *entries; i++)
1173         pivot_table_put2 (table, i, var_idx,
1174                           pivot_value_new_number (entries[i]));
1175     }
1176
1177   pivot_table_submit (table);
1178 }
1179
1180
1181 /* Show the contrast coefficients table */
1182 static void
1183 show_contrast_coeffs (const struct oneway_spec *cmd, const struct oneway_workspace *ws)
1184 {
1185   struct pivot_table *table = pivot_table_create (N_("Contrast Coefficients"));
1186
1187   struct pivot_dimension *indep_var = pivot_dimension_create__ (
1188     table, PIVOT_AXIS_COLUMN, pivot_value_new_variable (cmd->indep_var));
1189   indep_var->root->show_label = true;
1190
1191   struct pivot_dimension *contrast = pivot_dimension_create (
1192     table, PIVOT_AXIS_ROW, N_("Contrast"));
1193   contrast->root->show_label = true;
1194
1195   const struct covariance *cov = ws->vws[0].cov;
1196
1197   const struct contrasts_node *cn;
1198   int c_num = 1;
1199   ll_for_each (cn, struct contrasts_node, ll, &cmd->contrast_list)
1200     {
1201       int contrast_idx = pivot_category_create_leaf (
1202         contrast->root, pivot_value_new_integer (c_num++));
1203
1204       const struct coeff_node *coeffn;
1205       int indep_idx = 0;
1206       ll_for_each (coeffn, struct coeff_node, ll, &cn->coefficient_list)
1207         {
1208           const struct categoricals *cats = covariance_get_categoricals (cov);
1209           const struct ccase *gcc = categoricals_get_case_by_category (
1210             cats, indep_idx);
1211
1212           if (!contrast_idx)
1213             pivot_category_create_leaf (
1214               indep_var->root, pivot_value_new_var_value (
1215                 cmd->indep_var, case_data (gcc, cmd->indep_var)));
1216
1217           pivot_table_put2 (table, indep_idx++, contrast_idx,
1218                             pivot_value_new_integer (coeffn->coeff));
1219         }
1220     }
1221
1222   pivot_table_submit (table);
1223 }
1224
1225 /* Show the results of the contrast tests */
1226 static void
1227 show_contrast_tests (const struct oneway_spec *cmd, const struct oneway_workspace *ws)
1228 {
1229   struct pivot_table *table = pivot_table_create (N_("Contrast Tests"));
1230
1231   pivot_dimension_create (table, PIVOT_AXIS_COLUMN, N_("Statistics"),
1232                           N_("Value of Contrast"), PIVOT_RC_OTHER,
1233                           N_("Std. Error"), PIVOT_RC_OTHER,
1234                           N_("t"), PIVOT_RC_OTHER,
1235                           N_("df"), PIVOT_RC_OTHER,
1236                           N_("Sig. (2-tailed)"), PIVOT_RC_SIGNIFICANCE);
1237
1238   struct pivot_dimension *contrasts = pivot_dimension_create (
1239     table, PIVOT_AXIS_ROW, N_("Contrast"));
1240   contrasts->root->show_label = true;
1241   int n_contrasts = ll_count (&cmd->contrast_list);
1242   for (int i = 1; i <= n_contrasts; i++)
1243     pivot_category_create_leaf (contrasts->root, pivot_value_new_integer (i));
1244
1245   pivot_dimension_create (table, PIVOT_AXIS_ROW, N_("Assumption"),
1246                           N_("Assume equal variances"),
1247                           N_("Does not assume equal variances"));
1248
1249   struct pivot_dimension *variables = pivot_dimension_create (
1250     table, PIVOT_AXIS_ROW, N_("Dependent Variable"));
1251
1252   for (int v = 0; v < cmd->n_vars; ++v)
1253     {
1254       const struct per_var_ws *pvw = &ws->vws[v];
1255       const struct categoricals *cats = covariance_get_categoricals (pvw->cov);
1256       if (!categoricals_is_complete (cats))
1257         continue;
1258
1259       int var_idx = pivot_category_create_leaf (
1260         variables->root, pivot_value_new_variable (cmd->vars[v]));
1261
1262       struct contrasts_node *cn;
1263       int contrast_idx = 0;
1264       ll_for_each (cn, struct contrasts_node, ll, &cmd->contrast_list)
1265         {
1266
1267           /* Note: The calculation of the degrees of freedom in the
1268              "variances not equal" case is painfull!!
1269              The following formula may help to understand it:
1270              \frac{\left (\sum_{i=1}^k{c_i^2\frac{s_i^2}{n_i}}\right)^2}
1271              {
1272              \sum_{i=1}^k\left (
1273              \frac{\left (c_i^2\frac{s_i^2}{n_i}\right)^2}  {n_i-1}
1274              \right)
1275              }
1276           */
1277
1278           double grand_n;
1279           moments1_calculate (ws->dd_total[v]->mom, &grand_n, NULL, NULL,
1280                               NULL, NULL);
1281           double df = grand_n - pvw->n_groups;
1282
1283           double contrast_value = 0.0;
1284           double coef_msq = 0.0;
1285           double sec_vneq = 0.0;
1286           double df_denominator = 0.0;
1287           double df_numerator = 0.0;
1288           struct coeff_node *coeffn;
1289           int ci = 0;
1290           ll_for_each (coeffn, struct coeff_node, ll, &cn->coefficient_list)
1291             {
1292               const struct descriptive_data *dd
1293                 = categoricals_get_user_data_by_category (cats, ci);
1294               const double coef = coeffn->coeff;
1295
1296               double n, mean, variance;
1297               moments1_calculate (dd->mom, &n, &mean, &variance, NULL, NULL);
1298
1299               double winv = variance / n;
1300               contrast_value += coef * mean;
1301               coef_msq += pow2 (coef) / n;
1302               sec_vneq += pow2 (coef) * variance / n;
1303               df_numerator += pow2 (coef) * winv;
1304               df_denominator += pow2(pow2 (coef) * winv) / (n - 1);
1305
1306               ci++;
1307             }
1308           sec_vneq = sqrt (sec_vneq);
1309           df_numerator = pow2 (df_numerator);
1310
1311           double std_error_contrast = sqrt (pvw->mse * coef_msq);
1312           double T = contrast_value / std_error_contrast;
1313           double T_ne = contrast_value / sec_vneq;
1314           double df_ne = df_numerator / df_denominator;
1315
1316           struct entry
1317             {
1318               int stat_idx;
1319               int assumption_idx;
1320               double x;
1321             }
1322           entries[] =
1323             {
1324               /* Assume equal. */
1325               { 0, 0, contrast_value },
1326               { 1, 0, std_error_contrast },
1327               { 2, 0, T },
1328               { 3, 0, df },
1329               { 4, 0, 2 * gsl_cdf_tdist_Q (fabs(T), df) },
1330               /* Do not assume equal. */
1331               { 0, 1, contrast_value },
1332               { 1, 1, sec_vneq },
1333               { 2, 1, T_ne },
1334               { 3, 1, df_ne },
1335               { 4, 1, 2 * gsl_cdf_tdist_Q (fabs(T_ne), df_ne) },
1336             };
1337
1338           for (size_t i = 0; i < sizeof entries / sizeof *entries; i++)
1339             {
1340               const struct entry *e = &entries[i];
1341               pivot_table_put4 (
1342                 table, e->stat_idx, contrast_idx, e->assumption_idx, var_idx,
1343                 pivot_value_new_number (e->x));
1344             }
1345
1346           contrast_idx++;
1347         }
1348     }
1349
1350   pivot_table_submit (table);
1351 }
1352
1353 static void
1354 show_comparisons (const struct oneway_spec *cmd, const struct oneway_workspace *ws, int v)
1355 {
1356   struct pivot_table *table = pivot_table_create__ (
1357     pivot_value_new_user_text_nocopy (xasprintf (
1358                                         _("Multiple Comparisons (%s)"),
1359                                         var_to_string (cmd->vars[v]))),
1360     "Multiple Comparisons");
1361
1362   struct pivot_dimension *statistics = pivot_dimension_create (
1363     table, PIVOT_AXIS_COLUMN, N_("Statistics"),
1364     N_("Mean Difference (I - J)"), PIVOT_RC_OTHER,
1365     N_("Std. Error"), PIVOT_RC_OTHER,
1366     N_("Sig."), PIVOT_RC_SIGNIFICANCE);
1367   struct pivot_category *interval = pivot_category_create_group__ (
1368     statistics->root,
1369     pivot_value_new_text_format (N_("%g%% Confidence Interval"),
1370                                  (1 - cmd->alpha) * 100.0));
1371   pivot_category_create_leaves (interval,
1372                                 N_("Lower Bound"), PIVOT_RC_OTHER,
1373                                 N_("Upper Bound"), PIVOT_RC_OTHER);
1374
1375   struct pivot_dimension *j_family = pivot_dimension_create (
1376     table, PIVOT_AXIS_ROW, N_("(J) Family"));
1377   j_family->root->show_label = true;
1378
1379   struct pivot_dimension *i_family = pivot_dimension_create (
1380     table, PIVOT_AXIS_ROW, N_("(J) Family"));
1381   i_family->root->show_label = true;
1382
1383   const struct per_var_ws *pvw = &ws->vws[v];
1384   const struct categoricals *cat = pvw->cat;
1385   for (int i = 0; i < pvw->n_groups; i++)
1386     {
1387       const struct ccase *gcc = categoricals_get_case_by_category (cat, i);
1388       for (int j = 0; j < 2; j++)
1389         pivot_category_create_leaf (
1390           j ? j_family->root : i_family->root,
1391           pivot_value_new_var_value (cmd->indep_var,
1392                                      case_data (gcc, cmd->indep_var)));
1393     }
1394
1395   struct pivot_dimension *test = pivot_dimension_create (
1396     table, PIVOT_AXIS_ROW, N_("Test"));
1397
1398   for (int p = 0; p < cmd->n_posthoc; ++p)
1399     {
1400       const struct posthoc *ph = &ph_tests[cmd->posthoc[p]];
1401
1402       int test_idx = pivot_category_create_leaf (
1403         test->root, pivot_value_new_text (ph->label));
1404
1405       for (int i = 0; i < pvw->n_groups ; ++i)
1406         {
1407           struct descriptive_data *dd_i
1408             = categoricals_get_user_data_by_category (cat, i);
1409           double weight_i, mean_i, var_i;
1410           moments1_calculate (dd_i->mom, &weight_i, &mean_i, &var_i, 0, 0);
1411
1412           for (int j = 0 ; j < pvw->n_groups; ++j)
1413             {
1414               if (j == i)
1415                 continue;
1416
1417               struct descriptive_data *dd_j
1418                 = categoricals_get_user_data_by_category (cat, j);
1419               double weight_j, mean_j, var_j;
1420               moments1_calculate (dd_j->mom, &weight_j, &mean_j, &var_j, 0, 0);
1421
1422               double std_err = pvw->mse;
1423               std_err *= weight_i + weight_j;
1424               std_err /= weight_i * weight_j;
1425               std_err = sqrt (std_err);
1426
1427               double sig = 2 * multiple_comparison_sig (std_err, pvw,
1428                                                         dd_i, dd_j, ph);
1429               double half_range = mc_half_range (cmd, pvw, std_err,
1430                                                  dd_i, dd_j, ph);
1431               double entries[] = {
1432                 mean_i - mean_j,
1433                 std_err,
1434                 sig,
1435                 (mean_i - mean_j) - half_range,
1436                 (mean_i - mean_j) + half_range,
1437               };
1438               for (size_t k = 0; k < sizeof entries / sizeof *entries; k++)
1439                 pivot_table_put4 (table, k, j, i, test_idx,
1440                                   pivot_value_new_number (entries[k]));
1441             }
1442         }
1443     }
1444
1445   pivot_table_submit (table);
1446 }