CTABLES: Fix treatment of multiline titles.
[pspp] / src / language / stats / reliability.c
index 3633b3dd8937e048e4062c3f1ae20df5d0c229b4..02253251a5975210ef18de4ae30c128379479260 100644 (file)
@@ -1,5 +1,5 @@
 /* PSPP - a program for statistical analysis.
-   Copyright (C) 2009, 2010 Free Software Foundation, Inc.
+   Copyright (C) 2009, 2010, 2011, 2013, 2015, 2016 Free Software Foundation, Inc.
 
    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
 
 #include <math.h>
 
-#include <libpspp/misc.h>
-
-#include <libpspp/str.h>
-#include <libpspp/message.h>
-
-
-#include <data/procedure.h>
-#include <data/missing-values.h>
-#include <data/casereader.h>
-#include <data/casegrouper.h>
-#include <data/dictionary.h>
-#include <data/format.h>
-
-#include <language/lexer/variable-parser.h>
-#include <language/command.h>
-#include <language/lexer/lexer.h>
-
-#include <math/moments.h>
-#include <output/tab.h>
-#include <output/text-item.h>
+#include "data/casegrouper.h"
+#include "data/casereader.h"
+#include "data/dataset.h"
+#include "data/dictionary.h"
+#include "data/format.h"
+#include "data/missing-values.h"
+#include "language/command.h"
+#include "language/lexer/lexer.h"
+#include "language/lexer/variable-parser.h"
+#include "libpspp/message.h"
+#include "libpspp/misc.h"
+#include "libpspp/str.h"
+#include "math/moments.h"
+#include "output/pivot-table.h"
+#include "output/output-item.h"
 
 #include "gettext.h"
 #define _(msgid) gettext (msgid)
 #define N_(msgid) msgid
 
 struct cronbach
-{
-  const struct variable **items;
-  size_t n_items;
-  double alpha;
-  double sum_of_variances;
-  double variance_of_sums;
-  int totals_idx;          /* Casereader index into the totals */
-
-  struct moments1 **m ;    /* Moments of the items */
-  struct moments1 *total ; /* Moments of the totals */
-};
+  {
+    const struct variable **items;
+    size_t n_items;
+    double alpha;
+    double sum_of_variances;
+    double variance_of_sums;
+    int totals_idx;          /* Casereader index into the totals */
+
+    struct moments1 **m;    /* Moments of the items */
+    struct moments1 *total; /* Moments of the totals */
+  };
 
 #if 0
 static void
@@ -62,7 +57,7 @@ dump_cronbach (const struct cronbach *s)
 {
   int i;
   printf ("N items %d\n", s->n_items);
-  for (i = 0 ; i < s->n_items; ++i)
+  for (i = 0; i < s->n_items; ++i)
     {
       printf ("%s\n", var_get_name (s->items[i]));
     }
@@ -82,16 +77,10 @@ enum model
   };
 
 
-enum summary_opts
-  {
-    SUMMARY_TOTAL = 0x0001,
-  };
-
-
 struct reliability
 {
-  const struct variable **variables;
-  size_t n_variables;
+  const struct variable **vars;
+  size_t n_vars;
   enum mv_class exclude;
 
   struct cronbach *sc;
@@ -99,13 +88,12 @@ struct reliability
 
   int total_start;
 
-  struct string scale_name;
+  char *scale_name;
 
   enum model model;
   int split_point;
 
-
-  enum summary_opts summary;
+  bool summary_total;
 
   const struct variable *wv;
 };
@@ -113,59 +101,67 @@ struct reliability
 
 static bool run_reliability (struct dataset *ds, const struct reliability *reliability);
 
+static void
+reliability_destroy (struct reliability *rel)
+{
+  int j;
+  free (rel->scale_name);
+  if (rel->sc)
+    for (j = 0; j < rel->n_sc; ++j)
+      {
+       int x;
+       free (rel->sc[j].items);
+        moments1_destroy (rel->sc[j].total);
+        if (rel->sc[j].m)
+          for (x = 0; x < rel->sc[j].n_items; ++x)
+            free (rel->sc[j].m[x]);
+       free (rel->sc[j].m);
+      }
+
+  free (rel->sc);
+  free (rel->vars);
+}
+
 int
 cmd_reliability (struct lexer *lexer, struct dataset *ds)
 {
   const struct dictionary *dict = dataset_dict (ds);
 
-  struct reliability reliability;
-  reliability.n_variables = 0;
-  reliability.variables = NULL;
-  reliability.model = MODEL_ALPHA;
-    reliability.exclude = MV_ANY;
-  reliability.summary = 0;
-
-  reliability.wv = dict_get_weight (dict);
-
-  reliability.total_start = 0;
+  struct reliability r = {
+    .model = MODEL_ALPHA,
+    .exclude = MV_ANY,
+    .wv = dict_get_weight (dict),
+    .scale_name = xstrdup ("ANY"),
+  };
 
   lex_match (lexer, T_SLASH);
 
   if (!lex_force_match_id (lexer, "VARIABLES"))
-    {
-      goto error;
-    }
+    goto error;
 
   lex_match (lexer, T_EQUALS);
 
-  if (!parse_variables_const (lexer, dict, &reliability.variables, &reliability.n_variables,
+  int vars_start = lex_ofs (lexer);
+  if (!parse_variables_const (lexer, dict, &r.vars, &r.n_vars,
                              PV_NO_DUPLICATE | PV_NUMERIC))
     goto error;
+  int vars_end = lex_ofs (lexer) - 1;
 
-  if (reliability.n_variables < 2)
-    msg (MW, _("Reliability on a single variable is not useful."));
-
-
-    {
-      int i;
-      struct cronbach *c;
-      /* Create a default Scale */
-
-      reliability.n_sc = 1;
-      reliability.sc = xzalloc (sizeof (struct cronbach) * reliability.n_sc);
-
-      ds_init_cstr (&reliability.scale_name, "ANY");
-
-      c = &reliability.sc[0];
-      c->n_items = reliability.n_variables;
-      c->items = xzalloc (sizeof (struct variable*) * c->n_items);
-
-      for (i = 0 ; i < c->n_items ; ++i)
-       c->items[i] = reliability.variables[i];
-    }
+  if (r.n_vars < 2)
+    lex_ofs_msg (lexer, SW, vars_start, vars_end,
+                 _("Reliability on a single variable is not useful."));
 
+  /* Create a default scale. */
+  r.n_sc = 1;
+  r.sc = xcalloc (r.n_sc, sizeof (struct cronbach));
 
+  struct cronbach *c = &r.sc[0];
+  c->n_items = r.n_vars;
+  c->items = xcalloc (c->n_items, sizeof (struct variable*));
+  for (size_t i = 0; i < c->n_items; ++i)
+    c->items[i] = r.vars[i];
 
+  int split_ofs = 0;
   while (lex_token (lexer) != T_ENDCMD)
     {
       lex_match (lexer, T_SLASH);
@@ -173,25 +169,24 @@ cmd_reliability (struct lexer *lexer, struct dataset *ds)
       if (lex_match_id (lexer, "SCALE"))
        {
          struct const_var_set *vs;
-         if ( ! lex_force_match (lexer, T_LPAREN))
+         if (!lex_force_match (lexer, T_LPAREN))
            goto error;
 
-         if ( ! lex_force_string (lexer) ) 
+         if (!lex_force_string (lexer))
            goto error;
-
-         ds_init_substring (&reliability.scale_name, lex_tokss (lexer));
-
+          free (r.scale_name);
+          r.scale_name = xstrdup (lex_tokcstr (lexer));
          lex_get (lexer);
 
-         if ( ! lex_force_match (lexer, T_RPAREN))
+         if (!lex_force_match (lexer, T_RPAREN))
            goto error;
 
           lex_match (lexer, T_EQUALS);
 
-         vs = const_var_set_create_from_array (reliability.variables, reliability.n_variables);
-
+         vs = const_var_set_create_from_array (r.vars, r.n_vars);
 
-         if (!parse_const_var_set_vars (lexer, vs, &reliability.sc->items, &reliability.sc->n_items, 0))
+         free (r.sc->items);
+         if (!parse_const_var_set_vars (lexer, vs, &r.sc->items, &r.sc->n_items, 0))
            {
              const_var_set_destroy (vs);
              goto error;
@@ -203,38 +198,39 @@ cmd_reliability (struct lexer *lexer, struct dataset *ds)
        {
           lex_match (lexer, T_EQUALS);
          if (lex_match_id (lexer, "ALPHA"))
-           {
-             reliability.model = MODEL_ALPHA;
-           }
+            r.model = MODEL_ALPHA;
          else if (lex_match_id (lexer, "SPLIT"))
            {
-             reliability.model = MODEL_SPLIT;
-             reliability.split_point = -1;
-
-             if ( lex_match (lexer, T_LPAREN))
-               {
-                 lex_force_num (lexer);
-                 reliability.split_point = lex_number (lexer);
+             r.model = MODEL_SPLIT;
+             r.split_point = -1;
+
+             if (lex_match (lexer, T_LPAREN))
+                {
+                  if (!lex_force_num (lexer))
+                    goto error;
+                  split_ofs = lex_ofs (lexer);
+                 r.split_point = lex_number (lexer);
                  lex_get (lexer);
-                 lex_force_match (lexer, T_RPAREN);
+                 if (!lex_force_match (lexer, T_RPAREN))
+                   goto error;
                }
            }
          else
-           goto error;
+            {
+              lex_error_expecting (lexer, "ALPHA", "SPLIT");
+              goto error;
+            }
        }
       else if (lex_match_id (lexer, "SUMMARY"))
         {
           lex_match (lexer, T_EQUALS);
-         if (lex_match_id (lexer, "TOTAL"))
-           {
-             reliability.summary |= SUMMARY_TOTAL;
-           }
-         else if (lex_match (lexer, T_ALL))
-           {
-             reliability.summary = 0xFFFF;
-           }
+         if (lex_match_id (lexer, "TOTAL") || lex_match (lexer, T_ALL))
+            r.summary_total = true;
          else
-           goto error;
+            {
+              lex_error_expecting (lexer, "TOTAL", "ALL");
+              goto error;
+            }
        }
       else if (lex_match_id (lexer, "MISSING"))
         {
@@ -242,93 +238,105 @@ cmd_reliability (struct lexer *lexer, struct dataset *ds)
           while (lex_token (lexer) != T_ENDCMD && lex_token (lexer) != T_SLASH)
             {
              if (lex_match_id (lexer, "INCLUDE"))
+                r.exclude = MV_SYSTEM;
+              else if (lex_match_id (lexer, "EXCLUDE"))
+                r.exclude = MV_ANY;
+              else
                {
-                 reliability.exclude = MV_SYSTEM;
-               }
-             else if (lex_match_id (lexer, "EXCLUDE"))
-               {
-                 reliability.exclude = MV_ANY;
-               }
-             else
-               {
-                  lex_error (lexer, NULL);
+                  lex_error_expecting (lexer, "INCLUDE", "EXCLUDE");
                  goto error;
                }
            }
        }
+      else if (lex_match_id (lexer, "STATISTICS"))
+        {
+          int statistics_start = lex_ofs (lexer) - 1;
+          lex_match (lexer, T_EQUALS);
+          while (lex_match (lexer, T_ID))
+            continue;
+          int statistics_end = lex_ofs (lexer) - 1;
+
+          lex_ofs_msg (lexer, SW, statistics_start, statistics_end,
+                       _("The STATISTICS subcommand is not yet implemented.  "
+                         "No statistics will be produced."));
+        }
       else
        {
-         lex_error (lexer, NULL);
+         lex_error_expecting (lexer, "SCALE", "MODEL", "SUMMARY", "MISSING",
+                               "STATISTICS");
          goto error;
        }
     }
 
-  if ( reliability.model == MODEL_SPLIT)
+  if (r.model == MODEL_SPLIT)
     {
-      int i;
-      const struct cronbach *s;
-
-      reliability.n_sc += 2 ;
-      reliability.sc = xrealloc (reliability.sc, sizeof (struct cronbach) * reliability.n_sc);
-
-      s = &reliability.sc[0];
+      if (r.split_point >= r.n_vars)
+        {
+          lex_ofs_error (lexer, split_ofs, split_ofs,
+                         _("The split point must be less than the "
+                           "number of variables."));
+          lex_ofs_msg (lexer, SN, vars_start, vars_end,
+                       ngettext ("There is %zu variable.",
+                                 "There are %zu variables.", r.n_vars),
+                       r.n_vars);
+          goto error;
+        }
 
-      reliability.sc[1].n_items =
-       (reliability.split_point == -1) ? s->n_items / 2 : reliability.split_point;
+      r.n_sc += 2;
+      r.sc = xrealloc (r.sc, sizeof (struct cronbach) * r.n_sc);
 
-      reliability.sc[2].n_items = s->n_items - reliability.sc[1].n_items;
-      reliability.sc[1].items = xzalloc (sizeof (struct variable *)
-                                * reliability.sc[1].n_items);
+      const struct cronbach *s = &r.sc[0];
 
-      reliability.sc[2].items = xzalloc (sizeof (struct variable *) *
-                                reliability.sc[2].n_items);
+      r.sc[1].n_items = r.split_point == -1 ? s->n_items / 2 : r.split_point;
 
-      for  (i = 0; i < reliability.sc[1].n_items ; ++i)
-       reliability.sc[1].items[i] = s->items[i];
+      r.sc[2].n_items = s->n_items - r.sc[1].n_items;
+      r.sc[1].items = XCALLOC (r.sc[1].n_items, const struct variable *);
+      r.sc[2].items = XCALLOC (r.sc[2].n_items, const struct variable *);
 
+      size_t i = 0;
+      while (i < r.sc[1].n_items)
+        {
+          r.sc[1].items[i] = s->items[i];
+          i++;
+        }
       while (i < s->n_items)
        {
-         reliability.sc[2].items[i - reliability.sc[1].n_items] = s->items[i];
+         r.sc[2].items[i - r.sc[1].n_items] = s->items[i];
          i++;
        }
     }
 
-  if ( reliability.summary & SUMMARY_TOTAL)
+  if (r.summary_total)
     {
-      int i;
-      const int base_sc = reliability.n_sc;
-
-      reliability.total_start = base_sc;
+      const int base_sc = r.n_sc;
 
-      reliability.n_sc +=  reliability.sc[0].n_items ;
-      reliability.sc = xrealloc (reliability.sc, sizeof (struct cronbach) * reliability.n_sc);
+      r.total_start = base_sc;
 
+      r.n_sc +=  r.sc[0].n_items;
+      r.sc = xrealloc (r.sc, sizeof (struct cronbach) * r.n_sc);
 
-      for (i = 0 ; i < reliability.sc[0].n_items; ++i )
+      for (size_t i = 0; i < r.sc[0].n_items; ++i)
        {
-         int v_src;
-         int v_dest = 0;
-         struct cronbach *s = &reliability.sc[i + base_sc];
+         struct cronbach *s = &r.sc[i + base_sc];
 
-         s->n_items = reliability.sc[0].n_items - 1;
-         s->items = xzalloc (sizeof (struct variable *) * s->n_items);
-         for (v_src = 0 ; v_src < reliability.sc[0].n_items ; ++v_src)
-           {
-             if ( v_src != i)
-               s->items[v_dest++] = reliability.sc[0].items[v_src];
-           }
+         s->n_items = r.sc[0].n_items - 1;
+         s->items = xcalloc (s->n_items, sizeof (struct variable *));
+
+         size_t v_dest = 0;
+         for (size_t v_src = 0; v_src < r.sc[0].n_items; ++v_src)
+            if (v_src != i)
+              s->items[v_dest++] = r.sc[0].items[v_src];
        }
     }
 
-
-  if ( ! run_reliability (ds, &reliability)) 
+  if (!run_reliability (ds, &r))
     goto error;
 
-  free (reliability.variables);
+  reliability_destroy (&r);
   return CMD_SUCCESS;
 
  error:
-  free (reliability.variables);
+  reliability_destroy (&r);
   return CMD_FAILURE;
 }
 
@@ -346,123 +354,110 @@ static void reliability_statistics (const struct reliability *rel);
 static bool
 run_reliability (struct dataset *ds, const struct reliability *reliability)
 {
-  struct dictionary *dict = dataset_dict (ds);
-  bool ok;
-  struct casereader *group;
+  for (size_t si = 0; si < reliability->n_sc; ++si)
+    {
+      struct cronbach *s = &reliability->sc[si];
 
-  struct casegrouper *grouper = casegrouper_create_splits (proc_open (ds), dict);
+      s->m = xcalloc (s->n_items, sizeof *s->m);
+      s->total = moments1_create (MOMENT_VARIANCE);
 
+      for (size_t i = 0; i < s->n_items; ++i)
+       s->m[i] = moments1_create (MOMENT_VARIANCE);
+    }
 
+  struct dictionary *dict = dataset_dict (ds);
+  struct casegrouper *grouper = casegrouper_create_splits (proc_open (ds), dict);
+  struct casereader *group;
   while (casegrouper_get_next_group (grouper, &group))
     {
       do_reliability (group, ds, reliability);
 
       reliability_statistics (reliability);
 
-      if (reliability->summary & SUMMARY_TOTAL )
+      if (reliability->summary_total)
        reliability_summary_total (reliability);
     }
 
-  ok = casegrouper_destroy (grouper);
+  bool ok = casegrouper_destroy (grouper);
   ok = proc_commit (ds) && ok;
-
   return ok;
 }
-
-
 \f
-
-
 /* Return the sum of all the item variables in S */
-static  double
+static double
 append_sum (const struct ccase *c, casenumber n UNUSED, void *aux)
 {
   double sum = 0;
   const struct cronbach *s = aux;
 
-  int v;
-  for (v = 0 ; v < s->n_items; ++v)
-    {
-      sum += case_data (c, s->items[v])->f;
-    }
+  for (int v = 0; v < s->n_items; ++v)
+    sum += case_num (c, s->items[v]);
 
   return sum;
-};
+}
 
 static void
 case_processing_summary (casenumber n_valid, casenumber n_missing,
-                        const struct dictionary *dict);
-
+                        const struct dictionary *);
 
 static double
 alpha (int k, double sum_of_variances, double variance_of_sums)
 {
-  return k / ( k - 1.0) * ( 1 - sum_of_variances / variance_of_sums);
+  return k / (k - 1.0) * (1 - sum_of_variances / variance_of_sums);
 }
 
 static void
 do_reliability (struct casereader *input, struct dataset *ds,
                const struct reliability *rel)
 {
-  int i;
-  int si;
-  struct ccase *c;
-  casenumber n_missing ;
-  casenumber n_valid = 0;
-
-
-  for (si = 0 ; si < rel->n_sc; ++si)
+  for (size_t si = 0; si < rel->n_sc; ++si)
     {
       struct cronbach *s = &rel->sc[si];
 
-      s->m = xzalloc (sizeof (s->m) * s->n_items);
-      s->total = moments1_create (MOMENT_VARIANCE);
-
-      for (i = 0 ; i < s->n_items ; ++i )
-       s->m[i] = moments1_create (MOMENT_VARIANCE);
+      moments1_clear (s->total);
+      for (size_t i = 0; i < s->n_items; ++i)
+        moments1_clear (s->m[i]);
     }
 
+  casenumber n_missing;
   input = casereader_create_filter_missing (input,
-                                           rel->variables,
-                                           rel->n_variables,
+                                           rel->vars,
+                                           rel->n_vars,
                                            rel->exclude,
                                            &n_missing,
                                            NULL);
 
-  for (si = 0 ; si < rel->n_sc; ++si)
+  for (size_t si = 0; si < rel->n_sc; ++si)
     {
       struct cronbach *s = &rel->sc[si];
-
-
       s->totals_idx = caseproto_get_n_widths (casereader_get_proto (input));
-      input =
-       casereader_create_append_numeric (input, append_sum,
-                                         s, NULL);
+      input = casereader_create_append_numeric (input, append_sum, s, NULL);
     }
 
+  struct ccase *c;
+  casenumber n_valid = 0;
   for (; (c = casereader_read (input)) != NULL; case_unref (c))
     {
       double weight = 1.0;
-      n_valid ++;
+      n_valid++;
 
-      for (si = 0; si < rel->n_sc; ++si)
+      for (size_t si = 0; si < rel->n_sc; ++si)
        {
          struct cronbach *s = &rel->sc[si];
 
-         for (i = 0 ; i < s->n_items ; ++i )
-           moments1_add (s->m[i], case_data (c, s->items[i])->f, weight);
-
-         moments1_add (s->total, case_data_idx (c, s->totals_idx)->f, weight);
+         for (size_t i = 0; i < s->n_items; ++i)
+           moments1_add (s->m[i], case_num (c, s->items[i]), weight);
+         moments1_add (s->total, case_num_idx (c, s->totals_idx), weight);
        }
     }
   casereader_destroy (input);
 
-  for (si = 0; si < rel->n_sc; ++si)
+  for (size_t si = 0; si < rel->n_sc; ++si)
     {
       struct cronbach *s = &rel->sc[si];
 
       s->sum_of_variances = 0;
-      for (i = 0 ; i < s->n_items ; ++i )
+      for (size_t i = 0; i < s->n_items; ++i)
        {
          double weight, mean, variance;
          moments1_calculate (s->m[i], &weight, &mean, &variance, NULL, NULL);
@@ -473,369 +468,176 @@ do_reliability (struct casereader *input, struct dataset *ds,
       moments1_calculate (s->total, NULL, NULL, &s->variance_of_sums,
                          NULL, NULL);
 
-      s->alpha =
-       alpha (s->n_items, s->sum_of_variances, s->variance_of_sums);
+      s->alpha = alpha (s->n_items, s->sum_of_variances, s->variance_of_sums);
     }
 
-  text_item_submit (text_item_create_format (TEXT_ITEM_PARAGRAPH, "Scale: %s",
-                                             ds_cstr (&rel->scale_name)));
+  output_item_submit (text_item_create_nocopy (
+                        TEXT_ITEM_TITLE,
+                        xasprintf (_("Scale: %s"), rel->scale_name),
+                        NULL));
 
   case_processing_summary (n_valid, n_missing, dataset_dict (ds));
 }
 
-
-
-
-
 static void
 case_processing_summary (casenumber n_valid, casenumber n_missing,
                         const struct dictionary *dict)
 {
-  const struct variable *wv = dict_get_weight (dict);
-  const struct fmt_spec *wfmt = wv ? var_get_print_format (wv) : & F_8_0;
-
-  casenumber total;
-  int n_cols = 4;
-  int n_rows = 4;
-  int heading_columns = 2;
-  int heading_rows = 1;
-  struct tab_table *tbl;
-  tbl = tab_create (n_cols, n_rows);
-  tab_headers (tbl, heading_columns, 0, heading_rows, 0);
-
-  tab_title (tbl, _("Case Processing Summary"));
-
-  /* Vertical lines for the data only */
-  tab_box (tbl,
-          -1, -1,
-          -1, TAL_1,
-          heading_columns, 0,
-          n_cols - 1, n_rows - 1);
-
-  /* Box around table */
-  tab_box (tbl,
-          TAL_2, TAL_2,
-          -1, -1,
-          0, 0,
-          n_cols - 1, n_rows - 1);
-
-
-  tab_hline (tbl, TAL_2, 0, n_cols - 1, heading_rows);
-
-  tab_vline (tbl, TAL_2, heading_columns, 0, n_rows - 1);
-
-
-  tab_text (tbl, 0, heading_rows, TAB_LEFT | TAT_TITLE,
-               _("Cases"));
-
-  tab_text (tbl, 1, heading_rows, TAB_LEFT | TAT_TITLE,
-               _("Valid"));
-
-  tab_text (tbl, 1, heading_rows + 1, TAB_LEFT | TAT_TITLE,
-               _("Excluded"));
+  struct pivot_table *table = pivot_table_create (
+    N_("Case Processing Summary"));
+  pivot_table_set_weight_var (table, dict_get_weight (dict));
 
-  tab_text (tbl, 1, heading_rows + 2, TAB_LEFT | TAT_TITLE,
-               _("Total"));
+  pivot_dimension_create (table, PIVOT_AXIS_COLUMN, N_("Statistics"),
+                          N_("N"), PIVOT_RC_COUNT,
+                          N_("Percent"), PIVOT_RC_PERCENT);
 
-  tab_text (tbl, heading_columns, 0, TAB_CENTER | TAT_TITLE,
-               _("N"));
+  struct pivot_dimension *cases = pivot_dimension_create (
+    table, PIVOT_AXIS_ROW, N_("Cases"), N_("Valid"), N_("Excluded"),
+    N_("Total"));
+  cases->root->show_label = true;
 
-  tab_text (tbl, heading_columns + 1, 0, TAB_CENTER | TAT_TITLE, _("%"));
+  casenumber total = n_missing + n_valid;
 
-  total = n_missing + n_valid;
-
-  tab_double (tbl, 2, heading_rows, TAB_RIGHT,
-            n_valid, wfmt);
-
-
-  tab_double (tbl, 2, heading_rows + 1, TAB_RIGHT,
-            n_missing, wfmt);
-
-
-  tab_double (tbl, 2, heading_rows + 2, TAB_RIGHT,
-            total, wfmt);
-
-
-  tab_double (tbl, 3, heading_rows, TAB_RIGHT,
-            100 * n_valid / (double) total, NULL);
-
-
-  tab_double (tbl, 3, heading_rows + 1, TAB_RIGHT,
-            100 * n_missing / (double) total, NULL);
-
-
-  tab_double (tbl, 3, heading_rows + 2, TAB_RIGHT,
-            100 * total / (double) total, NULL);
+  struct entry
+    {
+      int stat_idx;
+      int case_idx;
+      double x;
+    }
+  entries[] = {
+    { 0, 0, n_valid },
+    { 0, 1, n_missing },
+    { 0, 2, total },
+    { 1, 0, 100.0 * n_valid / total },
+    { 1, 1, 100.0 * n_missing / total },
+    { 1, 2, 100.0 }
+  };
 
+  for (size_t i = 0; i < sizeof entries / sizeof *entries; i++)
+    {
+      const struct entry *e = &entries[i];
+      pivot_table_put2 (table, e->stat_idx, e->case_idx,
+                        pivot_value_new_number (e->x));
+    }
 
-  tab_submit (tbl);
+  pivot_table_submit (table);
 }
 
-
-
 static void
 reliability_summary_total (const struct reliability *rel)
 {
-  int i;
-  const int n_cols = 5;
-  const int heading_columns = 1;
-  const int heading_rows = 1;
-  const int n_rows = rel->sc[0].n_items + heading_rows ;
-
-  struct tab_table *tbl = tab_create (n_cols, n_rows);
-  tab_headers (tbl, heading_columns, 0, heading_rows, 0);
-
-  tab_title (tbl, _("Item-Total Statistics"));
-
-  /* Vertical lines for the data only */
-  tab_box (tbl,
-          -1, -1,
-          -1, TAL_1,
-          heading_columns, 0,
-          n_cols - 1, n_rows - 1);
-
-  /* Box around table */
-  tab_box (tbl,
-          TAL_2, TAL_2,
-          -1, -1,
-          0, 0,
-          n_cols - 1, n_rows - 1);
-
-
-  tab_hline (tbl, TAL_2, 0, n_cols - 1, heading_rows);
-
-  tab_vline (tbl, TAL_2, heading_columns, 0, n_rows - 1);
-
-  tab_text (tbl, 1, 0, TAB_CENTER | TAT_TITLE,
-           _("Scale Mean if Item Deleted"));
-
-  tab_text (tbl, 2, 0, TAB_CENTER | TAT_TITLE,
-           _("Scale Variance if Item Deleted"));
+  struct pivot_table *table = pivot_table_create (N_("Item-Total Statistics"));
 
-  tab_text (tbl, 3, 0, TAB_CENTER | TAT_TITLE,
-           _("Corrected Item-Total Correlation"));
+  pivot_dimension_create (table, PIVOT_AXIS_COLUMN, N_("Statistics"),
+                          N_("Scale Mean if Item Deleted"),
+                          N_("Scale Variance if Item Deleted"),
+                          N_("Corrected Item-Total Correlation"),
+                          N_("Cronbach's Alpha if Item Deleted"));
 
-  tab_text (tbl, 4, 0, TAB_CENTER | TAT_TITLE,
-           _("Cronbach's Alpha if Item Deleted"));
+  struct pivot_dimension *variables = pivot_dimension_create (
+    table, PIVOT_AXIS_ROW, N_("Variables"));
 
-
-  for (i = 0 ; i < rel->sc[0].n_items; ++i)
+  for (size_t i = 0; i < rel->sc[0].n_items; ++i)
     {
-      double cov, item_to_total_r;
-      double mean, weight, var;
-
       const struct cronbach *s = &rel->sc[rel->total_start + i];
-      tab_text (tbl, 0, heading_rows + i, TAB_LEFT| TAT_TITLE,
-               var_to_string (rel->sc[0].items[i]));
-
-      moments1_calculate (s->total, &weight, &mean, &var, 0, 0);
-
-      tab_double (tbl, 1, heading_rows + i, TAB_RIGHT,
-                mean, NULL);
-
-      tab_double (tbl, 2, heading_rows + i, TAB_RIGHT,
-                s->variance_of_sums, NULL);
-
-      tab_double (tbl, 4, heading_rows + i, TAB_RIGHT,
-                s->alpha, NULL);
-
 
-      moments1_calculate (rel->sc[0].m[i], &weight, &mean, &var, 0,0);
-      cov = rel->sc[0].variance_of_sums + var - s->variance_of_sums;
-      cov /= 2.0;
-
-      item_to_total_r = (cov - var) / (sqrt(var) * sqrt (s->variance_of_sums));
-
-
-      tab_double (tbl, 3, heading_rows + i, TAB_RIGHT,
-                item_to_total_r, NULL);
+      int var_idx = pivot_category_create_leaf (
+        variables->root, pivot_value_new_variable (rel->sc[0].items[i]));
+
+      double mean;
+      moments1_calculate (s->total, NULL, &mean, NULL, NULL, NULL);
+
+      double var;
+      moments1_calculate (rel->sc[0].m[i], NULL, NULL, &var, NULL, NULL);
+      double cov
+        = (rel->sc[0].variance_of_sums + var - s->variance_of_sums) / 2.0;
+
+      double entries[] = {
+        mean,
+        s->variance_of_sums,
+        (cov - var) / sqrt (var * s->variance_of_sums),
+        s->alpha,
+      };
+      for (size_t i = 0; i < sizeof entries / sizeof *entries; i++)
+        pivot_table_put2 (table, i, var_idx,
+                          pivot_value_new_number (entries[i]));
     }
 
-
-  tab_submit (tbl);
+  pivot_table_submit (table);
 }
 
 
-static void reliability_statistics_model_alpha (struct tab_table *tbl,
-                                               const struct reliability *rel);
-
-static void reliability_statistics_model_split (struct tab_table *tbl,
-                                               const struct reliability *rel);
-
-
-struct reliability_output_table
-{
-  int n_cols;
-  int n_rows;
-  int heading_cols;
-  int heading_rows;
-  void (*populate) (struct tab_table *, const struct reliability *);
-};
-
-
-static struct reliability_output_table rol[2] =
-  {
-    { 2, 2, 1, 1, reliability_statistics_model_alpha},
-    { 4, 9, 3, 0, reliability_statistics_model_split}
-  };
-
 static void
 reliability_statistics (const struct reliability *rel)
 {
-  int n_cols = rol[rel->model].n_cols;
-  int n_rows = rol[rel->model].n_rows;
-  int heading_columns = rol[rel->model].heading_cols;
-  int heading_rows = rol[rel->model].heading_rows;
-
-  struct tab_table *tbl = tab_create (n_cols, n_rows);
-  tab_headers (tbl, heading_columns, 0, heading_rows, 0);
-
-  tab_title (tbl, _("Reliability Statistics"));
-
-  /* Vertical lines for the data only */
-  tab_box (tbl,
-          -1, -1,
-          -1, TAL_1,
-          heading_columns, 0,
-          n_cols - 1, n_rows - 1);
-
-  /* Box around table */
-  tab_box (tbl,
-          TAL_2, TAL_2,
-          -1, -1,
-          0, 0,
-          n_cols - 1, n_rows - 1);
-
-
-  tab_hline (tbl, TAL_2, 0, n_cols - 1, heading_rows);
-
-  tab_vline (tbl, TAL_2, heading_columns, 0, n_rows - 1);
+  struct pivot_table *table = pivot_table_create (
+    N_("Reliability Statistics"));
+  pivot_table_set_weight_var (table, rel->wv);
 
-  if ( rel->model == MODEL_ALPHA )
-    reliability_statistics_model_alpha (tbl, rel);
-  else if (rel->model == MODEL_SPLIT )
-    reliability_statistics_model_split (tbl, rel);
-
-  tab_submit (tbl);
-}
-
-
-static void
-reliability_statistics_model_alpha (struct tab_table *tbl,
-                                   const struct reliability *rel)
-{
-  const struct variable *wv = rel->wv;
-  const struct fmt_spec *wfmt = wv ? var_get_print_format (wv) : & F_8_0;
-
-  const struct cronbach *s = &rel->sc[0];
-
-  tab_text (tbl, 0, 0, TAB_CENTER | TAT_TITLE,
-               _("Cronbach's Alpha"));
-
-  tab_text (tbl, 1, 0, TAB_CENTER | TAT_TITLE,
-               _("N of Items"));
-
-  tab_double (tbl, 0, 1, TAB_RIGHT, s->alpha, NULL);
-
-  tab_double (tbl, 1, 1, TAB_RIGHT, s->n_items, wfmt);
-}
-
-
-static void
-reliability_statistics_model_split (struct tab_table *tbl,
-                                   const struct reliability *rel)
-{
-  const struct variable *wv = rel->wv;
-  const struct fmt_spec *wfmt = wv ? var_get_print_format (wv) : & F_8_0;
-
-  tab_text (tbl, 0, 0, TAB_LEFT,
-           _("Cronbach's Alpha"));
-
-  tab_text (tbl, 1, 0, TAB_LEFT,
-           _("Part 1"));
-
-  tab_text (tbl, 2, 0, TAB_LEFT,
-           _("Value"));
-
-  tab_text (tbl, 2, 1, TAB_LEFT,
-           _("N of Items"));
-
-
-
-  tab_text (tbl, 1, 2, TAB_LEFT,
-           _("Part 2"));
-
-  tab_text (tbl, 2, 2, TAB_LEFT,
-           _("Value"));
-
-  tab_text (tbl, 2, 3, TAB_LEFT,
-           _("N of Items"));
-
-
-
-  tab_text (tbl, 1, 4, TAB_LEFT,
-           _("Total N of Items"));
-
-  tab_text (tbl, 0, 5, TAB_LEFT,
-           _("Correlation Between Forms"));
-
-
-  tab_text (tbl, 0, 6, TAB_LEFT,
-           _("Spearman-Brown Coefficient"));
-
-  tab_text (tbl, 1, 6, TAB_LEFT,
-           _("Equal Length"));
-
-  tab_text (tbl, 1, 7, TAB_LEFT,
-           _("Unequal Length"));
-
-
-  tab_text (tbl, 0, 8, TAB_LEFT,
-           _("Guttman Split-Half Coefficient"));
-
-
-
-  tab_double (tbl, 3, 0, TAB_RIGHT, rel->sc[1].alpha, NULL);
-  tab_double (tbl, 3, 2, TAB_RIGHT, rel->sc[2].alpha, NULL);
-
-  tab_double (tbl, 3, 1, TAB_RIGHT, rel->sc[1].n_items, wfmt);
-  tab_double (tbl, 3, 3, TAB_RIGHT, rel->sc[2].n_items, wfmt);
-
-  tab_double (tbl, 3, 4, TAB_RIGHT,
-            rel->sc[1].n_items + rel->sc[2].n_items, wfmt);
-
-  {
-    /* R is the correlation between the two parts */
-    double r = rel->sc[0].variance_of_sums -
-      rel->sc[1].variance_of_sums -
-      rel->sc[2].variance_of_sums ;
-
-    /* Guttman Split Half Coefficient */
-    double g = 2 * r / rel->sc[0].variance_of_sums;
-
-    /* Unequal Length Spearman Brown Coefficient, and
-     intermediate value used in the computation thereof */
-    double uly, tmp;
-
-    r /= sqrt (rel->sc[1].variance_of_sums);
-    r /= sqrt (rel->sc[2].variance_of_sums);
-    r /= 2.0;
-
-    tab_double (tbl, 3, 5, TAB_RIGHT, r, NULL);
-
-    /* Equal length Spearman-Brown Coefficient */
-    tab_double (tbl, 3, 6, TAB_RIGHT, 2 * r / (1.0 + r), NULL);
-
-    tab_double (tbl, 3, 8, TAB_RIGHT, g, NULL);
-
-    tmp = (1.0 - r*r) * rel->sc[1].n_items * rel->sc[2].n_items /
-      pow2 (rel->sc[0].n_items);
-
-    uly = sqrt( pow4 (r) + 4 * pow2 (r) * tmp);
-    uly -= pow2 (r);
-    uly /= 2 * tmp;
+  if (rel->model == MODEL_ALPHA)
+    {
+      pivot_dimension_create (table, PIVOT_AXIS_COLUMN,
+                              N_("Statistics"),
+                              N_("Cronbach's Alpha"), PIVOT_RC_OTHER,
+                              N_("N of Items"), PIVOT_RC_COUNT);
+
+      const struct cronbach *s = &rel->sc[0];
+      pivot_table_put1 (table, 0, pivot_value_new_number (s->alpha));
+      pivot_table_put1 (table, 1, pivot_value_new_number (s->n_items));
+    }
+  else
+    {
+      struct pivot_dimension *statistics = pivot_dimension_create (
+        table, PIVOT_AXIS_ROW, N_("Statistics"));
+      struct pivot_category *alpha = pivot_category_create_group (
+        statistics->root, N_("Cronbach's Alpha"));
+      pivot_category_create_group (alpha, N_("Part 1"),
+                                   N_("Value"), PIVOT_RC_OTHER,
+                                   N_("N of Items"), PIVOT_RC_COUNT);
+      pivot_category_create_group (alpha, N_("Part 2"),
+                                   N_("Value"), PIVOT_RC_OTHER,
+                                   N_("N of Items"), PIVOT_RC_COUNT);
+      pivot_category_create_leaves (alpha,
+                                    N_("Total N of Items"), PIVOT_RC_COUNT);
+      pivot_category_create_leaves (statistics->root,
+                                    N_("Correlation Between Forms"),
+                                    PIVOT_RC_OTHER);
+      pivot_category_create_group (statistics->root,
+                                   N_("Spearman-Brown Coefficient"),
+                                   N_("Equal Length"), PIVOT_RC_OTHER,
+                                   N_("Unequal Length"), PIVOT_RC_OTHER);
+      pivot_category_create_leaves (statistics->root,
+                                    N_("Guttman Split-Half Coefficient"),
+                                    PIVOT_RC_OTHER);
+
+      /* R is the correlation between the two parts */
+      double r0 = rel->sc[0].variance_of_sums -
+        rel->sc[1].variance_of_sums -
+        rel->sc[2].variance_of_sums;
+      double r1 = (r0 / sqrt (rel->sc[1].variance_of_sums)
+                   / sqrt (rel->sc[2].variance_of_sums)
+                   / 2.0);
+
+      /* Guttman Split Half Coefficient */
+      double g = 2 * r0 / rel->sc[0].variance_of_sums;
+
+      double tmp = (1.0 - r1*r1) * rel->sc[1].n_items * rel->sc[2].n_items /
+        pow2 (rel->sc[0].n_items);
+
+      double entries[] = {
+        rel->sc[1].alpha,
+        rel->sc[1].n_items,
+        rel->sc[2].alpha,
+        rel->sc[2].n_items,
+        rel->sc[1].n_items + rel->sc[2].n_items,
+        r1,
+        2 * r1 / (1.0 + r1),
+        (sqrt (pow4 (r1) + 4 * pow2 (r1) * tmp) - pow2 (r1)) / (2 * tmp),
+        g,
+      };
+      for (size_t i = 0; i < sizeof entries / sizeof *entries; i++)
+        pivot_table_put1 (table, i, pivot_value_new_number (entries[i]));
+    }
 
-    tab_double (tbl, 3, 7, TAB_RIGHT, uly, NULL);
-  }
+  pivot_table_submit (table);
 }
-