Move all command implementations into a single 'commands' directory.
[pspp] / src / language / stats / means.c
diff --git a/src/language/stats/means.c b/src/language/stats/means.c
deleted file mode 100644 (file)
index c1337ad..0000000
+++ /dev/null
@@ -1,1183 +0,0 @@
-/* PSPP - a program for statistical analysis.
-   Copyright (C) 2019 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
-   the Free Software Foundation, either version 3 of the License, or
-   (at your option) any later version.
-
-   This program is distributed in the hope that it will be useful,
-   but WITHOUT ANY WARRANTY; without even the implied warranty of
-   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-   GNU General Public License for more details.
-
-   You should have received a copy of the GNU General Public License
-   along with this program.  If not, see <http://www.gnu.org/licenses/>. */
-
-#include <config.h>
-
-#include "data/case.h"
-#include "data/casegrouper.h"
-#include "data/casereader.h"
-#include "data/dataset.h"
-#include "data/dictionary.h"
-#include "data/format.h"
-#include "data/variable.h"
-
-#include "libpspp/hmap.h"
-#include "libpspp/bt.h"
-#include "libpspp/hash-functions.h"
-#include "libpspp/misc.h"
-#include "libpspp/pool.h"
-
-#include "language/command.h"
-#include "language/lexer/lexer.h"
-
-#include "count-one-bits.h"
-#include "count-leading-zeros.h"
-
-#include "output/pivot-table.h"
-
-#include "means.h"
-
-
-#include "gettext.h"
-#define _(msgid) gettext (msgid)
-#define N_(msgid) (msgid)
-
-
-/* A "cell" in this procedure represents a distinct value of the
-   procedure's categorical variables,  and a set of summary statistics
-   of all cases which whose categorical variables have that set of
-   values.   For example,  the dataset
-
-   v1    v2    cat1     cat2
-   100   202      0     1
-   100   202      0     2
-   100   202      1     0
-   100   202      0     1
-
-
-   has three cells in layer 0 and two cells in layer 1  in addition
-   to a "grand summary" cell to which all (non-missing) cases
-   contribute.
-
-   The cells form a n-ary tree structure with the "grand summary"
-   cell at the root.
-*/
-struct cell
-{
-  struct hmap_node hmap_node; /* Element in hash table. */
-  struct bt_node  bt_node;    /* Element in binary tree */
-
-  int n_children;
-  struct cell_container *children;
-
-  /* The statistics to be calculated for the cell.  */
-  struct statistic **stat;
-
-  /* The parent of this cell, or NULL if this is the root cell.  */
-  const struct cell *parent_cell;
-
-  /* A bit-field variable which indicates which control variables
-     are allocated a fixed value (for this cell),  and which are
-     "wildcards".
-
-     A one indicates a fixed value.  A zero indicates a wildcard.
-     Wildcard values are used to calculate totals and sub-totals.
-  */
-  unsigned int not_wild;
-
-  /* The value(s). */
-  union value *values;
-
-  /* The variables corresponding to the above values.  */
-  const struct variable **vars;
-};
-
-/*  A structure used to find the union of all values used
-    within a layer, and to sort those values.  */
-struct instance
-{
-  struct hmap_node hmap_node; /* Element in hash table. */
-  struct bt_node  bt_node;    /* Element in binary tree */
-
-  /* A unique, consecutive, zero based index identifying this
-     instance.  */
-  int index;
-
-  /* The top level value of this instance.  */
-  union value value;
-  const struct variable *var;
-};
-
-
-static void
-destroy_workspace (const struct mtable *mt, struct workspace *ws)
-{
-  for (int l = 0; l < mt->n_layers; ++l)
-    {
-      struct cell_container *instances = ws->instances + l;
-      struct instance *inst;
-      struct instance *next;
-      HMAP_FOR_EACH_SAFE (inst, next, struct instance, hmap_node,
-                         &instances->map)
-       {
-         int width = var_get_width (inst->var);
-         value_destroy (&inst->value, width);
-         free (inst);
-       }
-      hmap_destroy (&instances->map);
-    }
-  free (ws->control_idx);
-  free (ws->instances);
-}
-
-/* Destroy CELL.  */
-static void
-destroy_cell (const struct means *means,
-             const struct mtable *mt, struct cell *cell)
-{
-  int idx = 0;
-  for (int i = 0; i < mt->n_layers; ++i)
-    {
-      if (0 == ((cell->not_wild >> i) & 0x1))
-       continue;
-
-      const struct layer *layer = mt->layers[i];
-      for (int cmb = 0; cmb < mt->n_combinations; ++cmb)
-      {
-        struct workspace *ws = mt->ws + cmb;
-        const struct variable *var
-          = layer->factor_vars[ws->control_idx[i]];
-
-        int width = var_get_width (var);
-        value_destroy (&cell->values[idx++], width);
-      }
-    }
-  for (int i = 0; i < cell->n_children; ++i)
-    {
-      struct cell_container *container = cell->children + i;
-      hmap_destroy (&container->map);
-    }
-
-  for (int v = 0; v < mt->n_dep_vars; ++v)
-    {
-      for (int s = 0; s < means->n_statistics; ++s)
-        {
-          stat_destroy *des = cell_spec[means->statistics[s]].sf;
-          des (cell->stat[s + v * means->n_statistics]);
-        }
-    }
-  free (cell->stat);
-
-  free (cell->children);
-  free (cell->values);
-  free (cell->vars);
-  free (cell);
-}
-
-
-/* Walk the tree in postorder starting from CELL and destroy all the
-   cells.  */
-static void
-means_destroy_cells (const struct means *means, struct cell *cell,
-                    const struct mtable *table)
-{
-  for (int i = 0; i < cell->n_children; ++i)
-    {
-      struct cell_container *container = cell->children + i;
-      struct cell *sub_cell;
-      struct cell *next;
-      HMAP_FOR_EACH_SAFE (sub_cell,  next, struct cell, hmap_node,
-                         &container->map)
-       {
-         means_destroy_cells (means, sub_cell, table);
-       }
-    }
-
-  destroy_cell (means, table, cell);
-}
-
-#if 0
-
-static void
-dump_cell (const struct cell *cell, const struct mtable *mt, int level)
-{
-  for (int l = 0; l < level; ++l)
-    putchar (' ');
-  printf ("%p: ", cell);
-  for (int i = 0; i < mt->n_layers; ++i)
-    {
-      putchar (((cell->not_wild >> i) & 0x1) ? 'w' : '.');
-    }
-  printf (" - ");
-  int x = 0;
-  for (int i = 0; i < mt->n_layers; ++i)
-    {
-      if ((cell->not_wild >> i) & 0x1)
-       {
-         printf ("%s: ", var_get_name (cell->vars[x]));
-         printf ("%g ", cell->values[x++].f);
-       }
-      else
-       printf ("x ");
-    }
-  stat_get *sg = cell_spec[MEANS_N].sd;
-  printf ("--- S1: %g", sg (cell->stat[0]));
-
-  printf ("--- N Children: %d", cell->n_children);
-  //  printf ("--- Level: %d", level);
-  printf ("--- Parent: %p", cell->parent_cell);
-  printf ("\n");
-}
-
-static void
-dump_indeces (const size_t *indexes, int n)
-{
-  for (int i = 0; i < n; ++i)
-    {
-      printf ("%ld; ", indexes[i]);
-    }
-  printf ("\n");
-}
-
-/* Dump the tree in pre-order.  */
-static void
-dump_tree (const struct cell *cell, const struct mtable *table,
-          int level, const struct cell *parent)
-{
-  assert (cell->parent_cell == parent);
-  dump_cell (cell, table, level);
-
-  for (int i = 0; i < cell->n_children; ++i)
-    {
-      struct cell_container *container = cell->children + i;
-      struct cell *sub_cell;
-      BT_FOR_EACH (sub_cell, struct cell, bt_node, &container->bt)
-       {
-         dump_tree (sub_cell, table, level + 1, cell);
-       }
-    }
-}
-
-#endif
-
-/* Generate a hash based on the values of the N variables in
-   the array VARS which are taken from the case C.  */
-static unsigned int
-generate_hash (const struct mtable *mt,
-              const struct ccase *c,
-              unsigned int not_wild,
-              const struct workspace *ws)
-{
-  unsigned int hash = 0;
-  for (int i = 0; i < mt->n_layers; ++i)
-    {
-      if (0 == ((not_wild >> i) & 0x1))
-       continue;
-
-      const struct layer *layer = mt->layers[i];
-      const struct variable *var = layer->factor_vars[ws->control_idx[i]];
-      const union value *vv = case_data (c, var);
-      int width = var_get_width (var);
-      hash = hash_int (i, hash);
-      hash = value_hash (vv, width, hash);
-    }
-
-  return hash;
-}
-
-/* Create a cell based on the N variables in the array VARS,
-   which are indeces into the case C.
-   The caller is responsible for destroying this cell when
-   no longer needed. */
-static struct cell *
-generate_cell (const struct means *means,
-              const struct mtable *mt,
-              const struct ccase *c,
-               unsigned int not_wild,
-              const struct cell *pcell,
-              const struct workspace *ws)
-{
-  int n_vars = count_one_bits (not_wild);
-  struct cell *cell = XZALLOC (struct cell);
-  cell->values = xcalloc (n_vars, sizeof *cell->values);
-  cell->vars = xcalloc (n_vars, sizeof *cell->vars);
-  cell->not_wild = not_wild;
-
-  cell->parent_cell = pcell;
-  cell->n_children = mt->n_layers -
-    (sizeof (cell->not_wild) * CHAR_BIT) +
-    count_leading_zeros (cell->not_wild);
-
-  int idx = 0;
-  for (int i = 0; i < mt->n_layers; ++i)
-    {
-      if (0 == ((not_wild >> i) & 0x1))
-       continue;
-
-      const struct layer *layer = mt->layers[i];
-      const struct variable *var = layer->factor_vars[ws->control_idx[i]];
-      const union value *vv = case_data (c, var);
-      int width = var_get_width (var);
-      cell->vars[idx] = var;
-      value_clone (&cell->values[idx++], vv, width);
-    }
-  assert (idx == n_vars);
-
-  cell->children = xcalloc (cell->n_children, sizeof *cell->children);
-  for (int i = 0; i < cell->n_children; ++i)
-    {
-      struct cell_container *container = cell->children + i;
-      hmap_init (&container->map);
-    }
-
-  cell->stat = xcalloc (means->n_statistics * mt->n_dep_vars, sizeof *cell->stat);
-  for (int v = 0; v < mt->n_dep_vars; ++v)
-    {
-      for (int stat = 0; stat < means->n_statistics; ++stat)
-        {
-          stat_create *sc = cell_spec[means->statistics[stat]].sc;
-
-          cell->stat[stat + v * means->n_statistics] = sc (means->pool);
-        }
-    }
-  return cell;
-}
-
-
-/* If a  cell based on the N variables in the array VARS,
-   which are indeces into the case C and whose hash is HASH,
-   exists in HMAP, then return that cell.
-   Otherwise, return NULL.  */
-static struct cell *
-lookup_cell (const struct mtable *mt,
-            struct hmap *hmap,  unsigned int hash,
-            const struct ccase *c,
-            unsigned int not_wild,
-            const struct workspace *ws)
-{
-  struct cell *cell = NULL;
-  HMAP_FOR_EACH_WITH_HASH (cell, struct cell, hmap_node, hash, hmap)
-    {
-      bool match = true;
-      int idx = 0;
-      if (cell->not_wild != not_wild)
-       continue;
-      for (int i = 0; i < mt->n_layers; ++i)
-       {
-         if (0 == ((cell->not_wild >> i) & 0x1))
-           continue;
-
-         const struct layer *layer = mt->layers[i];
-         const struct variable *var = layer->factor_vars[ws->control_idx[i]];
-         const union value *vv = case_data (c, var);
-         int width = var_get_width (var);
-         assert (var == cell->vars[idx]);
-         if (!value_equal (vv, &cell->values[idx++], width))
-           {
-             match = false;
-             break;
-           }
-       }
-      if (match)
-       return cell;
-    }
-  return NULL;
-}
-
-
-/*  A comparison function used to sort cells in a binary tree.
-    Only the innermost value needs to be compared, because no
-    two cells with similar outer values will appear in the same
-    tree/map.   */
-static int
-cell_compare_3way (const struct bt_node *a,
-                  const struct bt_node *b,
-                  const void *aux UNUSED)
-{
-  const struct cell *fa = BT_DATA (a, struct cell, bt_node);
-  const struct cell *fb = BT_DATA (b, struct cell, bt_node);
-
-  assert (fa->not_wild == fb->not_wild);
-  int vidx = count_one_bits (fa->not_wild) - 1;
-  assert (fa->vars[vidx] == fb->vars[vidx]);
-
-  return value_compare_3way (&fa->values[vidx],
-                            &fb->values[vidx],
-                            var_get_width (fa->vars[vidx]));
-}
-
-/*  A comparison function used to sort cells in a binary tree.  */
-static int
-compare_instance_3way (const struct bt_node *a,
-                      const struct bt_node *b,
-                      const void *aux UNUSED)
-{
-  const struct instance *fa = BT_DATA (a, struct instance, bt_node);
-  const struct instance *fb = BT_DATA (b, struct instance, bt_node);
-
-  assert (fa->var == fb->var);
-
-  return  value_compare_3way (&fa->value,
-                             &fb->value,
-                             var_get_width (fa->var));
-}
-
-
-static void arrange_cells (struct workspace *ws,
-                          struct cell *cell, const struct mtable *table);
-
-
-/* Iterate CONTAINER's map inserting a copy of its elements into
-   CONTAINER's binary tree.    Also, for each layer in TABLE, create
-   an instance container, containing the union of all elements in
-   CONTAINER.  */
-static void
-arrange_cell (struct workspace *ws, struct cell_container *container,
-             const struct mtable *mt)
-{
-  struct bt *bt = &container->bt;
-  struct hmap *map = &container->map;
-  bt_init (bt, cell_compare_3way, NULL);
-
-  struct cell *cell;
-  HMAP_FOR_EACH (cell, struct cell, hmap_node, map)
-    {
-      bt_insert (bt, &cell->bt_node);
-
-      int idx = 0;
-      for (int i = 0; i < mt->n_layers; ++i)
-       {
-         if (0 == ((cell->not_wild >> i) & 0x1))
-           continue;
-
-         struct cell_container *instances = ws->instances + i;
-         const struct variable *var = cell->vars[idx];
-         int width = var_get_width (var);
-         unsigned int hash
-           = value_hash (&cell->values[idx], width, 0);
-
-         struct instance *inst = NULL;
-         struct instance *next = NULL;
-         HMAP_FOR_EACH_WITH_HASH_SAFE (inst, next, struct instance,
-                                       hmap_node,
-                                       hash, &instances->map)
-           {
-             assert (cell->vars[idx] == var);
-             if (value_equal (&inst->value,
-                              &cell->values[idx],
-                              width))
-               {
-                 break;
-               }
-           }
-
-         if (!inst)
-           {
-             inst = xzalloc (sizeof *inst);
-             inst->index = -1;
-             inst->var = var;
-             value_clone (&inst->value, &cell->values[idx],
-                          width);
-             hmap_insert (&instances->map, &inst->hmap_node, hash);
-           }
-
-         idx++;
-       }
-
-      arrange_cells (ws, cell, mt);
-    }
-}
-
-/* Arrange the children and then all the subtotals.  */
-static void
-arrange_cells (struct workspace *ws, struct cell *cell,
-              const struct mtable *table)
-{
-  for (int i = 0; i < cell->n_children; ++i)
-    {
-      struct cell_container *container = cell->children + i;
-      arrange_cell (ws, container, table);
-    }
-}
-
-
-\f
-
-/*  If the top level value in CELL, has an instance in the L_IDX'th layer,
-    then return that instance.  Otherwise return NULL.  */
-static const struct instance *
-lookup_instance (const struct mtable *mt, const struct workspace *ws,
-                int l_idx, const struct cell *cell)
-{
-  const struct layer *layer = mt->layers[l_idx];
-  int n_vals = count_one_bits (cell->not_wild);
-  const struct variable *var = layer->factor_vars[ws->control_idx[l_idx]];
-  const union value *val = cell->values + n_vals - 1;
-  int width = var_get_width (var);
-  unsigned int hash = value_hash (val, width, 0);
-  const struct cell_container *instances = ws->instances + l_idx;
-  struct instance *inst = NULL;
-  struct instance *next;
-  HMAP_FOR_EACH_WITH_HASH_SAFE (inst, next,
-                               struct instance, hmap_node,
-                               hash, &instances->map)
-    {
-      if (value_equal (val, &inst->value, width))
-       break;
-    }
-  return inst;
-}
-
-/* Enter the values into PT.  */
-static void
-populate_table (const struct means *means, const struct mtable *mt,
-               const struct workspace *ws,
-                const struct cell *cell,
-                struct pivot_table *pt)
-{
-  size_t *indexes = XCALLOC (pt->n_dimensions, size_t);
-  for (int v = 0; v < mt->n_dep_vars; ++v)
-    {
-      for (int s = 0; s < means->n_statistics; ++s)
-        {
-          int i = 0;
-          if (mt->n_dep_vars > 1)
-            indexes[i++] = v;
-          indexes[i++] = s;
-          int stat = means->statistics[s];
-          stat_get *sg = cell_spec[stat].sd;
-          {
-            const struct cell *pc = cell;
-            for (; i < pt->n_dimensions; ++i)
-              {
-                int l_idx = pt->n_dimensions - i - 1;
-               const struct cell_container *instances = ws->instances + l_idx;
-                if (0 == (cell->not_wild >> l_idx & 0x1U))
-                  {
-                    indexes [i] = hmap_count (&instances->map);
-                  }
-                else
-                  {
-                    assert (pc);
-                    const struct instance *inst
-                     = lookup_instance (mt, ws, l_idx, pc);
-                    assert (inst);
-                    indexes [i] = inst->index;
-                    pc = pc->parent_cell;
-                  }
-              }
-          }
-
-         int idx = s + v * means->n_statistics;
-         struct pivot_value *pv
-           = pivot_value_new_number (sg (cell->stat[idx]));
-         if (NULL == cell_spec[stat].rc)
-           {
-             const struct variable *dv = mt->dep_vars[v];
-             pv->numeric.format = * var_get_print_format (dv);
-           }
-          pivot_table_put (pt, indexes, pt->n_dimensions, pv);
-        }
-    }
-  free (indexes);
-
-  for (int i = 0; i < cell->n_children; ++i)
-    {
-      struct cell_container *container = cell->children + i;
-      struct cell *child = NULL;
-      BT_FOR_EACH (child, struct cell, bt_node, &container->bt)
-       {
-          populate_table (means, mt, ws, child, pt);
-       }
-    }
-}
-
-static void
-create_table_structure (const struct mtable *mt, struct pivot_table *pt,
-                       const struct workspace *ws)
-{
-  int * lindexes = ws->control_idx;
-  /* The inner layers are situated rightmost in the table.
-     So this iteration is in reverse order.  */
-  for (int l = mt->n_layers - 1; l >= 0; --l)
-    {
-      const struct layer *layer = mt->layers[l];
-      const struct cell_container *instances = ws->instances + l;
-      const struct variable *var = layer->factor_vars[lindexes[l]];
-      struct pivot_dimension *dim_layer
-       = pivot_dimension_create (pt, PIVOT_AXIS_ROW,
-                                 var_to_string (var));
-      dim_layer->root->show_label = true;
-
-      /* Place the values of the control variables as table headings.  */
-      {
-       struct instance *inst = NULL;
-       BT_FOR_EACH (inst, struct instance, bt_node, &instances->bt)
-         {
-           struct substring space = SS_LITERAL_INITIALIZER ("\t ");
-           struct string str;
-           ds_init_empty (&str);
-           var_append_value_name (var,
-                                  &inst->value,
-                                  &str);
-
-           ds_ltrim (&str, space);
-
-           pivot_category_create_leaf (dim_layer->root,
-                                        pivot_value_new_text (ds_cstr (&str)));
-
-           ds_destroy (&str);
-         }
-      }
-
-      pivot_category_create_leaf (dim_layer->root,
-                                  pivot_value_new_text ("Total"));
-    }
-}
-
-/* Initialise C_DES with a string describing the control variable
-   relating to MT, LINDEXES.  */
-static void
-layers_to_string (const struct mtable *mt, const int *lindexes,
-                 struct string *c_des)
-{
-  for (int l = 0; l < mt->n_layers; ++l)
-    {
-      const struct layer *layer = mt->layers[l];
-      const struct variable *ctrl_var = layer->factor_vars[lindexes[l]];
-      if (l > 0)
-       ds_put_cstr (c_des, " * ");
-      ds_put_cstr (c_des, var_get_name (ctrl_var));
-    }
-}
-
-static void
-populate_case_processing_summary (struct pivot_category *pc,
-                                 const struct mtable *mt,
-                                 const int *lindexes)
-{
-  struct string ds;
-  ds_init_empty (&ds);
-  int l = 0;
-  for (l = 0; l < mt->n_layers; ++l)
-    {
-      const struct layer *layer = mt->layers[l];
-      const struct variable *ctrl_var = layer->factor_vars[lindexes[l]];
-      if (l > 0)
-       ds_put_cstr (&ds, " * ");
-      ds_put_cstr (&ds, var_get_name (ctrl_var));
-    }
-  for (int dv = 0; dv < mt->n_dep_vars; ++dv)
-    {
-      struct string dss;
-      ds_init_empty (&dss);
-      ds_put_cstr (&dss, var_get_name (mt->dep_vars[dv]));
-      if (mt->n_layers > 0)
-       {
-         ds_put_cstr (&dss, " * ");
-         ds_put_substring (&dss, ds.ss);
-       }
-      pivot_category_create_leaf (pc,
-                                 pivot_value_new_text (ds_cstr (&dss)));
-      ds_destroy (&dss);
-    }
-
-  ds_destroy (&ds);
-}
-
-/* Create the "Case Processing Summary" table.  */
-static void
-means_case_processing_summary (const struct mtable *mt)
-{
-  struct pivot_table *pt = pivot_table_create (N_("Case Processing Summary"));
-
-  struct pivot_dimension *dim_cases =
-    pivot_dimension_create (pt, PIVOT_AXIS_COLUMN, N_("Cases"));
-  dim_cases->root->show_label = true;
-
-  struct pivot_category *cats[3];
-  cats[0] = pivot_category_create_group (dim_cases->root,
-                                        N_("Included"), NULL);
-  cats[1] = pivot_category_create_group (dim_cases->root,
-                                        N_("Excluded"), NULL);
-  cats[2] = pivot_category_create_group (dim_cases->root,
-                                        N_("Total"), NULL);
-  for (int i = 0; i < 3; ++i)
-    {
-      pivot_category_create_leaf_rc (cats[i],
-                                     pivot_value_new_text (N_("N")),
-                                    PIVOT_RC_COUNT);
-      pivot_category_create_leaf_rc (cats[i],
-                                     pivot_value_new_text (N_("Percent")),
-                                    PIVOT_RC_PERCENT);
-    }
-
-  struct pivot_dimension *rows =
-    pivot_dimension_create (pt, PIVOT_AXIS_ROW, N_("Variables"));
-
-  for (int cmb = 0; cmb < mt->n_combinations; ++cmb)
-    {
-      const struct workspace *ws = mt->ws + cmb;
-      populate_case_processing_summary (rows->root, mt, ws->control_idx);
-      for (int dv = 0; dv < mt->n_dep_vars; ++dv)
-        {
-          int idx = cmb * mt->n_dep_vars + dv;
-          const struct summary *summ = mt->summ + idx;
-          double n_included = summ->n_total - summ->n_missing;
-          pivot_table_put2 (pt, 5, idx,
-                            pivot_value_new_number (100.0 * summ->n_total / summ->n_total));
-          pivot_table_put2 (pt, 4, idx,
-                            pivot_value_new_number (summ->n_total));
-
-          pivot_table_put2 (pt, 3, idx,
-                            pivot_value_new_number (100.0 * summ->n_missing / summ->n_total));
-          pivot_table_put2 (pt, 2, idx,
-                            pivot_value_new_number (summ->n_missing));
-
-          pivot_table_put2 (pt, 1, idx,
-                            pivot_value_new_number (100.0 * n_included / summ->n_total));
-          pivot_table_put2 (pt, 0, idx,
-                            pivot_value_new_number (n_included));
-        }
-    }
-
-  pivot_table_submit (pt);
-}
-
-static void
-means_shipout_single (const struct mtable *mt, const struct means *means,
-                     const struct workspace *ws)
-{
-  struct pivot_table *pt = pivot_table_create (N_("Report"));
-
-  struct pivot_dimension *dim_cells =
-    pivot_dimension_create (pt, PIVOT_AXIS_COLUMN, N_("Statistics"));
-
-  /* Set the statistics headings, eg "Mean", "Std. Dev" etc.  */
-  for (int i = 0; i < means->n_statistics; ++i)
-    {
-      const struct cell_spec *cs = cell_spec + means->statistics[i];
-      pivot_category_create_leaf_rc
-       (dim_cells->root,
-        pivot_value_new_text (gettext (cs->title)), cs->rc);
-    }
-
-  create_table_structure (mt, pt, ws);
-  populate_table (means, mt, ws, ws->root_cell, pt);
-  pivot_table_submit (pt);
-}
-
-
-static void
-means_shipout_multivar (const struct mtable *mt, const struct means *means,
-                       const struct workspace *ws)
-{
-  struct string dss;
-  ds_init_empty (&dss);
-  for (int dv = 0; dv < mt->n_dep_vars; ++dv)
-    {
-      if (dv > 0)
-       ds_put_cstr (&dss, " * ");
-      ds_put_cstr (&dss, var_get_name (mt->dep_vars[dv]));
-    }
-
-  for (int l = 0; l < mt->n_layers; ++l)
-    {
-      ds_put_cstr (&dss, " * ");
-      const struct layer *layer = mt->layers[l];
-      const struct variable *var = layer->factor_vars[ws->control_idx[l]];
-      ds_put_cstr (&dss, var_get_name (var));
-    }
-
-  struct pivot_table *pt = pivot_table_create (ds_cstr (&dss));
-  ds_destroy (&dss);
-
-  struct pivot_dimension *dim_cells =
-    pivot_dimension_create (pt, PIVOT_AXIS_COLUMN, N_("Variables"));
-
-  for (int i = 0; i < mt->n_dep_vars; ++i)
-    {
-      pivot_category_create_leaf
-       (dim_cells->root,
-        pivot_value_new_variable (mt->dep_vars[i]));
-    }
-
-  struct pivot_dimension *dim_stats
-    = pivot_dimension_create (pt, PIVOT_AXIS_ROW,
-                             N_ ("Statistics"));
-  dim_stats->root->show_label = false;
-
-  for (int i = 0; i < means->n_statistics; ++i)
-    {
-      const struct cell_spec *cs = cell_spec + means->statistics[i];
-      pivot_category_create_leaf_rc
-       (dim_stats->root,
-        pivot_value_new_text (gettext (cs->title)), cs->rc);
-    }
-
-  create_table_structure (mt, pt, ws);
-  populate_table (means, mt, ws, ws->root_cell, pt);
-  pivot_table_submit (pt);
-}
-
-static void
-means_shipout (const struct mtable *mt, const struct means *means)
-{
-  for (int cmb = 0; cmb < mt->n_combinations; ++cmb)
-    {
-      const struct workspace *ws = mt->ws + cmb;
-      if (ws->root_cell == NULL)
-       {
-         struct string des;
-         ds_init_empty (&des);
-         layers_to_string (mt, ws->control_idx, &des);
-         msg (MW, _("The table \"%s\" has no non-empty control variables."
-                    "  No result for this table will be displayed."),
-              ds_cstr (&des));
-         ds_destroy (&des);
-         continue;
-       }
-      if (mt->n_dep_vars > 1)
-       means_shipout_multivar (mt, means, ws);
-      else
-       means_shipout_single (mt, means, ws);
-    }
-}
-
-
-\f
-
-static bool
-control_var_missing (const struct means *means,
-                    const struct mtable *mt,
-                    unsigned int not_wild UNUSED,
-                    const struct ccase *c,
-                    const struct workspace *ws)
-{
-  bool miss = false;
-  for (int l = 0; l < mt->n_layers; ++l)
-    {
-      /* if (0 == ((not_wild >> l) & 0x1)) */
-      /* { */
-      /*   continue; */
-      /* } */
-
-      const struct layer *layer = mt->layers[l];
-      const struct variable *var = layer->factor_vars[ws->control_idx[l]];
-      const union value *vv = case_data (c, var);
-
-      miss = (var_is_value_missing (var, vv) & means->ctrl_exclude) != 0;
-      if (miss)
-       break;
-    }
-
-  return miss;
-}
-
-/* Lookup the set of control variables described by MT, C and NOT_WILD,
-   in the hash table MAP.  If there is no such entry, then create a
-   cell with these paremeters and add is to MAP.
-   If the generated cell has childen, repeat for all the children.
-   Returns the root cell.
-*/
-static struct cell *
-service_cell_map (const struct means *means, const struct mtable *mt,
-                const struct ccase *c,
-                 unsigned int not_wild,
-                struct hmap *map,
-                const struct cell *pcell,
-                 int level,
-                const struct workspace *ws)
-{
-  struct cell *cell = NULL;
-  if (map)
-    {
-      if (!control_var_missing (means, mt, not_wild, c, ws))
-       {
-         /* Lookup this set of values in the cell's hash table.  */
-         unsigned int hash = generate_hash (mt, c, not_wild, ws);
-         cell = lookup_cell (mt, map, hash, c, not_wild, ws);
-
-         /* If it has not been seen before, then create a new
-            subcell, with this set of values, and insert it
-            into the table.  */
-         if (cell == NULL)
-           {
-              cell = generate_cell (means, mt, c, not_wild, pcell, ws);
-             hmap_insert (map, &cell->hmap_node, hash);
-           }
-       }
-    }
-  else
-    {
-      /* This condition should only happen in the root node case. */
-      cell = ws->root_cell;
-      if (cell == NULL &&
-         !control_var_missing (means, mt, not_wild, c, ws))
-       cell = generate_cell (means, mt, c, not_wild, pcell, ws);
-    }
-
-  if (cell)
-    {
-      /* Here is where the business really happens!   After
-        testing for missing values, the cell's statistics
-        are accumulated.  */
-      if (!control_var_missing (means, mt, not_wild, c, ws))
-        {
-          for (int v = 0; v < mt->n_dep_vars; ++v)
-            {
-              const struct variable *dep_var = mt->dep_vars[v];
-             const union value *vv = case_data (c, dep_var);
-             if (var_is_value_missing (dep_var, vv) & means->dep_exclude)
-               continue;
-
-              for (int stat = 0; stat < means->n_statistics; ++stat)
-                {
-                  const double weight = dict_get_case_weight (means->dict, c,
-                                                              NULL);
-                  stat_update *su = cell_spec[means->statistics[stat]].su;
-                  su (cell->stat[stat + v * means->n_statistics], weight,
-                     case_num (c, dep_var));
-                }
-            }
-        }
-
-      /* Recurse into all the children (if there are any).  */
-      for (int i = 0; i < cell->n_children; ++i)
-       {
-         struct cell_container *cc = cell->children + i;
-         service_cell_map (means, mt, c,
-                           not_wild | (0x1U << (i + level)),
-                          &cc->map, cell, level + i + 1, ws);
-       }
-    }
-
-  return cell;
-}
-
-/*  Do all the necessary preparation and pre-calculation that
-    needs to be done before iterating the data.  */
-static void
-prepare_means (struct means *cmd)
-{
-  for (int t = 0; t < cmd->n_tables; ++t)
-    {
-      struct mtable *mt = cmd->table + t;
-
-      for (int i = 0; i < mt->n_combinations; ++i)
-        {
-          struct workspace *ws = mt->ws + i;
-         ws->root_cell = NULL;
-          ws->control_idx = xcalloc (mt->n_layers, sizeof *ws->control_idx);
-          ws->instances = xcalloc (mt->n_layers, sizeof *ws->instances);
-          int cmb = i;
-          for (int l = mt->n_layers - 1; l >= 0; --l)
-            {
-             struct cell_container *instances = ws->instances + l;
-              const struct layer *layer = mt->layers[l];
-              ws->control_idx[l] = cmb % layer->n_factor_vars;
-              cmb /= layer->n_factor_vars;
-             hmap_init (&instances->map);
-            }
-        }
-    }
-}
-
-
-/* Do all the necessary calculations that occur AFTER iterating
-   the data.  */
-static void
-post_means (struct means *cmd)
-{
-  for (int t = 0; t < cmd->n_tables; ++t)
-    {
-      struct mtable *mt = cmd->table + t;
-      for (int cmb = 0; cmb < mt->n_combinations; ++cmb)
-       {
-         struct workspace *ws = mt->ws + cmb;
-         if (ws->root_cell == NULL)
-           continue;
-         arrange_cells (ws, ws->root_cell, mt);
-         /*  The root cell should have no parent.  */
-         assert (ws->root_cell->parent_cell == 0);
-
-         for (int l = 0; l < mt->n_layers; ++l)
-           {
-             struct cell_container *instances = ws->instances + l;
-             bt_init (&instances->bt, compare_instance_3way, NULL);
-
-             /* Iterate the instance hash table, and insert each instance
-                into the binary tree BT.  */
-             struct instance *inst;
-             HMAP_FOR_EACH (inst, struct instance, hmap_node,
-                            &instances->map)
-               {
-                 bt_insert (&instances->bt, &inst->bt_node);
-               }
-
-             /* Iterate the binary tree (in order) and assign the index
-                member accordingly.  */
-             int index = 0;
-             BT_FOR_EACH (inst, struct instance, bt_node, &instances->bt)
-               {
-                 inst->index = index++;
-               }
-           }
-       }
-    }
-}
-
-
-/* Update the summary information (the missings and the totals).  */
-static void
-update_summaries (const struct means *means, struct mtable *mt,
-                 const struct ccase *c, double weight)
-{
-  for (int dv = 0; dv < mt->n_dep_vars; ++dv)
-    {
-      for (int cmb = 0; cmb < mt->n_combinations; ++cmb)
-       {
-         struct workspace *ws = mt->ws + cmb;
-         struct summary *summ = mt->summ
-           + cmb * mt->n_dep_vars + dv;
-
-         summ->n_total += weight;
-         const struct variable *var = mt->dep_vars[dv];
-         const union value *vv = case_data (c, var);
-         /* First check if the dependent variable is missing.  */
-         if (var_is_value_missing (var, vv) & means->dep_exclude)
-           summ->n_missing += weight;
-         /* If the dep var is not missing, then check each
-            control variable.  */
-         else
-           for (int l = 0; l < mt->n_layers; ++l)
-             {
-               const struct layer *layer = mt->layers [l];
-               const struct variable *var
-                 = layer->factor_vars[ws->control_idx[l]];
-               const union value *vv = case_data (c, var);
-               if (var_is_value_missing (var, vv) & means->ctrl_exclude)
-                 {
-                   summ->n_missing += weight;
-                   break;
-                 }
-             }
-       }
-    }
-}
-
-
-void
-run_means (struct means *cmd, struct casereader *input,
-          const struct dataset *ds UNUSED)
-{
-  struct ccase *c = NULL;
-  struct casereader *reader;
-
-  prepare_means (cmd);
-
-  for (reader = input;
-       (c = casereader_read (reader)) != NULL; case_unref (c))
-    {
-      const double weight
-       = dict_get_case_weight (cmd->dict, c, NULL);
-      for (int t = 0; t < cmd->n_tables; ++t)
-       {
-         struct mtable *mt = cmd->table + t;
-         update_summaries (cmd, mt, c, weight);
-
-         for (int cmb = 0; cmb < mt->n_combinations; ++cmb)
-           {
-             struct workspace *ws = mt->ws + cmb;
-
-             ws->root_cell = service_cell_map (cmd, mt, c,
-                                               0U, NULL, NULL, 0, ws);
-           }
-       }
-    }
-  casereader_destroy (reader);
-
-  post_means (cmd);
-}
-
-int
-cmd_means (struct lexer *lexer, struct dataset *ds)
-{
-  struct means means = {
-    .pool = pool_create (),
-    .ctrl_exclude = MV_ANY,
-    .dep_exclude = MV_ANY,
-    .dict = dataset_dict (ds),
-  };
-  means_set_default_statistics (&means);
-
-  if (!means_parse (lexer, &means))
-    goto error;
-
-  /* Calculate some constant data for each table.  */
-  for (int t = 0; t < means.n_tables; ++t)
-    {
-      struct mtable *mt = means.table + t;
-      mt->n_combinations = 1;
-      for (int l = 0; l < mt->n_layers; ++l)
-       mt->n_combinations *= mt->layers[l]->n_factor_vars;
-    }
-
-  struct casegrouper *grouper
-    = casegrouper_create_splits (proc_open (ds), means.dict);
-  struct casereader *group;
-  while (casegrouper_get_next_group (grouper, &group))
-    {
-      /* Allocate the workspaces.  */
-      for (int t = 0; t < means.n_tables; ++t)
-       {
-         struct mtable *mt = means.table + t;
-         mt->summ = xcalloc (mt->n_combinations * mt->n_dep_vars,
-                             sizeof *mt->summ);
-         mt->ws = xcalloc (mt->n_combinations, sizeof *mt->ws);
-       }
-      run_means (&means, group, ds);
-      for (int t = 0; t < means.n_tables; ++t)
-        {
-          const struct mtable *mt = means.table + t;
-
-          means_case_processing_summary (mt);
-          means_shipout (mt, &means);
-
-          for (int i = 0; i < mt->n_combinations; ++i)
-            {
-              struct workspace *ws = mt->ws + i;
-              if (ws->root_cell)
-                means_destroy_cells (&means, ws->root_cell, mt);
-            }
-        }
-
-      /* Destroy the workspaces.  */
-      for (int t = 0; t < means.n_tables; ++t)
-        {
-          struct mtable *mt = means.table + t;
-          free (mt->summ);
-          for (int i = 0; i < mt->n_combinations; ++i)
-            {
-              struct workspace *ws = mt->ws + i;
-              destroy_workspace (mt, ws);
-            }
-          free (mt->ws);
-        }
-    }
-
-  bool ok = casegrouper_destroy (grouper);
-  ok = proc_commit (ds) && ok;
-  if (!ok)
-    goto error;
-
-  pool_destroy (means.pool);
-  return CMD_SUCCESS;
-
- error:
-  pool_destroy (means.pool);
-  return CMD_FAILURE;
-}