Merge commit 'origin/stable' build40
authorJohn Darrington <john@darrington.wattle.id.au>
Thu, 24 Sep 2009 18:43:42 +0000 (20:43 +0200)
committerJohn Darrington <john@darrington.wattle.id.au>
Thu, 24 Sep 2009 18:43:42 +0000 (20:43 +0200)
Conflicts:

AUTHORS
NEWS
configure.ac
lib/gtksheet/gtkitementry.c
po/LINGUAS
po/nl.po
src/data/file-name.c
src/data/sys-file-writer.c
src/language/stats/crosstabs.q
src/language/stats/examine.q
src/language/stats/t-test.q
src/output/charts/box-whisker.c
src/output/charts/plot-hist.c
src/ui/gui/output-viewer.c
src/ui/gui/psppire.c
src/ui/gui/psppire.h
src/ui/terminal/main.c

14 files changed:
1  2 
AUTHORS
NEWS
po/automake.mk
src/data/file-name.c
src/data/sys-file-writer.c
src/language/stats/examine.q
src/language/stats/regression.q
src/language/stats/t-test.q
src/output/charts/box-whisker.c
src/output/charts/plot-chart.h
src/output/charts/plot-hist.c
src/ui/gui/psppire.c
src/ui/gui/psppire.h
src/ui/terminal/main.c

diff --combined AUTHORS
index db7a59105fbe6600eed2136d196ac4e691e710ff,262d422caade6bde7ba5cfd7e59876afd46a0bb1..38ed6fe0428e4c9e4967860054f06cee66eae708
+++ b/AUTHORS
@@@ -6,14 -6,15 +6,15 @@@ most of the core libraries which ensur
  speed are his work.
  
  * John Darrington wrote the graphical user interface, and the T-TEST,
 -ONEWAY, EXAMINE, RANK and  NPAR TESTS commands, implemented support
 -for long variable names, psql and gnumeric and made numerous revisions
 -to other modules.   
 +ONEWAY, EXAMINE, RANK and NPAR TESTS commands, implemented support for
 +long variable names, PostgreSQL and Gnumeric and made numerous
 +revisions to other modules.
  
  * Jason Stover contributed statistical and numerical functionality,
  including lib/gslextras and the linear regression features. Jason 
  is also an important contributor to GSL, which is used by PSPP. 
  
+ * Harry Thijssen contributed and maintains the Dutch translation.
  
  We also thank past contributors:
  
@@@ -22,8 -23,3 +23,8 @@@
  * Michael Kiefte contributed bug fixes and other enhancements.
  
  * Patrick Kobly contributed bug fixes and other enhancements.
 +
 +* Rob van Son wrote the original version of the routine for
 +  calculation of the significance of the Wilcoxon matched pairs signed
 +  rank statistic used by the NPAR TEST command.
 +
diff --combined NEWS
index df10031e1d6221403902643e9ffa63cd2a535673,61a493863d439685a6e722212e9e90233ba43cad..1798fdd3c28d8021df00c9076ea59f5e2b262c72
--- 1/NEWS
--- 2/NEWS
+++ b/NEWS
  PSPP NEWS -- history of user-visible changes.
- Time-stamp: <2009-05-24 22:25:04 blp>
+ Time-stamp: <2009-09-08 21:08:29 blp>
  Copyright (C) 1996-9, 2000, 2008, 2009 Free Software Foundation, Inc.
  See the end for copying conditions.
  
  Please send PSPP bug reports to bug-gnu-pspp@gnu.org.
  
- Changes from 0.6.1 to 0.7.0:
 +Changes from 0.7.1 to 0.7.2:
 +
 + * Updated Perl module interface.
 +
 + * Value labels for long string variables are now supported.
 +
 + * Missing values for long string variables are now supported.
 +
 +Changes from 0.7.0 to 0.7.1:
 +
 + *  Added a perl module to facilitate reading and writing of pspp system 
 +    files from perl programs.
 +
++Changes from 0.6.2-pre6 to 0.7.0:
 +
 +  * Custom variable and data file attributes are now supported.
 +    Commands VARIABLE ATTRIBUTE and DATAFILE ATTRIBUTE have been added
 +    for setting and clear attributes.  Support for attributes has also
 +    been added to commands that read and write system files, such as
 +    SAVE and GET, as well as to the DISPLAY command.
 +
+ Changes from 0.6.1 to 0.6.2-pre6:
+   * New translations:
+     - Dutch, thanks to Harry Thijssen.
+     - Brazilian Portuguese, thanks to Michel Boaventura.
+     Thanks for translations are also due to the coordinators at
+     translationproject.org.
+   * Statistical bug fixes:
+     - REGRESSION: Report correct standard error of the estimate (bug
+       #25677).
+     - T-TEST: Report correct significance of paired sample T-test in
+       the common case (bug #26936) and corner cases.  Thanks to Mike
+       Griffiths and Matej Cepl for reporting these bugs.
+   * Build fixes and changes:
+     - Make running "make" after running "configure" with different
+       settings reliably rebuild version.c.
+     - Cygwin and MinGW build fixes.
+     - Fixes for building with recent gnulib.
+     - The Makefile now honors two new variables, PSPP_LDFLAGS and
+       PSPPIRE_LDFLAGS, that affect linking of the PSPP and PSPPIRE
+       binaries, respectively.  This makes building easier for some
+       packagers.
+     - Fixes for "configure --enable-relocatable" (bug #25508).
+   * Data file bug fixes and changes:
+     - Fix reading text data files that contain a mix of white space
+       and commas.  Now "a ,b" is treated as two fields containing "a"
+       and "b"; previously it was treated as three, with an empty field
+       in the middle.
+     - Fix writing corrupted .sav files on Windows.
+     - Fix writing corrupted .por files (bug #26034).
+     - Fix reading .por files whose initial lines are not padded out
+       with spaces as expected.
+     - PSPP will no longer issue warnings about some .sav file records
+       or values that it does not understand.  These warnings were
+       harmless, but needlessly alarmed some users.
+     - Fix crash reading empty string fields from PostgreSQL databases.
+   * Bug fixes that affect PSPP and PSPPIRE:
+     - Users may now control precision of output statistics.  Instead
+       of hard coding the width and decimals of output numbers, respect
+       the default format in most instances.  Counts are now normally
+       displayed with the format of the weight variable, if any.
+     - Fix crash when an INSERT command specifies the name of a file
+       that does not exist (bug #24569).
+     - Fix crash when CROSSTABS specifies a long-string variable (bugs
+       #24557 and #26131).
+     - Fix crash drawing pie charts with many segments.
+     - Fix crash when NUMERIC specifies an invalid format.
+   * PSPPIRE bug fixes and changes:
+     - On Windows, write the output file to the user's home directory
+       instead of the current directory, to better match user
+       expectations.
+     - Some data editor fixes.
+   * Documentation:
+     - Fix typo in BINOMIAL section of user manual (bug #25892).
++>>>>>>> origin/stable:NEWS
  Changes from 0.6.0 to 0.6.1:
  
    * Statistical bug fixes:
@@@ -666,8 -730,6 +752,6 @@@ Changes for version 0.1.0
  ----------------------------------------------------------------------
  Copyright information:
  
- Copyright (C) 1996-9, 2000 Free Software Foundation, Inc.
     Permission is granted to anyone to make or distribute verbatim
     copies of this document as received, in any medium, provided that
     the copyright notice and this permission notice are preserved, thus
diff --combined po/automake.mk
index 1f5ca31bd0b1c3607586d7d5bdc0cc4895a102ea,0000000000000000000000000000000000000000..56715e1ebdc8c8acb5834e56f28c2b2c893987a9
mode 100644,000000..100644
--- /dev/null
@@@ -1,54 -1,0 +1,54 @@@
- POFILES=po/en_GB.po po/nl.po
 +include $(top_srcdir)/po/Makevars
 +
 +XGETTEXT=xgettext
 +MSGMERGE=msgmerge
 +MSGFMT=msgfmt
 +
++POFILES=po/en_GB.po po/nl.po po/pt_BR.po
 +
 +POTFILE=po/$(DOMAIN).pot
 +
 +TRANSLATABLE_FILES = $(DIST_SOURCES) $(all_q_sources)
 +
 +$(POTFILE): $(TRANSLATABLE_FILES)
 +      @$(MKDIR_P) po
 +      $(XGETTEXT) --directory=$(top_srcdir) $(TRANSLATABLE_FILES) \
 +      $(XGETTEXT_OPTIONS) \
 +      --copyright-holder="$(COPYRIGHT_HOLDER)" \
 +      --package-name=$(PACKAGE) \
 +      --package-version=$(VERSION) \
 +      --msgid-bugs-address=$(MSGID_BUGS_ADDRESS) \
 +      --add-comments='TRANSLATORS:' \
 +      -o $(POTFILE)
 +
 +
 +$(POFILES): $(POTFILE)
 +      $(MSGMERGE) $(top_srcdir)/$* $< -o $@
 +
 +.po.gmo:
 +      @$(MKDIR_P) `dirname $@`
 +      $(MSGFMT) $< -o $@
 +
 +
 +GMOFILES = $(POFILES:.po=.gmo)
 +
 +ALL_LOCAL += $(GMOFILES)
 +
 +install-data-hook: $(GMOFILES)
 +      for f in $(GMOFILES); do \
 +        lang=`echo $$f | sed -e 's%po/\(.*\)\.gmo%\1%' ` ; \
 +        $(INSTALL) -D $$f $(DESTDIR)$(prefix)/share/locale/$$lang/LC_MESSAGES/$(DOMAIN).mo ; \
 +      done
 +      
 +
 +uninstall-hook:
 +      for f in $(GMOFILES); do \
 +        lang=`echo $$f | sed -e 's%po/\(.*\)\.gmo%\1%' ` ; \
 +        $(RM) $(DESTDIR)$(prefix)/share/locale/$$lang/LC_MESSAGES/$(DOMAIN).mo ; \
 +      done
 +
 +
 +EXTRA_DIST += $(POFILES) $(POTFILE)
 +
 +CLEANFILES += $(POFILES) $(GMOFILES) $(POTFILE)
 +
diff --combined src/data/file-name.c
index 601afd5579e04a2ffff00193166989aedcd8e3b5,6be5ef0afa468b2705de8b95e88e328d8afca5c5..91229595924c7bfe7fe11d1486970ae598950f5c
@@@ -448,9 -448,9 +448,9 @@@ fn_compare_file_identities (const struc
  unsigned int
  fn_hash_identity (const struct file_identity *identity)
  {
 -  unsigned int hash = identity->device ^ identity->inode;
 +  unsigned int hash = hash_int (identity->device, identity->inode);
    if (identity->name != NULL)
 -    hash ^= hsh_hash_string (identity->name);
 +    hash = hash_string (identity->name, hash);
    return hash;
  }
  
@@@ -476,7 -476,7 +476,6 @@@ default_output_path (void
          const char *home_drive = getenv ("HOMEDRIVE");
          const char *home_path = getenv ("HOMEPATH");
  
--
          if (home_drive != NULL && home_path != NULL)
            home_dir = xasprintf ("%s%s",
                                  home_drive, home_path);
        if (home_dir == NULL)
        home_dir = "c:/users/default"; /* poor default */
  
-       path = xasprintf ("%s%c", home_dir, '/');
+       /* Copy home_dir into path.  Add a slash at the end but
+          only if there isn't already one there, because Windows
+          treats // specially. */
+       if (home_dir[0] == '\0'
+           || strchr ("/\\", home_dir[strlen (home_dir) - 1]) == NULL)
+         path = xasprintf ("%s%c", home_dir, '/');
+       else
+         path = xstrdup (home_dir);
  
 -
        for(i = 0; i < strlen (path); i++)
        if (path[i] == '\\') path[i] = '/';
      }
index 3fed2e05445135ec2ebcc300ce23c4855d0f41df,001b78f162a7d1724d73afba520f9066facbcfa4..5daea89dc5a908ac385a5f19dc8c2c62db7c0e20
@@@ -1,5 -1,5 +1,5 @@@
  /* PSPP - a program for statistical analysis.
 -   Copyright (C) 1997-9, 2000, 2006, 2007 Free Software Foundation, Inc.
 +   Copyright (C) 1997-9, 2000, 2006, 2007, 2009 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 <libpspp/message.h>
  #include <libpspp/misc.h>
  #include <libpspp/str.h>
 +#include <libpspp/i18n.h>
  #include <libpspp/version.h>
  
 +#include <data/attributes.h>
  #include <data/case.h>
  #include <data/casewriter-provider.h>
  #include <data/casewriter.h>
@@@ -95,34 -93,24 +95,34 @@@ struct sfm_write
  static const struct casewriter_class sys_file_casewriter_class;
  
  static void write_header (struct sfm_writer *, const struct dictionary *);
 -static void write_variable (struct sfm_writer *, const struct variable *);
 +static void write_variable (struct sfm_writer *, const struct variable *, const struct dictionary *);
  static void write_value_labels (struct sfm_writer *,
 -                                struct variable *, int idx);
 +                                struct variable *, int idx, const struct dictionary *);
  static void write_integer_info_record (struct sfm_writer *);
  static void write_float_info_record (struct sfm_writer *);
  
  static void write_longvar_table (struct sfm_writer *w,
                                   const struct dictionary *dict);
  
 +static void write_encoding_record (struct sfm_writer *w,
 +                                 const struct dictionary *);
 +
  static void write_vls_length_table (struct sfm_writer *w,
                              const struct dictionary *dict);
  
 +static void write_long_string_value_labels (struct sfm_writer *,
 +                                            const struct dictionary *);
  
  static void write_variable_display_parameters (struct sfm_writer *w,
                                                 const struct dictionary *dict);
  
  static void write_documents (struct sfm_writer *, const struct dictionary *);
  
 +static void write_data_file_attributes (struct sfm_writer *,
 +                                        const struct dictionary *);
 +static void write_variable_attributes (struct sfm_writer *,
 +                                       const struct dictionary *);
 +
  static void write_int (struct sfm_writer *, int32_t);
  static inline void convert_double_to_output_format (double, uint8_t[8]);
  static void write_float (struct sfm_writer *, double);
@@@ -132,9 -120,8 +132,9 @@@ static void write_zeros (struct sfm_wri
  static void write_spaces (struct sfm_writer *, size_t);
  static void write_value (struct sfm_writer *, const union value *, int width);
  
 -static void write_case_uncompressed (struct sfm_writer *, struct ccase *);
 -static void write_case_compressed (struct sfm_writer *, struct ccase *);
 +static void write_case_uncompressed (struct sfm_writer *,
 +                                     const struct ccase *);
 +static void write_case_compressed (struct sfm_writer *, const struct ccase *);
  static void flush_compressed (struct sfm_writer *);
  static void put_cmp_opcode (struct sfm_writer *, uint8_t);
  static void put_cmp_number (struct sfm_writer *, double);
@@@ -223,7 -210,7 +223,7 @@@ sfm_open_writer (struct file_handle *fh
    /* Write basic variable info. */
    short_names_assign (d);
    for (i = 0; i < dict_get_var_cnt (d); i++)
 -    write_variable (w, dict_get_var (d, i));
 +    write_variable (w, dict_get_var (d, i), d);
  
    /* Write out value labels. */
    idx = 0;
      {
        struct variable *v = dict_get_var (d, i);
  
 -      write_value_labels (w, v, idx);
 +      write_value_labels (w, v, idx, d);
        idx += sfm_width_to_octs (var_get_width (v));
      }
  
  
    write_vls_length_table (w, d);
  
 +  write_long_string_value_labels (w, d);
 +
 +  if (attrset_count (dict_get_attributes (d)))
 +    write_data_file_attributes (w, d);
 +  write_variable_attributes (w, d);
 +
 +  write_encoding_record (w, d);
 +
    /* Write end-of-headers record. */
    write_int (w, 999);
    write_int (w, 0);
        return NULL;
      }
  
 -  return casewriter_create (dict_get_next_value_idx (d),
 -                            &sys_file_casewriter_class, w);
 +  return casewriter_create (dict_get_proto (d), &sys_file_casewriter_class, w);
  
  error:
    close_writer (w);
@@@ -421,12 -401,12 +421,12 @@@ write_variable_continuation_records (st
  /* Write the variable record(s) for variable V to system file
     W. */
  static void
 -write_variable (struct sfm_writer *w, const struct variable *v)
 +write_variable (struct sfm_writer *w, const struct variable *v, const struct dictionary *dict)
  {
    int width = var_get_width (v);
    int segment_cnt = sfm_width_to_segments (width);
    int seg0_width = sfm_segment_alloc_width (width, 0);
 -  const struct missing_values *mv = var_get_missing_values (v);
 +  struct missing_values mv;
    int i;
  
    /* Record type. */
    /* Number of missing values.  If there is a range, then the
       range counts as 2 missing values and causes the number to be
       negated. */
 -  write_int (w, mv_has_range (mv) ? -2 - mv_n_values (mv) : mv_n_values (mv));
 +  mv_copy (&mv, var_get_missing_values (v));
 +  if (mv_get_width (&mv) > 8)
 +    mv_resize (&mv, 8);
 +  if (mv_has_range (&mv))
 +    write_int (w, -2 - mv_n_values (&mv));
 +  else
 +    write_int (w, mv_n_values (&mv));
  
    /* Print and write formats. */
    write_format (w, *var_get_print_format (v), seg0_width);
    /* Value label. */
    if (var_has_label (v))
      {
--      const char *label = var_get_label (v);
-       char *l = recode_string (dict_get_encoding (dict), UTF8, label, -1);
-       size_t padded_len = ROUND_UP (MIN (strlen (l), 255), 4);
-       write_int (w, padded_len);
-       write_string (w, l, padded_len);
-       free (l);
++      char *label = recode_string (dict_get_encoding (dict), UTF8, var_get_label (v), -1);
+       size_t label_len = MIN (strlen (label), 255);
+       size_t padded_len = ROUND_UP (label_len, 4);
+       write_int (w, label_len);
+       write_string (w, label, padded_len);
++      free (label);
      }
  
    /* Write the missing values, if any, range first. */
 -  if (mv_has_range (mv))
 +  if (mv_has_range (&mv))
      {
        double x, y;
 -      mv_get_range (mv, &x, &y);
 +      mv_get_range (&mv, &x, &y);
        write_float (w, x);
        write_float (w, y);
      }
 -  for (i = 0; i < mv_n_values (mv); i++)
 -    {
 -      union value value;
 -      mv_get_value (mv, &value, i);
 -      write_value (w, &value, seg0_width);
 -    }
 +  for (i = 0; i < mv_n_values (&mv); i++)
 +    write_value (w, mv_get_value (&mv, i), mv_get_width (&mv));
  
    write_variable_continuation_records (w, seg0_width);
  
  
        write_variable_continuation_records (w, seg_width);
      }
 +
 +  mv_destroy (&mv);
  }
  
  /* Writes the value labels for variable V having system file
 -   variable index IDX to system file W. */
 +   variable index IDX to system file W.
 +
 +   Value labels for long string variables are written separately,
 +   by write_long_string_value_labels. */
  static void
 -write_value_labels (struct sfm_writer *w, struct variable *v, int idx)
 +write_value_labels (struct sfm_writer *w, struct variable *v, int idx, const struct dictionary *dict)
  {
    const struct val_labs *val_labs;
 -  struct val_labs_iterator *i;
 -  struct val_lab *vl;
 +  const struct val_lab **labels;
 +  size_t n_labels;
 +  size_t i;
  
    val_labs = var_get_value_labels (v);
 -  if (val_labs == NULL)
 +  n_labels = val_labs_count (val_labs);
 +  if (n_labels == 0 || var_get_width (v) > 8)
      return;
  
    /* Value label record. */
    write_int (w, 3);             /* Record type. */
    write_int (w, val_labs_count (val_labs));
 -  for (vl = val_labs_first_sorted (val_labs, &i); vl != NULL;
 -       vl = val_labs_next (val_labs, &i))
 +  labels = val_labs_sorted (val_labs);
 +  for (i = 0; i < n_labels; i++)
      {
 -      uint8_t len = MIN (strlen (vl->label), 255);
 +      const struct val_lab *vl = labels[i];
 +      char *label = recode_string (dict_get_encoding (dict), UTF8, val_lab_get_label (vl), -1);
 +      uint8_t len = MIN (strlen (label), 255);
  
 -      write_value (w, &vl->value, var_get_width (v));
 +      write_value (w, val_lab_get_value (vl), var_get_width (v));
        write_bytes (w, &len, 1);
 -      write_bytes (w, vl->label, len);
 +      write_bytes (w, label, len);
        write_zeros (w, REM_RND_UP (len + 1, 8));
 +      free (label);
      }
 +  free (labels);
  
    /* Value label variable record. */
    write_int (w, 4);             /* Record type. */
@@@ -555,72 -521,6 +555,72 @@@ write_documents (struct sfm_writer *w, 
    write_bytes (w, dict_get_documents (d), line_cnt * DOC_LINE_LENGTH);
  }
  
 +static void
 +put_attrset (struct string *string, const struct attrset *attrs)
 +{
 +  const struct attribute *attr;
 +  struct attrset_iterator i;
 +
 +  for (attr = attrset_first (attrs, &i); attr != NULL;
 +       attr = attrset_next (attrs, &i)) 
 +    {
 +      size_t n_values = attribute_get_n_values (attr);
 +      size_t j;
 +
 +      ds_put_cstr (string, attribute_get_name (attr));
 +      ds_put_char (string, '(');
 +      for (j = 0; j < n_values; j++) 
 +        ds_put_format (string, "'%s'\n", attribute_get_value (attr, j));
 +      ds_put_char (string, ')');
 +    }
 +}
 +
 +static void
 +write_attribute_record (struct sfm_writer *w, const struct string *content,
 +                        int subtype) 
 +{
 +  write_int (w, 7);
 +  write_int (w, subtype);
 +  write_int (w, 1);
 +  write_int (w, ds_length (content));
 +  write_bytes (w, ds_data (content), ds_length (content));
 +}
 +
 +static void
 +write_data_file_attributes (struct sfm_writer *w,
 +                            const struct dictionary *d)
 +{
 +  struct string s = DS_EMPTY_INITIALIZER;
 +  put_attrset (&s, dict_get_attributes (d));
 +  write_attribute_record (w, &s, 17);
 +  ds_destroy (&s);
 +}
 +
 +static void
 +write_variable_attributes (struct sfm_writer *w, const struct dictionary *d)
 +{
 +  struct string s = DS_EMPTY_INITIALIZER;
 +  size_t n_vars = dict_get_var_cnt (d);
 +  size_t n_attrsets = 0;
 +  size_t i;
 +
 +  for (i = 0; i < n_vars; i++)
 +    { 
 +      struct variable *v = dict_get_var (d, i);
 +      struct attrset *attrs = var_get_attributes (v);
 +      if (attrset_count (attrs)) 
 +        {
 +          if (n_attrsets++)
 +            ds_put_char (&s, '/');
 +          ds_put_format (&s, "%s:", var_get_short_name (v, 0));
 +          put_attrset (&s, attrs);
 +        }
 +    }
 +  if (n_attrsets) 
 +    write_attribute_record (w, &s, 18);
 +  ds_destroy (&s);
 +}
 +
  /* Write the alignment, width and scale values. */
  static void
  write_variable_display_parameters (struct sfm_writer *w,
@@@ -684,89 -584,6 +684,89 @@@ write_vls_length_table (struct sfm_writ
    ds_destroy (&map);
  }
  
 +
 +static void
 +write_long_string_value_labels (struct sfm_writer *w,
 +                                const struct dictionary *dict)
 +{
 +  size_t n_vars = dict_get_var_cnt (dict);
 +  size_t size, i;
 +  off_t start UNUSED;
 +
 +  /* Figure out the size in advance. */
 +  size = 0;
 +  for (i = 0; i < n_vars; i++)
 +    {
 +      struct variable *var = dict_get_var (dict, i);
 +      const struct val_labs *val_labs = var_get_value_labels (var);
 +      int width = var_get_width (var);
 +      const struct val_lab *val_lab;
 +
 +      if (val_labs_count (val_labs) == 0 || width < 9)
 +        continue;
 +
 +      size += 12 + strlen (var_get_name (var));
 +      for (val_lab = val_labs_first (val_labs); val_lab != NULL;
 +           val_lab = val_labs_next (val_labs, val_lab))
 +        size += 8 + width + strlen (val_lab_get_label (val_lab));
 +    }
 +  if (size == 0)
 +    return;
 +
 +  write_int (w, 7);             /* Record type. */
 +  write_int (w, 21);            /* Record subtype */
 +  write_int (w, 1);             /* Data item (byte) size. */
 +  write_int (w, size);          /* Number of data items. */
 +
 +  start = ftello (w->file);
 +  for (i = 0; i < n_vars; i++)
 +    {
 +      struct variable *var = dict_get_var (dict, i);
 +      const struct val_labs *val_labs = var_get_value_labels (var);
 +      const char *var_name = var_get_name (var);
 +      int width = var_get_width (var);
 +      const struct val_lab *val_lab;
 +
 +      if (val_labs_count (val_labs) == 0 || width < 9)
 +        continue;
 +
 +      write_int (w, strlen (var_name));
 +      write_bytes (w, var_name, strlen (var_name));
 +      write_int (w, width);
 +      write_int (w, val_labs_count (val_labs));
 +      for (val_lab = val_labs_first (val_labs); val_lab != NULL;
 +           val_lab = val_labs_next (val_labs, val_lab))
 +        {
 +          const char *label = val_lab_get_label (val_lab);
 +          size_t label_length = strlen (label);
 +
 +          write_int (w, width);
 +          write_bytes (w, value_str (val_lab_get_value (val_lab), width),
 +                       width);
 +          write_int (w, label_length);
 +          write_bytes (w, label, label_length);
 +        }
 +    }
 +  assert (ftello (w->file) == start + size);
 +}
 +
 +static void
 +write_encoding_record (struct sfm_writer *w,
 +                     const struct dictionary *d)
 +{
 +  const char *enc = dict_get_encoding (d);
 +
 +  if ( NULL == enc)
 +    return;
 +
 +  write_int (w, 7);             /* Record type. */
 +  write_int (w, 20);            /* Record subtype. */
 +  write_int (w, 1);             /* Data item (char) size. */
 +  write_int (w, strlen (enc));  /* Number of data items. */
 +  write_string (w, enc, strlen (enc));
 +}
 +
 +
  /* Writes the long variable name table. */
  static void
  write_longvar_table (struct sfm_writer *w, const struct dictionary *dict)
    for (i = 0; i < dict_get_var_cnt (dict); i++)
      {
        struct variable *v = dict_get_var (dict, i);
 +      char *longname = recode_string (dict_get_encoding (dict), UTF8, var_get_name (v), -1);
  
        if (i)
          ds_put_char (&map, '\t');
        ds_put_format (&map, "%s=%s",
 -                     var_get_short_name (v, 0), var_get_name (v));
 +                     var_get_short_name (v, 0), longname);
 +      free (longname);
      }
  
    write_int (w, 7);             /* Record type. */
@@@ -857,7 -672,7 +857,7 @@@ sys_file_casewriter_write (struct casew
    if (ferror (w->file))
      {
        casewriter_force_error (writer);
 -      case_destroy (c);
 +      case_unref (c);
        return;
      }
  
    else
      write_case_compressed (w, c);
  
 -  case_destroy (c);
 +  case_unref (c);
  }
  
  /* Destroys system file writer W. */
@@@ -946,7 -761,7 +946,7 @@@ static const struct casewriter_class sy
  \f
  /* Writes case C to system file W, without compressing it. */
  static void
 -write_case_uncompressed (struct sfm_writer *w, struct ccase *c)
 +write_case_uncompressed (struct sfm_writer *w, const struct ccase *c)
  {
    size_t i;
  
      {
        struct sfm_var *v = &w->sfm_vars[i];
  
 -      if (v->width == 0)
 +      if (v->var_width == 0)
          write_float (w, case_num_idx (c, v->case_index));
        else
          {
            write_bytes (w, case_str_idx (c, v->case_index) + v->offset,
 -                       v->width);
 +                       v->segment_width);
            write_spaces (w, v->padding);
          }
      }
  
  /* Writes case C to system file W, with compression. */
  static void
 -write_case_compressed (struct sfm_writer *w, struct ccase *c)
 +write_case_compressed (struct sfm_writer *w, const struct ccase *c)
  {
    size_t i;
  
      {
        struct sfm_var *v = &w->sfm_vars[i];
  
 -      if (v->width == 0)
 +      if (v->var_width == 0)
          {
            double d = case_num_idx (c, v->case_index);
            if (d == SYSMIS)
               multiple of 8, by ensuring that the final partial
               oct (8 byte unit) is treated as padded with spaces
               on the right. */
 -          for (width = v->width; width > 0; width -= 8, offset += 8)
 +          for (width = v->segment_width; width > 0; width -= 8, offset += 8)
              {
                const void *data = case_str_idx (c, v->case_index) + offset;
                int chunk_size = MIN (width, 8);
@@@ -1122,13 -937,13 +1122,13 @@@ write_value (struct sfm_writer *w, cons
      write_float (w, value->f);
    else
      {
 -      write_bytes (w, value->s, width);
 +      write_bytes (w, value_str (value, width), width);
        write_zeros (w, 8 - width);
      }
  }
  
  /* Writes null-terminated STRING in a field of the given WIDTH to
 -   W.  If WIDTH is longer than WIDTH, it is truncated; if WIDTH
 +   W.  If STRING is longer than WIDTH, it is truncated; if WIDTH
     is narrowed, it is padded on the right with spaces. */
  static void
  write_string (struct sfm_writer *w, const char *string, size_t width)
index e8333b1ceeaa5257d922b9be4700bc6aec8ef7d1,592bb56b13fa18d02279a6c50082f448e51849d5..08077942340234031939cded549e87886d5119ee
@@@ -1,5 -1,5 +1,5 @@@
  /* PSPP - a program for statistical analysis.
 -   Copyright (C) 2004, 2009 Free Software Foundation, Inc.
 +   Copyright (C) 2004, 2008, 2009 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 <stdio.h>
  #include <stdlib.h>
  
 +#include <math/sort.h>
 +#include <math/order-stats.h>
 +#include <math/percentiles.h>
 +#include <math/tukey-hinges.h>
 +#include <math/box-whisker.h>
 +#include <math/trimmed-mean.h>
 +#include <math/extrema.h>
 +#include <math/np.h>
  #include <data/case.h>
  #include <data/casegrouper.h>
  #include <data/casereader.h>
 +#include <data/casewriter.h>
  #include <data/dictionary.h>
  #include <data/procedure.h>
 +#include <data/subcase.h>
  #include <data/value-labels.h>
  #include <data/variable.h>
 -#include <data/format.h>
  #include <language/command.h>
  #include <language/dictionary/split-file.h>
  #include <language/lexer/lexer.h>
@@@ -47,7 -38,9 +47,7 @@@
  #include <libpspp/message.h>
  #include <libpspp/misc.h>
  #include <libpspp/str.h>
 -#include <math/factor-stats.h>
  #include <math/moments.h>
 -#include <math/percentiles.h>
  #include <output/charts/box-whisker.h>
  #include <output/charts/cartesian.h>
  #include <output/manager.h>
@@@ -64,7 -57,6 +64,7 @@@
  #include <output/chart.h>
  #include <output/charts/plot-hist.h>
  #include <output/charts/plot-chart.h>
 +#include <math/histogram.h>
  
  /* (specification)
     "EXAMINE" (xmn_):
@@@ -72,8 -64,8 +72,8 @@@
     +total=custom;
     +nototal=custom;
     missing=miss:pairwise/!listwise,
 -           rep:report/!noreport,
 -           incl:include/!exclude;
 +   rep:report/!noreport,
 +   incl:include/!exclude;
     +compare=cmp:variables/!groups;
     +percentiles=custom;
     +id=var;
  /* (functions) */
  
  
 -
  static struct cmd_examine cmd;
  
  static const struct variable **dependent_vars;
 -
  static size_t n_dependent_vars;
  
 +/* PERCENTILES */
 +
 +static subc_list_double percentile_list;
 +static enum pc_alg percentile_algorithm;
  
 -struct factor
 +struct factor_metrics
  {
 -  /* The independent variable */
 -  struct variable *indep_var[2];
 +  struct moments1 *moments;
 +
 +  struct percentile **ptl;
 +  size_t n_ptiles;
 +
 +  struct statistic *tukey_hinges;
 +  struct statistic *box_whisker;
 +  struct statistic *trimmed_mean;
 +  struct statistic *histogram;
 +  struct order_stats *np;
 +
 +  /* Three quartiles indexing into PTL */
 +  struct percentile **quartiles;
 +
 +  /* A reader sorted in ASCENDING order */
 +  struct casereader *up_reader;
 +
 +  /* The minimum value of all the weights */
 +  double cmin;
 +
 +  /* Sum of all weights, including those for missing values */
 +  double n;
 +
 +  /* Sum of weights of non_missing values */
 +  double n_valid;
  
 +  double mean;
  
 -  /* Hash table of factor stats indexed by 2 values */
 -  struct hsh_table *fstats;
 +  double variance;
  
 -  /* The hash table after it has been crunched */
 -  struct factor_statistics **fs;
 +  double skewness;
  
 -  struct factor *next;
 +  double kurtosis;
  
 +  double se_mean;
 +
 +  struct extrema *minima;
 +  struct extrema *maxima;
  };
  
 -/* Linked list of factors */
 -static struct factor *factors = 0;
 +struct factor_result
 +{
 +  struct ll ll;
  
 -static struct metrics *totals = 0;
 +  union value value[2];
  
 -/* Parse the clause specifying the factors */
 -static int examine_parse_independent_vars (struct lexer *lexer, const struct dictionary *dict, struct cmd_examine *cmd);
 +  /* An array of factor metrics, one for each variable */
 +  struct factor_metrics *metrics;
 +};
  
 +struct xfactor
 +{
 +  /* We need to make a list of this structure */
 +  struct ll ll;
  
 +  /* The independent variable */
 +  const struct variable const* indep_var[2];
  
 -/* Output functions */
 -static void show_summary (const struct variable **dependent_var, int n_dep_var,
 -                        const struct dictionary *dict,
 -                        const struct factor *f);
 +  /* A list of results for this factor */
 +  struct ll_list result_list ;
 +};
  
 -static void show_extremes (const struct variable **dependent_var,
 -                         int n_dep_var,
 -                         const struct factor *factor,
 -                         int n_extremities);
  
 -static void show_descriptives (const struct variable **dependent_var,
 -                            int n_dep_var,
 -                            struct factor *factor);
 +static void
 +factor_destroy (struct xfactor *fctr)
 +{
 +  struct ll *ll = ll_head (&fctr->result_list);
 +  while (ll != ll_null (&fctr->result_list))
 +    {
 +      int v;
 +      struct factor_result *result =
 +      ll_data (ll, struct factor_result, ll);
 +      int i;
  
 -static void show_percentiles (const struct variable **dependent_var,
 -                           int n_dep_var,
 -                           struct factor *factor);
 +      for (v = 0; v < n_dependent_vars; ++v)
 +      {
 +        int i;
 +        moments1_destroy (result->metrics[v].moments);
 +        extrema_destroy (result->metrics[v].minima);
 +        extrema_destroy (result->metrics[v].maxima);
 +        statistic_destroy (result->metrics[v].trimmed_mean);
 +        statistic_destroy (result->metrics[v].tukey_hinges);
 +        statistic_destroy (result->metrics[v].box_whisker);
 +        statistic_destroy (result->metrics[v].histogram);
 +        for (i = 0 ; i < result->metrics[v].n_ptiles; ++i)
 +          statistic_destroy ((struct statistic *) result->metrics[v].ptl[i]);
 +        free (result->metrics[v].ptl);
 +        free (result->metrics[v].quartiles);
 +        casereader_destroy (result->metrics[v].up_reader);
 +      }
 +
 +      for (i = 0; i < 2; i++)
 +        if (fctr->indep_var[i])
 +          value_destroy (&result->value[i],
 +                         var_get_width (fctr->indep_var[i]));
 +      free (result->metrics);
 +      ll = ll_next (ll);
 +      free (result);
 +    }
 +}
  
 +static struct xfactor level0_factor;
 +static struct ll_list factor_list;
 +
 +/* Parse the clause specifying the factors */
 +static int examine_parse_independent_vars (struct lexer *lexer,
 +                                         const struct dictionary *dict,
 +                                         struct cmd_examine *cmd);
  
 +/* Output functions */
 +static void show_summary (const struct variable **dependent_var, int n_dep_var,
 +                        const struct dictionary *dict,
 +                        const struct xfactor *f);
  
  
 -void np_plot (const struct metrics *m, const char *factorname);
 +static void show_descriptives (const struct variable **dependent_var,
 +                             int n_dep_var,
 +                             const struct xfactor *f);
  
  
 -void box_plot_group (const struct factor *fctr,
 -                  const struct variable **vars, int n_vars,
 -                  const struct variable *id
 -                  ) ;
 +static void show_percentiles (const struct variable **dependent_var,
 +                             int n_dep_var,
 +                             const struct xfactor *f);
  
  
 -void box_plot_variables (const struct factor *fctr,
 -                      const struct variable **vars, int n_vars,
 -                      const struct variable *id
 -                      );
 +static void show_extremes (const struct variable **dependent_var,
 +                         int n_dep_var,
 +                         const struct xfactor *f);
 +
  
  
  
@@@ -244,24 -163,34 +244,24 @@@ void factor_calc (const struct ccase *c
  
  /* Represent a factor as a string, so it can be
     printed in a human readable fashion */
 -static void factor_to_string (const struct factor *fctr,
 -                             const struct factor_statistics *fs,
 -                            const struct variable *var,
 -                            struct string *str
 -                            );
 +static void factor_to_string (const struct xfactor *fctr,
 +                            const struct factor_result *result,
 +                            struct string *str);
  
  /* Represent a factor as a string, so it can be
     printed in a human readable fashion,
     but sacrificing some readablility for the sake of brevity */
 -static void factor_to_string_concise (const struct factor *fctr,
 -                                    const struct factor_statistics *fs,
 -                                    struct string *);
 -
 +static void
 +factor_to_string_concise (const struct xfactor *fctr,
 +                        const struct factor_result *result,
 +                        struct string *str
 +                        );
  
  
  
  /* Categories of missing values to exclude. */
  static enum mv_class exclude_values;
  
 -/* PERCENTILES */
 -
 -static subc_list_double percentile_list;
 -
 -static enum pc_alg percentile_algorithm;
 -
 -static short sbc_percentile;
 -
 -
  int
  cmd_examine (struct lexer *lexer, struct dataset *ds)
  {
    subc_list_double_create (&percentile_list);
    percentile_algorithm = PC_HAVERAGE;
  
 +  ll_init (&factor_list);
 +
    if ( !parse_examine (lexer, ds, &cmd, NULL) )
      {
        subc_list_double_destroy (&percentile_list);
      }
  
    grouper = casegrouper_create_splits (proc_open (ds), dataset_dict (ds));
 +
    while (casegrouper_get_next_group (grouper, &group))
 -    run_examine (&cmd, group, ds);
 +    {
 +      struct casereader *reader =
 +      casereader_create_arithmetic_sequence (group, 1, 1);
 +
 +      run_examine (&cmd, reader, ds);
 +    }
 +
    ok = casegrouper_destroy (grouper);
    ok = proc_commit (ds) && ok;
  
 -  if ( totals )
 +  if ( dependent_vars )
 +    free (dependent_vars);
 +
 +  subc_list_double_destroy (&percentile_list);
 +
 +  return ok ? CMD_SUCCESS : CMD_CASCADING_FAILURE;
 +};
 +
 +
 +/* Plot the normal and detrended normal plots for RESULT.
 +   Label the plots with LABEL */
 +static void
 +np_plot (struct np *np, const char *label)
 +{
 +  double yfirst = 0, ylast = 0;
 +
 +  double x_lower;
 +  double x_upper;
 +  double slack;
 +
 +  /* Normal Plot */
 +  struct chart *np_chart;
 +
 +  /* Detrended Normal Plot */
 +  struct chart *dnp_chart;
 +
 +  /* The slope and intercept of the ideal normal probability line */
 +  const double slope = 1.0 / np->stddev;
 +  const double intercept = -np->mean / np->stddev;
 +
 +  if ( np->n < 1.0 )
      {
 -      free ( totals );
 +      msg (MW, _("Not creating plot because data set is empty."));
 +      return ;
      }
  
 -  if ( dependent_vars )
 -    free (dependent_vars);
 +  np_chart = chart_create ();
 +  dnp_chart = chart_create ();
 +
 +  if ( !np_chart || ! dnp_chart )
 +    return ;
 +
 +  chart_write_title (np_chart, _("Normal Q-Q Plot of %s"), label);
 +  chart_write_xlabel (np_chart, _("Observed Value"));
 +  chart_write_ylabel (np_chart, _("Expected Normal"));
 +
 +  chart_write_title (dnp_chart, _("Detrended Normal Q-Q Plot of %s"),
 +                   label);
 +  chart_write_xlabel (dnp_chart, _("Observed Value"));
 +  chart_write_ylabel (dnp_chart, _("Dev from Normal"));
 +
 +  yfirst = gsl_cdf_ugaussian_Pinv (1 / (np->n + 1));
 +  ylast = gsl_cdf_ugaussian_Pinv (np->n / (np->n + 1));
 +
 +  /* Need to make sure that both the scatter plot and the ideal fit into the
 +     plot */
 +  x_lower = MIN (np->y_min, (yfirst - intercept) / slope) ;
 +  x_upper = MAX (np->y_max, (ylast  - intercept) / slope) ;
 +  slack = (x_upper - x_lower)  * 0.05 ;
 +
 +  chart_write_xscale (np_chart, x_lower - slack, x_upper + slack, 5);
 +  chart_write_xscale (dnp_chart, np->y_min, np->y_max, 5);
 +
 +  chart_write_yscale (np_chart, yfirst, ylast, 5);
 +  chart_write_yscale (dnp_chart, np->dns_min, np->dns_max, 5);
  
    {
 -    struct factor *f = factors ;
 -    while ( f )
 +    struct casereader *reader = casewriter_make_reader (np->writer);
 +    struct ccase *c;
 +    while ((c = casereader_read (reader)) != NULL)
        {
 -      struct factor *ff = f;
 +              chart_datum (np_chart, 0, case_data_idx (c, NP_IDX_Y)->f, case_data_idx (c, NP_IDX_NS)->f);
 +      chart_datum (dnp_chart, 0, case_data_idx (c, NP_IDX_Y)->f, case_data_idx (c, NP_IDX_DNS)->f);
  
 -      f = f->next;
 -      free ( ff->fs );
 -      hsh_destroy ( ff->fstats ) ;
 -      free ( ff ) ;
 +      case_unref (c);
        }
 -    factors = 0;
 +    casereader_destroy (reader);
    }
  
 -  subc_list_double_destroy (&percentile_list);
 -
 -  return ok ? CMD_SUCCESS : CMD_CASCADING_FAILURE;
 -};
 +  chart_line (dnp_chart, 0, 0, np->y_min, np->y_max , CHART_DIM_X);
 +  chart_line (np_chart, slope, intercept, yfirst, ylast , CHART_DIM_Y);
  
 +  chart_submit (np_chart);
 +  chart_submit (dnp_chart);
 +}
  
  
 -/* Show all the appropriate tables */
  static void
 -output_examine (const struct dictionary *dict)
 +show_npplot (const struct variable **dependent_var,
 +           int n_dep_var,
 +           const struct xfactor *fctr)
  {
 -  struct factor *fctr;
 +  int v;
  
 -  /* Show totals if appropriate */
 -  if ( ! cmd.sbc_nototal || factors == 0 )
 +  for (v = 0; v < n_dep_var; ++v)
      {
 -      show_summary (dependent_vars, n_dependent_vars, dict, 0);
 +      struct ll *ll;
 +      for (ll = ll_head (&fctr->result_list);
 +         ll != ll_null (&fctr->result_list);
 +         ll = ll_next (ll))
 +      {
 +        struct string str;
 +        const struct factor_result *result =
 +          ll_data (ll, struct factor_result, ll);
 +
 +        ds_init_empty (&str);
 +        ds_put_format (&str, "%s ", var_get_name (dependent_var[v]));
 +
 +        factor_to_string (fctr, result, &str);
 +
 +        np_plot ((struct np*) result->metrics[v].np, ds_cstr(&str));
 +
 +        statistic_destroy ((struct statistic *)result->metrics[v].np);
 +
 +        ds_destroy (&str);
 +      }
 +    }
 +}
 +
 +
 +static void
 +show_histogram (const struct variable **dependent_var,
 +              int n_dep_var,
 +              const struct xfactor *fctr)
 +{
 +  int v;
  
 -      if ( cmd.sbc_statistics )
 +  for (v = 0; v < n_dep_var; ++v)
 +    {
 +      struct ll *ll;
 +      for (ll = ll_head (&fctr->result_list);
 +         ll != ll_null (&fctr->result_list);
 +         ll = ll_next (ll))
        {
 -        if ( cmd.a_statistics[XMN_ST_EXTREME])
 -          show_extremes (dependent_vars, n_dependent_vars, 0, cmd.st_n);
 +        struct string str;
 +        const struct factor_result *result =
 +          ll_data (ll, struct factor_result, ll);
  
 -        if ( cmd.a_statistics[XMN_ST_DESCRIPTIVES])
 -          show_descriptives (dependent_vars, n_dependent_vars, 0);
 +        ds_init_empty (&str);
 +        ds_put_format (&str, "%s ", var_get_name (dependent_var[v]));
  
 +        factor_to_string (fctr, result, &str);
 +
 +        histogram_plot ((struct histogram *) result->metrics[v].histogram,
 +                        ds_cstr (&str),
 +                        (struct moments1 *) result->metrics[v].moments);
 +
 +        ds_destroy (&str);
        }
 -      if ( sbc_percentile )
 -      show_percentiles (dependent_vars, n_dependent_vars, 0);
 +    }
 +}
 +
 +
 +
 +static void
 +show_boxplot_groups (const struct variable **dependent_var,
 +                   int n_dep_var,
 +                   const struct xfactor *fctr)
 +{
 +  int v;
 +
 +  for (v = 0; v < n_dep_var; ++v)
 +    {
 +      struct ll *ll;
 +      int f = 0;
 +      struct chart *ch = chart_create ();
 +      double y_min = DBL_MAX;
 +      double y_max = -DBL_MAX;
  
 -      if ( cmd.sbc_plot)
 +      for (ll = ll_head (&fctr->result_list);
 +         ll != ll_null (&fctr->result_list);
 +         ll = ll_next (ll))
        {
 -        int v;
 -        if ( cmd.a_plot[XMN_PLT_STEMLEAF] )
 -          msg (SW, _ ("%s is not currently supported."), "STEMLEAF");
 +        const struct extremum  *max, *min;
 +        const struct factor_result *result =
 +          ll_data (ll, struct factor_result, ll);
  
 -        if ( cmd.a_plot[XMN_PLT_SPREADLEVEL] )
 -          msg (SW, _ ("%s is not currently supported."), "SPREADLEVEL");
 +        const struct ll_list *max_list =
 +          extrema_list (result->metrics[v].maxima);
  
 -        if ( cmd.a_plot[XMN_PLT_NPPLOT] )
 -          {
 -            for ( v = 0 ; v < n_dependent_vars; ++v )
 -              np_plot (&totals[v], var_to_string (dependent_vars[v]));
 -          }
 +        const struct ll_list *min_list =
 +          extrema_list (result->metrics[v].minima);
  
 -        if ( cmd.a_plot[XMN_PLT_BOXPLOT] )
 +        if ( ll_is_empty (max_list))
            {
 -            if ( cmd.cmp == XMN_GROUPS )
 -              {
 -                box_plot_group (0, (const struct variable **) dependent_vars,
 -                                  n_dependent_vars, cmd.v_id);
 -              }
 -            else
 -              box_plot_variables (0,
 -                                    (const struct variable **) dependent_vars,
 -                                    n_dependent_vars, cmd.v_id);
 +            msg (MW, _("Not creating plot because data set is empty."));
 +            continue;
            }
  
 -        if ( cmd.a_plot[XMN_PLT_HISTOGRAM] )
 -          {
 -            for ( v = 0 ; v < n_dependent_vars; ++v )
 -              {
 -                struct normal_curve normal;
 +        max = (const struct extremum *)
 +          ll_data (ll_head(max_list), struct extremum, ll);
  
 -                normal.N      = totals[v].n;
 -                normal.mean   = totals[v].mean;
 -                normal.stddev = totals[v].stddev;
 +          min = (const struct extremum *)
 +          ll_data (ll_head (min_list), struct extremum, ll);
  
 -                histogram_plot (totals[v].histogram,
 -                               var_to_string (dependent_vars[v]),
 -                               &normal, 0);
 -              }
 -          }
 +        y_max = MAX (y_max, max->value);
 +        y_min = MIN (y_min, min->value);
 +      }
 +
 +      boxplot_draw_yscale (ch, y_max, y_min);
 +
 +      if ( fctr->indep_var[0])
 +      chart_write_title (ch, _("Boxplot of %s vs. %s"),
 +                         var_to_string (dependent_var[v]),
 +                         var_to_string (fctr->indep_var[0]) );
 +      else
 +      chart_write_title (ch, _("Boxplot of %s"),
 +                         var_to_string (dependent_var[v]));
 +
 +      for (ll = ll_head (&fctr->result_list);
 +         ll != ll_null (&fctr->result_list);
 +         ll = ll_next (ll))
 +      {
 +        const struct factor_result *result =
 +          ll_data (ll, struct factor_result, ll);
 +
 +        struct string str;
 +        const double box_width = (ch->data_right - ch->data_left)
 +          / (ll_count (&fctr->result_list) * 2.0 ) ;
  
 +        const double box_centre = (f++ * 2 + 1) * box_width + ch->data_left;
 +
 +        ds_init_empty (&str);
 +        factor_to_string_concise (fctr, result, &str);
 +
 +        boxplot_draw_boxplot (ch,
 +                              box_centre, box_width,
 +                              (const struct box_whisker *)
 +                               result->metrics[v].box_whisker,
 +                              ds_cstr (&str));
 +
 +        ds_destroy (&str);
        }
  
 +      chart_submit (ch);
      }
 +}
  
  
 -  /* Show grouped statistics  as appropriate */
 -  fctr = factors;
 -  while ( fctr )
 -    {
 -      show_summary (dependent_vars, n_dependent_vars, dict, fctr);
  
 -      if ( cmd.sbc_statistics )
 -      {
 -        if ( cmd.a_statistics[XMN_ST_EXTREME])
 -          show_extremes (dependent_vars, n_dependent_vars, fctr, cmd.st_n);
 +static void
 +show_boxplot_variables (const struct variable **dependent_var,
 +                      int n_dep_var,
 +                      const struct xfactor *fctr
 +                      )
  
 -        if ( cmd.a_statistics[XMN_ST_DESCRIPTIVES])
 -          show_descriptives (dependent_vars, n_dependent_vars, fctr);
 -      }
 +{
 +  int v;
 +  struct ll *ll;
 +  const struct ll_list *result_list = &fctr->result_list;
 +
 +  for (ll = ll_head (result_list);
 +       ll != ll_null (result_list);
 +       ll = ll_next (ll))
 +
 +    {
 +      struct string title;
 +      struct chart *ch = chart_create ();
 +      double y_min = DBL_MAX;
 +      double y_max = -DBL_MAX;
  
 -      if ( sbc_percentile )
 -      show_percentiles (dependent_vars, n_dependent_vars, fctr);
 +      const struct factor_result *result =
 +      ll_data (ll, struct factor_result, ll);
  
 +      const double box_width = (ch->data_right - ch->data_left)
 +      / (n_dep_var * 2.0 ) ;
  
 -      if ( cmd.sbc_plot)
 +      for (v = 0; v < n_dep_var; ++v)
        {
 -        size_t v;
 +        const struct ll *max_ll =
 +          ll_head (extrema_list (result->metrics[v].maxima));
 +        const struct ll *min_ll =
 +          ll_head (extrema_list (result->metrics[v].minima));
  
 -        struct factor_statistics **fs = fctr->fs ;
 +        const struct extremum  *max =
 +          (const struct extremum *) ll_data (max_ll, struct extremum, ll);
  
 -        if ( cmd.a_plot[XMN_PLT_BOXPLOT] )
 -          {
 -            if ( cmd.cmp == XMN_VARIABLES )
 -              box_plot_variables (fctr,
 -                                    (const struct variable **) dependent_vars,
 -                                    n_dependent_vars, cmd.v_id);
 -            else
 -              box_plot_group (fctr,
 -                                (const struct variable **) dependent_vars,
 -                                n_dependent_vars, cmd.v_id);
 -          }
 +          const struct extremum  *min =
 +          (const struct extremum *) ll_data (min_ll, struct extremum, ll);
  
 -        for ( v = 0 ; v < n_dependent_vars; ++v )
 -          {
 +        y_max = MAX (y_max, max->value);
 +        y_min = MIN (y_min, min->value);
 +      }
  
 -            for ( fs = fctr->fs ; *fs ; ++fs )
 -              {
 -                struct string str;
 -                ds_init_empty (&str);
 -                factor_to_string (fctr, *fs, dependent_vars[v], &str);
  
 -                if ( cmd.a_plot[XMN_PLT_NPPLOT] )
 -                  np_plot (& (*fs)->m[v], ds_cstr (&str));
 +      boxplot_draw_yscale (ch, y_max, y_min);
  
 -                if ( cmd.a_plot[XMN_PLT_HISTOGRAM] )
 -                  {
 -                    struct normal_curve normal;
 +      ds_init_empty (&title);
 +      factor_to_string (fctr, result, &title);
  
 -                    normal.N      = (*fs)->m[v].n;
 -                    normal.mean   = (*fs)->m[v].mean;
 -                    normal.stddev = (*fs)->m[v].stddev;
 +#if 0
 +      ds_put_format (&title, "%s = ", var_get_name (fctr->indep_var[0]));
 +      var_append_value_name (fctr->indep_var[0], &result->value[0], &title);
 +#endif
  
 -                    histogram_plot ((*fs)->m[v].histogram,
 -                                   ds_cstr (&str) ,  &normal, 0);
 -                  }
 +      chart_write_title (ch, ds_cstr (&title));
 +      ds_destroy (&title);
  
 -                ds_destroy (&str);
 +      for (v = 0; v < n_dep_var; ++v)
 +      {
 +        struct string str;
 +        const double box_centre = (v * 2 + 1) * box_width + ch->data_left;
  
 -              } /* for ( fs .... */
 +        ds_init_empty (&str);
 +        ds_init_cstr (&str, var_get_name (dependent_var[v]));
  
 -          } /* for ( v = 0 ..... */
 +        boxplot_draw_boxplot (ch,
 +                              box_centre, box_width,
 +                              (const struct box_whisker *) result->metrics[v].box_whisker,
 +                              ds_cstr (&str));
  
 +        ds_destroy (&str);
        }
  
 -      fctr = fctr->next;
 +      chart_submit (ch);
      }
 -
  }
  
  
 -/* Create a hash table of percentiles and their values from the list of
 -   percentiles */
 -static struct hsh_table *
 -list_to_ptile_hash (const subc_list_double *l)
 +/* Show all the appropriate tables */
 +static void
 +output_examine (const struct dictionary *dict)
  {
 -  int i;
 +  struct ll *ll;
 +
 +  show_summary (dependent_vars, n_dependent_vars, dict, &level0_factor);
  
 -  struct hsh_table *h ;
 +  if ( cmd.a_statistics[XMN_ST_EXTREME] )
 +    show_extremes (dependent_vars, n_dependent_vars, &level0_factor);
  
 -  h = hsh_create (subc_list_double_count (l),
 -               (hsh_compare_func *) ptile_compare,
 -               (hsh_hash_func *) ptile_hash,
 -               (hsh_free_func *) free,
 -               0);
 +  if ( cmd.a_statistics[XMN_ST_DESCRIPTIVES] )
 +    show_descriptives (dependent_vars, n_dependent_vars, &level0_factor);
  
 +  if ( cmd.sbc_percentiles)
 +    show_percentiles (dependent_vars, n_dependent_vars, &level0_factor);
  
 -  for ( i = 0 ; i < subc_list_double_count (l) ; ++i )
 +  if ( cmd.sbc_plot)
      {
 -      struct percentile *p = xmalloc (sizeof *p);
 -
 -      p->p = subc_list_double_at (l,i);
 -      p->v = SYSMIS;
 +      if (cmd.a_plot[XMN_PLT_BOXPLOT])
 +      show_boxplot_groups (dependent_vars, n_dependent_vars, &level0_factor);
  
 -      hsh_insert (h, p);
 +      if (cmd.a_plot[XMN_PLT_HISTOGRAM])
 +      show_histogram (dependent_vars, n_dependent_vars, &level0_factor);
  
 +      if (cmd.a_plot[XMN_PLT_NPPLOT])
 +      show_npplot (dependent_vars, n_dependent_vars, &level0_factor);
      }
  
 -  return h;
 +  for (ll = ll_head (&factor_list);
 +       ll != ll_null (&factor_list); ll = ll_next (ll))
 +    {
 +      struct xfactor *factor = ll_data (ll, struct xfactor, ll);
 +      show_summary (dependent_vars, n_dependent_vars, dict, factor);
 +
 +      if ( cmd.a_statistics[XMN_ST_EXTREME] )
 +      show_extremes (dependent_vars, n_dependent_vars, factor);
  
 +      if ( cmd.a_statistics[XMN_ST_DESCRIPTIVES] )
 +      show_descriptives (dependent_vars, n_dependent_vars, factor);
 +
 +      if ( cmd.sbc_percentiles)
 +      show_percentiles (dependent_vars, n_dependent_vars, factor);
 +
 +      if (cmd.a_plot[XMN_PLT_BOXPLOT] &&
 +        cmd.cmp == XMN_GROUPS)
 +      show_boxplot_groups (dependent_vars, n_dependent_vars, factor);
 +
 +
 +      if (cmd.a_plot[XMN_PLT_BOXPLOT] &&
 +        cmd.cmp == XMN_VARIABLES)
 +      show_boxplot_variables (dependent_vars, n_dependent_vars,
 +                              factor);
 +
 +      if (cmd.a_plot[XMN_PLT_HISTOGRAM])
 +      show_histogram (dependent_vars, n_dependent_vars, factor);
 +
 +      if (cmd.a_plot[XMN_PLT_NPPLOT])
 +      show_npplot (dependent_vars, n_dependent_vars, factor);
 +    }
  }
  
  /* Parse the PERCENTILES subcommand */
  static int
  xmn_custom_percentiles (struct lexer *lexer, struct dataset *ds UNUSED,
 -                     struct cmd_examine *p UNUSED, void *aux UNUSED)
 +                      struct cmd_examine *p UNUSED, void *aux UNUSED)
  {
 -  sbc_percentile = 1;
 -
    lex_match (lexer, '=');
  
    lex_match (lexer, '(');
  
  /* TOTAL and NOTOTAL are simple, mutually exclusive flags */
  static int
 -xmn_custom_total (struct lexer *lexer UNUSED, struct dataset *ds UNUSED, struct cmd_examine *p, void *aux UNUSED)
 +xmn_custom_total (struct lexer *lexer UNUSED, struct dataset *ds UNUSED,
 +                struct cmd_examine *p, void *aux UNUSED)
  {
    if ( p->sbc_nototal )
      {
 -      msg (SE, _ ("%s and %s are mutually exclusive"),"TOTAL","NOTOTAL");
 +      msg (SE, _("%s and %s are mutually exclusive"),"TOTAL","NOTOTAL");
        return 0;
      }
  
@@@ -766,7 -513,7 +766,7 @@@ xmn_custom_nototal (struct lexer *lexe
  {
    if ( p->sbc_total )
      {
 -      msg (SE, _ ("%s and %s are mutually exclusive"),"TOTAL","NOTOTAL");
 +      msg (SE, _("%s and %s are mutually exclusive"), "TOTAL", "NOTOTAL");
        return 0;
      }
  
  /* Parser for the variables sub command
     Returns 1 on success */
  static int
 -xmn_custom_variables (struct lexer *lexer, struct dataset *ds, struct cmd_examine *cmd, void *aux UNUSED)
 +xmn_custom_variables (struct lexer *lexer, struct dataset *ds,
 +                    struct cmd_examine *cmd,
 +                    void *aux UNUSED)
  {
    const struct dictionary *dict = dataset_dict (ds);
    lex_match (lexer, '=');
  
    if ( (lex_token (lexer) != T_ID || dict_lookup_var (dict, lex_tokid (lexer)) == NULL)
 -      && lex_token (lexer) != T_ALL)
 +       && lex_token (lexer) != T_ALL)
      {
        return 2;
      }
  
    if (!parse_variables_const (lexer, dict, &dependent_vars, &n_dependent_vars,
 -                      PV_NO_DUPLICATE | PV_NUMERIC | PV_NO_SCRATCH) )
 +                            PV_NO_DUPLICATE | PV_NUMERIC | PV_NO_SCRATCH) )
      {
        free (dependent_vars);
        return 0;
  
    assert (n_dependent_vars);
  
 -  totals = xnmalloc (n_dependent_vars, sizeof *totals);
  
    if ( lex_match (lexer, T_BY))
      {
        int success ;
        success =  examine_parse_independent_vars (lexer, dict, cmd);
 -      if ( success != 1 ) {
 -        free (dependent_vars);
 -              free (totals) ;
 -      }
 +      if ( success != 1 )
 +      {
 +        free (dependent_vars);
 +      }
        return success;
      }
  
  
  /* Parse the clause specifying the factors */
  static int
 -examine_parse_independent_vars (struct lexer *lexer, const struct dictionary *dict, struct cmd_examine *cmd)
 +examine_parse_independent_vars (struct lexer *lexer,
 +                              const struct dictionary *dict,
 +                              struct cmd_examine *cmd)
  {
    int success;
 -  struct factor *sf = xmalloc (sizeof *sf);
 +  struct xfactor *sf = xmalloc (sizeof *sf);
  
 -  if ( (lex_token (lexer) != T_ID || dict_lookup_var (dict, lex_tokid (lexer)) == NULL)
 -      && lex_token (lexer) != T_ALL)
 +  ll_init (&sf->result_list);
 +
 +  if ( (lex_token (lexer) != T_ID ||
 +      dict_lookup_var (dict, lex_tokid (lexer)) == NULL)
 +       && lex_token (lexer) != T_ALL)
      {
        free ( sf ) ;
        return 2;
      }
  
 -
    sf->indep_var[0] = parse_variable (lexer, dict);
 -  sf->indep_var[1] = 0;
 +  sf->indep_var[1] = NULL;
  
    if ( lex_token (lexer) == T_BY )
      {
 -
        lex_match (lexer, T_BY);
  
 -      if ( (lex_token (lexer) != T_ID || dict_lookup_var (dict, lex_tokid (lexer)) == NULL)
 -        && lex_token (lexer) != T_ALL)
 +      if ( (lex_token (lexer) != T_ID ||
 +          dict_lookup_var (dict, lex_tokid (lexer)) == NULL)
 +         && lex_token (lexer) != T_ALL)
        {
 -        free ( sf ) ;
 +        free (sf);
          return 2;
        }
  
        sf->indep_var[1] = parse_variable (lexer, dict);
  
 +      ll_push_tail (&factor_list, &sf->ll);
      }
 -
 -
 -  sf->fstats = hsh_create (4,
 -                        (hsh_compare_func *) factor_statistics_compare,
 -                        (hsh_hash_func *) factor_statistics_hash,
 -                        (hsh_free_func *) factor_statistics_free,
 -                        0);
 -
 -  sf->next = factors;
 -  factors = sf;
 +  else
 +    ll_push_tail (&factor_list, &sf->ll);
  
    lex_match (lexer, ',');
  
    return success;
  }
  
 +static void
 +examine_group (struct cmd_examine *cmd, struct casereader *reader, int level,
 +             const struct dictionary *dict, struct xfactor *factor)
 +{
 +  struct ccase *c;
 +  const struct variable *wv = dict_get_weight (dict);
 +  int v;
 +  int n_extrema = 1;
 +  struct factor_result *result = xzalloc (sizeof (*result));
 +  int i;
 +
 +  for (i = 0; i < 2; i++)
 +    if (factor->indep_var[i])
 +      value_init (&result->value[i], var_get_width (factor->indep_var[i]));
  
 +  result->metrics = xcalloc (n_dependent_vars, sizeof (*result->metrics));
  
 +  if ( cmd->a_statistics[XMN_ST_EXTREME] )
 +    n_extrema = cmd->st_n;
  
 -static void populate_percentiles (struct tab_table *tbl, int col, int row,
 -                                const struct metrics *m);
  
 -static void populate_descriptives (struct tab_table *t, int col, int row,
 -                                 const struct variable *,
 -                                 const struct metrics *fs);
 +  c = casereader_peek (reader, 0);
 +  if (c != NULL)
 +    {
 +      if ( level > 0)
 +        for (i = 0; i < 2; i++)
 +          if (factor->indep_var[i])
 +            value_copy (&result->value[i], case_data (c, factor->indep_var[i]),
 +                        var_get_width (factor->indep_var[i]));
 +      case_unref (c);
 +    }
  
 -static void populate_extremes (struct tab_table *t, int col, int row, int n,
 -                             const struct variable *var,
 -                             const struct metrics *m);
 +  for (v = 0; v < n_dependent_vars; ++v)
 +    {
 +      struct casewriter *writer;
 +      struct casereader *input = casereader_clone (reader);
 +
 +      result->metrics[v].moments = moments1_create (MOMENT_KURTOSIS);
 +      result->metrics[v].minima = extrema_create (n_extrema, EXTREME_MINIMA);
 +      result->metrics[v].maxima = extrema_create (n_extrema, EXTREME_MAXIMA);
 +      result->metrics[v].cmin = DBL_MAX;
 +
 +      if (cmd->a_statistics[XMN_ST_DESCRIPTIVES] ||
 +        cmd->a_plot[XMN_PLT_BOXPLOT] ||
 +        cmd->a_plot[XMN_PLT_NPPLOT] ||
 +        cmd->sbc_percentiles)
 +      {
 +        /* In this case, we need to sort the data, so we create a sorting
 +           casewriter */
 +        struct subcase up_ordering;
 +          subcase_init_var (&up_ordering, dependent_vars[v], SC_ASCEND);
 +        writer = sort_create_writer (&up_ordering,
 +                                     casereader_get_proto (reader));
 +          subcase_destroy (&up_ordering);
 +      }
 +      else
 +      {
 +        /* but in this case, sorting is unnecessary, so an ordinary
 +           casewriter is sufficient */
 +        writer =
 +          autopaging_writer_create (casereader_get_proto (reader));
 +      }
  
 -static void populate_summary (struct tab_table *t, int col, int row,
 -                            const struct dictionary *dict,
 -                            const struct metrics *m);
  
 +      /* Sort or just iterate, whilst calculating moments etc */
 +      while ((c = casereader_read (input)) != NULL)
 +      {
 +          int n_vals = caseproto_get_n_widths (casereader_get_proto (reader));
 +        const casenumber loc = case_data_idx (c, n_vals - 1)->f;
  
 +        const double weight = wv ? case_data (c, wv)->f : 1.0;
 +        const union value *value = case_data (c, dependent_vars[v]);
  
 +        if (weight != SYSMIS)
 +          minimize (&result->metrics[v].cmin, weight);
  
 -/* Perform calculations for the sub factors */
 -void
 -factor_calc (const struct ccase *c, int case_no, double weight,
 -           bool case_missing)
 -{
 -  size_t v;
 -  struct factor *fctr = factors;
 +        moments1_add (result->metrics[v].moments,
 +                      value->f,
 +                      weight);
  
 -  while ( fctr)
 -    {
 -      struct factor_statistics **foo ;
 -      union value *indep_vals[2] ;
 +        result->metrics[v].n += weight;
  
 -      indep_vals[0] = value_dup (
 -                               case_data (c, fctr->indep_var[0]),
 -                               var_get_width (fctr->indep_var[0])
 -                               );
 +        if ( ! var_is_value_missing (dependent_vars[v], value, MV_ANY) )
 +          result->metrics[v].n_valid += weight;
  
 -      if ( fctr->indep_var[1] )
 -      indep_vals[1] = value_dup (
 -                                 case_data (c, fctr->indep_var[1]),
 -                                 var_get_width (fctr->indep_var[1])
 -                                 );
 -      else
 -      {
 -        const union value sm = {SYSMIS};
 -        indep_vals[1] = value_dup (&sm, 0);
 -      }
 +        extrema_add (result->metrics[v].maxima,
 +                     value->f,
 +                     weight,
 +                     loc);
  
 -      assert (fctr->fstats);
 +        extrema_add (result->metrics[v].minima,
 +                     value->f,
 +                     weight,
 +                     loc);
  
 -      foo = ( struct factor_statistics ** )
 -      hsh_probe (fctr->fstats, (void *) &indep_vals);
 +        casewriter_write (writer, c);
 +      }
 +      casereader_destroy (input);
 +      result->metrics[v].up_reader = casewriter_make_reader (writer);
 +    }
  
 -      if ( !*foo )
 +  /* If percentiles or descriptives have been requested, then a
 +     second pass through the data (which has now been sorted)
 +     is necessary */
 +  if ( cmd->a_statistics[XMN_ST_DESCRIPTIVES] ||
 +       cmd->a_plot[XMN_PLT_BOXPLOT] ||
 +       cmd->a_plot[XMN_PLT_NPPLOT] ||
 +       cmd->sbc_percentiles)
 +    {
 +      for (v = 0; v < n_dependent_vars; ++v)
        {
 +        int i;
 +        int n_os;
 +        struct order_stats **os ;
 +        struct factor_metrics *metric = &result->metrics[v];
  
 -        *foo = create_factor_statistics (n_dependent_vars,
 -                                        indep_vals[0],
 -                                        indep_vals[1]);
 +        metric->n_ptiles = percentile_list.n_data;
  
 -        for ( v =  0 ; v  < n_dependent_vars ; ++v )
 +        metric->ptl = xcalloc (metric->n_ptiles,
 +                               sizeof (struct percentile *));
 +
 +        metric->quartiles = xcalloc (3, sizeof (*metric->quartiles));
 +
 +        for (i = 0 ; i < metric->n_ptiles; ++i)
            {
 -            metrics_precalc ( & (*foo)->m[v] );
 +            metric->ptl[i] = (struct percentile *)
 +              percentile_create (percentile_list.data[i] / 100.0, metric->n_valid);
 +
 +            if ( percentile_list.data[i] == 25)
 +              metric->quartiles[0] = metric->ptl[i];
 +            else if ( percentile_list.data[i] == 50)
 +              metric->quartiles[1] = metric->ptl[i];
 +            else if ( percentile_list.data[i] == 75)
 +              metric->quartiles[2] = metric->ptl[i];
            }
  
 -      }
 -      else
 -      {
 -        free (indep_vals[0]);
 -        free (indep_vals[1]);
 -      }
 +        metric->tukey_hinges = tukey_hinges_create (metric->n_valid, metric->cmin);
 +        metric->trimmed_mean = trimmed_mean_create (metric->n_valid, 0.05);
  
 -      for ( v =  0 ; v  < n_dependent_vars ; ++v )
 -      {
 -        const struct variable *var = dependent_vars[v];
 -        union value *val = value_dup (
 -                                      case_data (c, var),
 -                                      var_get_width (var)
 -                                      );
 +        n_os = metric->n_ptiles + 2;
  
 -        if (case_missing || var_is_value_missing (var, val, exclude_values))
 +       if ( cmd->a_plot[XMN_PLT_NPPLOT] )
            {
 -            free (val);
 -            val = NULL;
 +            metric->np = np_create (metric->moments);
 +            n_os ++;
            }
  
 -        metrics_calc ( & (*foo)->m[v], val, weight, case_no);
 +        os = xcalloc (sizeof (struct order_stats *), n_os);
  
 -        free (val);
 -      }
 +        for (i = 0 ; i < metric->n_ptiles ; ++i )
 +          {
 +            os[i] = (struct order_stats *) metric->ptl[i];
 +          }
  
 -      fctr = fctr->next;
 -    }
 -}
 +        os[i] = (struct order_stats *) metric->tukey_hinges;
 +        os[i+1] = (struct order_stats *) metric->trimmed_mean;
  
 -static void
 -run_examine (struct cmd_examine *cmd, struct casereader *input,
 -             struct dataset *ds)
 -{
 -  struct dictionary *dict = dataset_dict (ds);
 -  casenumber case_no;
 -  struct ccase c;
 -  int v;
 -  bool ok;
 +        if (cmd->a_plot[XMN_PLT_NPPLOT])
 +          os[i+2] = metric->np;
  
 -  struct factor *fctr;
 -
 -  if (!casereader_peek (input, 0, &c))
 -    {
 -      casereader_destroy (input);
 -      return;
 +        order_stats_accumulate (os, n_os,
 +                                casereader_clone (metric->up_reader),
 +                                wv, dependent_vars[v], MV_ANY);
 +        free (os);
 +      }
      }
 -  output_split_file_values (ds, &c);
 -  case_destroy (&c);
 -
 -  input = casereader_create_filter_weight (input, dict, NULL, NULL);
 -  input = casereader_create_counter (input, &case_no, 0);
  
 -  /* Make sure we haven't got rubbish left over from a
 -     previous split. */
 -  fctr = factors;
 -  while (fctr)
 +  /* FIXME: Do this in the above loop */
 +  if ( cmd->a_plot[XMN_PLT_HISTOGRAM] )
      {
 -      struct factor *next = fctr->next;
 +      struct ccase *c;
 +      struct casereader *input = casereader_clone (reader);
  
 -      hsh_clear (fctr->fstats);
 +      for (v = 0; v < n_dependent_vars; ++v)
 +      {
 +        const struct extremum  *max, *min;
 +        struct factor_metrics *metric = &result->metrics[v];
  
 -      fctr->fs = 0;
 +        const struct ll_list *max_list =
 +          extrema_list (result->metrics[v].maxima);
  
 -      fctr = next;
 -    }
 +        const struct ll_list *min_list =
 +          extrema_list (result->metrics[v].minima);
  
 -  for ( v = 0 ; v < n_dependent_vars ; ++v )
 -    metrics_precalc (&totals[v]);
 +        if ( ll_is_empty (max_list))
 +          {
 +            msg (MW, _("Not creating plot because data set is empty."));
 +            continue;
 +          }
  
 -  for (; casereader_read (input, &c); case_destroy (&c))
 -    {
 -      bool case_missing = false;
 -      const double weight = dict_get_case_weight (dict, &c, NULL);
 +        assert (! ll_is_empty (min_list));
  
 -      if ( cmd->miss == XMN_LISTWISE )
 -      {
 -        for ( v = 0 ; v < n_dependent_vars ; ++v )
 -          {
 -            const struct variable *var = dependent_vars[v];
 -            union value *val = value_dup (
 -                                                case_data (&c, var),
 -                                                var_get_width (var)
 -                                                );
 +        max = (const struct extremum *)
 +          ll_data (ll_head(max_list), struct extremum, ll);
  
 -            if ( var_is_value_missing (var, val, exclude_values))
 -              case_missing = true;
 +          min = (const struct extremum *)
 +          ll_data (ll_head (min_list), struct extremum, ll);
  
 -            free (val);
 -          }
 +                metric->histogram = histogram_create (10, min->value, max->value);
        }
  
 -      for ( v = 0 ; v < n_dependent_vars ; ++v )
 +      while ((c = casereader_read (input)) != NULL)
        {
 -        const struct variable *var = dependent_vars[v];
 -        union value *val = value_dup (
 -                                      case_data (&c, var),
 -                                      var_get_width (var)
 -                                      );
 -
 -        if ( var_is_value_missing (var, val, exclude_values)
 -               || case_missing )
 +        const double weight = wv ? case_data (c, wv)->f : 1.0;
 +
 +        for (v = 0; v < n_dependent_vars; ++v)
            {
 -            free (val) ;
 -            val = NULL;
 +            struct factor_metrics *metric = &result->metrics[v];
 +            if ( metric->histogram)
 +              histogram_add ((struct histogram *) metric->histogram,
 +                             case_data (c, dependent_vars[v])->f, weight);
            }
 -
 -        metrics_calc (&totals[v], val, weight, case_no);
 -
 -        free (val);
 +        case_unref (c);
        }
 -
 -      factor_calc (&c, case_no, weight, case_missing);
 +      casereader_destroy (input);
      }
 -  ok = casereader_destroy (input);
  
 -  for ( v = 0 ; v < n_dependent_vars ; ++v)
 +  /* In this case, a third iteration is required */
 +  if (cmd->a_plot[XMN_PLT_BOXPLOT])
      {
 -      fctr = factors;
 -      while ( fctr )
 +      for (v = 0; v < n_dependent_vars; ++v)
        {
 -        struct hsh_iterator hi;
 -        struct factor_statistics *fs;
 +        struct factor_metrics *metric = &result->metrics[v];
 +          int n_vals = caseproto_get_n_widths (casereader_get_proto (
 +                                                 metric->up_reader));
 +
 +        metric->box_whisker =
 +          box_whisker_create ((struct tukey_hinges *) metric->tukey_hinges,
 +                              cmd->v_id, n_vals - 1);
 +
 +        order_stats_accumulate ((struct order_stats **) &metric->box_whisker,
 +                                1,
 +                                casereader_clone (metric->up_reader),
 +                                wv, dependent_vars[v], MV_ANY);
 +      }
 +    }
  
 -        for ( fs = hsh_first (fctr->fstats, &hi);
 -              fs != 0 ;
 -              fs = hsh_next (fctr->fstats, &hi))
 -          {
 +  ll_push_tail (&factor->result_list, &result->ll);
 +  casereader_destroy (reader);
 +}
  
 -            fs->m[v].ptile_hash = list_to_ptile_hash (&percentile_list);
 -            fs->m[v].ptile_alg = percentile_algorithm;
 -            metrics_postcalc (&fs->m[v]);
 -          }
  
 -        fctr = fctr->next;
 -      }
 +static void
 +run_examine (struct cmd_examine *cmd, struct casereader *input,
 +             struct dataset *ds)
 +{
 +  struct ll *ll;
 +  const struct dictionary *dict = dataset_dict (ds);
 +  struct ccase *c;
 +  struct casereader *level0 = casereader_clone (input);
  
 -      totals[v].ptile_hash = list_to_ptile_hash (&percentile_list);
 -      totals[v].ptile_alg = percentile_algorithm;
 -      metrics_postcalc (&totals[v]);
 +  c = casereader_peek (input, 0);
 +  if (c == NULL)
 +    {
 +      casereader_destroy (input);
 +      return;
      }
  
 +  output_split_file_values (ds, c);
 +  case_unref (c);
  
 -  /* Make sure that the combination of factors are complete */
 -
 -  fctr = factors;
 -  while ( fctr )
 -    {
 -      struct hsh_iterator hi;
 -      struct hsh_iterator hi0;
 -      struct hsh_iterator hi1;
 -      struct factor_statistics *fs;
 +  ll_init (&level0_factor.result_list);
  
 -      struct hsh_table *idh0 = NULL;
 -      struct hsh_table *idh1 = NULL;
 -      union value **val0;
 -      union value **val1;
 +  examine_group (cmd, level0, 0, dict, &level0_factor);
  
 -      idh0 = hsh_create (4, (hsh_compare_func *) compare_ptr_values,
 -                       (hsh_hash_func *) hash_ptr_value,
 -                      0,0);
 +  for (ll = ll_head (&factor_list);
 +       ll != ll_null (&factor_list);
 +       ll = ll_next (ll))
 +    {
 +      struct xfactor *factor = ll_data (ll, struct xfactor, ll);
  
 -      idh1 = hsh_create (4, (hsh_compare_func *) compare_ptr_values,
 -                       (hsh_hash_func *) hash_ptr_value,
 -                      0,0);
 +      struct casereader *group = NULL;
 +      struct casereader *level1;
 +      struct casegrouper *grouper1 = NULL;
  
 +      level1 = casereader_clone (input);
 +      level1 = sort_execute_1var (level1, factor->indep_var[0]);
 +      grouper1 = casegrouper_create_vars (level1, &factor->indep_var[0], 1);
  
 -      for ( fs = hsh_first (fctr->fstats, &hi);
 -          fs != 0 ;
 -          fs = hsh_next (fctr->fstats, &hi))
 +      while (casegrouper_get_next_group (grouper1, &group))
        {
 -        hsh_insert (idh0, &fs->id[0]);
 -        hsh_insert (idh1, &fs->id[1]);
 -      }
 +        struct casereader *group_copy = casereader_clone (group);
  
 -      /* Ensure that the factors combination is complete */
 -      for ( val0 = hsh_first (idh0, &hi0);
 -          val0 != 0 ;
 -          val0 = hsh_next (idh0, &hi0))
 -      {
 -        for ( val1 = hsh_first (idh1, &hi1);
 -              val1 != 0 ;
 -              val1 = hsh_next (idh1, &hi1))
 +        if ( !factor->indep_var[1])
 +          examine_group (cmd, group_copy, 1, dict, factor);
 +        else
            {
 -            struct factor_statistics **ffs;
 -            union value *key[2];
 -            key[0] = *val0;
 -            key[1] = *val1;
 -
 -            ffs = (struct factor_statistics **)
 -              hsh_probe (fctr->fstats, &key );
 -
 -            if ( !*ffs ) {
 -              size_t i;
 -               (*ffs) = create_factor_statistics (n_dependent_vars,
 -                                                 key[0], key[1]);
 -              for ( i = 0 ; i < n_dependent_vars ; ++i )
 -                metrics_precalc ( & (*ffs)->m[i]);
 -            }
 -          }
 -      }
 +            int n_groups = 0;
 +            struct casereader *group2 = NULL;
 +            struct casegrouper *grouper2 = NULL;
  
 -      hsh_destroy (idh0);
 -      hsh_destroy (idh1);
 +            group_copy = sort_execute_1var (group_copy,
 +                                              factor->indep_var[1]);
  
 -      fctr->fs = (struct factor_statistics **) hsh_sort_copy (fctr->fstats);
 +            grouper2 = casegrouper_create_vars (group_copy,
 +                                                  &factor->indep_var[1], 1);
  
 -      fctr = fctr->next;
 +            while (casegrouper_get_next_group (grouper2, &group2))
 +              {
 +                examine_group (cmd, group2, 2, dict, factor);
 +                n_groups++;
 +              }
 +            casegrouper_destroy (grouper2);
 +          }
 +
 +        casereader_destroy (group);
 +      }
 +      casegrouper_destroy (grouper1);
      }
  
 -  if (ok)
 -    output_examine (dict);
 +  casereader_destroy (input);
  
 +  output_examine (dict);
 +
 +  factor_destroy (&level0_factor);
 +
 +  {
 +    struct ll *ll;
 +    for (ll = ll_head (&factor_list);
 +       ll != ll_null (&factor_list);
 +       ll = ll_next (ll))
 +      {
 +      struct xfactor *f = ll_data (ll, struct xfactor, ll);
 +      factor_destroy (f);
 +      }
 +  }
  
 -  if ( totals )
 -    {
 -      size_t i;
 -      for ( i = 0 ; i < n_dependent_vars ; ++i )
 -      {
 -        metrics_destroy (&totals[i]);
 -      }
 -    }
  }
  
  
  static void
  show_summary (const struct variable **dependent_var, int n_dep_var,
              const struct dictionary *dict,
 -            const struct factor *fctr)
 +            const struct xfactor *fctr)
  {
 +  const struct variable *wv = dict_get_weight (dict);
 +  const struct fmt_spec *wfmt = wv ? var_get_print_format (wv) : & F_8_0;
 +
    static const char *subtitle[]=
      {
 -      N_ ("Valid"),
 -      N_ ("Missing"),
 -      N_ ("Total")
 +      N_("Valid"),
 +      N_("Missing"),
 +      N_("Total")
      };
  
 -  int i;
 -  int heading_columns ;
 +  int v, j;
 +  int heading_columns = 1;
    int n_cols;
    const int heading_rows = 3;
    struct tab_table *tbl;
  
    int n_rows ;
 -  int n_factors = 1;
 +  n_rows = n_dep_var;
  
 -  if ( fctr )
 +  assert (fctr);
 +
 +  if ( fctr->indep_var[0] )
      {
        heading_columns = 2;
 -      n_factors = hsh_count (fctr->fstats);
 -      n_rows = n_dep_var * n_factors ;
  
        if ( fctr->indep_var[1] )
 -      heading_columns = 3;
 -    }
 -  else
 -    {
 -      heading_columns = 1;
 -      n_rows = n_dep_var;
 +      {
 +        heading_columns = 3;
 +      }
      }
  
 +  n_rows *= ll_count (&fctr->result_list);
    n_rows += heading_rows;
  
    n_cols = heading_columns + 6;
  
 -  tbl = tab_create (n_cols,n_rows,0);
 +  tbl = tab_create (n_cols, n_rows, 0);
    tab_headers (tbl, heading_columns, 0, heading_rows, 0);
  
 -  tab_dim (tbl, tab_natural_dimensions);
 +  tab_dim (tbl, tab_natural_dimensions, NULL);
  
    /* Outline the box */
    tab_box (tbl,
    tab_vline (tbl, TAL_2, heading_columns, 0, n_rows - 1);
  
  
 -  tab_title (tbl, _ ("Case Processing Summary"));
 +  tab_title (tbl, _("Case Processing Summary"));
  
    tab_joint_text (tbl, heading_columns, 0,
 -               n_cols -1, 0,
 -               TAB_CENTER | TAT_TITLE,
 -               ("Cases"));
 +                n_cols -1, 0,
 +                TAB_CENTER | TAT_TITLE,
 +                _("Cases"));
  
    /* Remove lines ... */
    tab_box (tbl,
           heading_columns, 0,
           n_cols - 1, 0);
  
 -  for ( i = 0 ; i < 3 ; ++i )
 +  for (j = 0 ; j < 3 ; ++j)
      {
 -      tab_text (tbl, heading_columns + i * 2 , 2, TAB_CENTER | TAT_TITLE,
 -              _ ("N"));
 +      tab_text (tbl, heading_columns + j * 2 , 2, TAB_CENTER | TAT_TITLE,
 +              _("N"));
  
 -      tab_text (tbl, heading_columns + i * 2 + 1, 2, TAB_CENTER | TAT_TITLE,
 -              _ ("Percent"));
 +      tab_text (tbl, heading_columns + j * 2 + 1, 2, TAB_CENTER | TAT_TITLE,
 +              _("Percent"));
  
 -      tab_joint_text (tbl, heading_columns + i*2 , 1,
 -                   heading_columns + i * 2 + 1, 1,
 -                   TAB_CENTER | TAT_TITLE,
 -                   subtitle[i]);
 +      tab_joint_text (tbl, heading_columns + j * 2 , 1,
 +                    heading_columns + j * 2 + 1, 1,
 +                    TAB_CENTER | TAT_TITLE,
 +                    subtitle[j]);
  
        tab_box (tbl, -1, -1,
               TAL_0, TAL_0,
 -             heading_columns + i * 2, 1,
 -             heading_columns + i * 2 + 1, 1);
 +             heading_columns + j * 2, 1,
 +             heading_columns + j * 2 + 1, 1);
      }
  
  
    /* Titles for the independent variables */
 -  if ( fctr )
 +  if ( fctr->indep_var[0] )
      {
        tab_text (tbl, 1, heading_rows - 1, TAB_CENTER | TAT_TITLE,
                var_to_string (fctr->indep_var[0]));
        }
      }
  
 -
 -  for ( i = 0 ; i < n_dep_var ; ++i )
 +  for (v = 0 ; v < n_dep_var ; ++v)
      {
 -      int n_factors = 1;
 -      if ( fctr )
 -      n_factors = hsh_count (fctr->fstats);
 +      int j = 0;
 +      struct ll *ll;
 +      const union value *last_value = NULL;
  
 -      if ( i > 0 )
 -      tab_hline (tbl, TAL_1, 0, n_cols -1 , i * n_factors + heading_rows);
 +      if ( v > 0 )
 +      tab_hline (tbl, TAL_1, 0, n_cols -1 ,
 +                 v * ll_count (&fctr->result_list)
 +                 + heading_rows);
  
        tab_text (tbl,
 -              0, i * n_factors + heading_rows,
 +              0,
 +              v * ll_count (&fctr->result_list) + heading_rows,
                TAB_LEFT | TAT_TITLE,
 -              var_to_string (dependent_var[i])
 +              var_to_string (dependent_var[v])
                );
  
 -      if ( !fctr )
 -      populate_summary (tbl, heading_columns,
 -                       (i * n_factors) + heading_rows,
 -                        dict,
 -                       &totals[i]);
 -      else
 +
 +      for (ll = ll_head (&fctr->result_list);
 +         ll != ll_null (&fctr->result_list); ll = ll_next (ll))
        {
 -        struct factor_statistics **fs = fctr->fs;
 -        int count = 0 ;
 -        const union value *prev = NULL;
 +        double n;
 +        const struct factor_result *result =
 +          ll_data (ll, struct factor_result, ll);
  
 -        while (*fs)
 +        if ( fctr->indep_var[0] )
            {
 -            if ( !prev ||
 -                 0 != compare_values (prev, (*fs)->id[0],
 -                                 var_get_width (fctr->indep_var[0])))
 +
 +            if ( last_value == NULL ||
 +                 !value_equal (last_value, &result->value[0],
 +                                 var_get_width (fctr->indep_var[0])))
                {
 -                struct string vstr;
 -                ds_init_empty (&vstr);
 -                var_append_value_name (fctr->indep_var[0],
 -                                    (*fs)->id[0], &vstr);
 -
 -                tab_text (tbl,
 -                          1,
 -                          (i * n_factors ) + count +
 -                          heading_rows,
 +                struct string str;
 +
 +                last_value = &result->value[0];
 +                ds_init_empty (&str);
 +
 +                var_append_value_name (fctr->indep_var[0], &result->value[0],
 +                                       &str);
 +
 +                tab_text (tbl, 1,
 +                          heading_rows + j +
 +                          v * ll_count (&fctr->result_list),
                            TAB_LEFT | TAT_TITLE,
 -                          ds_cstr (&vstr)
 -                          );
 +                          ds_cstr (&str));
  
 -                ds_destroy (&vstr);
 +                ds_destroy (&str);
  
 -                if (fctr->indep_var[1] && count > 0 )
 +                if ( fctr->indep_var[1] && j > 0)
                    tab_hline (tbl, TAL_1, 1, n_cols - 1,
 -                            (i * n_factors ) + count + heading_rows);
 +                             heading_rows + j +
 +                             v * ll_count (&fctr->result_list));
                }
  
 -            prev = (*fs)->id[0];
 -
              if ( fctr->indep_var[1])
                {
 -                struct string vstr;
 -                ds_init_empty (&vstr);
 +                struct string str;
 +
 +                ds_init_empty (&str);
 +
                  var_append_value_name (fctr->indep_var[1],
 -                                       (*fs)->id[1], &vstr);
 -                tab_text (tbl,
 -                          2,
 -                          (i * n_factors ) + count +
 -                          heading_rows,
 +                                       &result->value[1], &str);
 +
 +                tab_text (tbl, 2,
 +                          heading_rows + j +
 +                          v * ll_count (&fctr->result_list),
                            TAB_LEFT | TAT_TITLE,
 -                          ds_cstr (&vstr)
 -                          );
 -                ds_destroy (&vstr);
 +                          ds_cstr (&str));
 +
 +                ds_destroy (&str);
                }
 +          }
  
 -            populate_summary (tbl, heading_columns,
 -                              (i * n_factors) + count
 -                              + heading_rows,
 -                              dict,
 -                              & (*fs)->m[i]);
  
 -            count++ ;
 -            fs++;
 -          }
 +        moments1_calculate (result->metrics[v].moments,
 +                            &n, &result->metrics[v].mean,
 +                            &result->metrics[v].variance,
 +                            &result->metrics[v].skewness,
 +                            &result->metrics[v].kurtosis);
 +
 +        result->metrics[v].se_mean = sqrt (result->metrics[v].variance / n) ;
 +
 +        /* Total Valid */
 +        tab_double (tbl, heading_columns,
 +                   heading_rows + j + v * ll_count (&fctr->result_list),
 +                   TAB_LEFT,
 +                   n, wfmt);
 +
 +        tab_text_format (tbl, heading_columns + 1,
 +                           heading_rows + j + v * ll_count (&fctr->result_list),
 +                           TAB_RIGHT,
 +                           "%g%%", n * 100.0 / result->metrics[v].n);
 +
 +        /* Total Missing */
 +        tab_double (tbl, heading_columns + 2,
 +                   heading_rows + j + v * ll_count (&fctr->result_list),
 +                   TAB_LEFT,
 +                   result->metrics[v].n - n,
 +                   wfmt);
 +
 +        tab_text_format (tbl, heading_columns + 3,
 +                           heading_rows + j + v * ll_count (&fctr->result_list),
 +                           TAB_RIGHT,
 +                           "%g%%",
 +                           (result->metrics[v].n - n) * 100.0 / result->metrics[v].n
 +                           );
 +
 +        /* Total Valid + Missing */
 +        tab_double (tbl, heading_columns + 4,
 +                   heading_rows + j + v * ll_count (&fctr->result_list),
 +                   TAB_LEFT,
 +                   result->metrics[v].n,
 +                   wfmt);
 +
 +        tab_text_format (tbl, heading_columns + 5,
 +                           heading_rows + j + v * ll_count (&fctr->result_list),
 +                           TAB_RIGHT,
 +                           "%g%%",
 +                           ((result->metrics[v].n) * 100.0
 +                            / result->metrics[v].n));
 +
 +        ++j;
        }
      }
  
 -  tab_submit (tbl);
 -}
  
 -
 -static void
 -populate_summary (struct tab_table *t, int col, int row,
 -                const struct dictionary *dict,
 -                const struct metrics *m)
 -
 -{
 -  const double total = m->n + m->n_missing ;
 -
 -  const struct variable *wv = dict_get_weight (dict);
 -  const struct fmt_spec *wfmt = wv ? var_get_print_format (wv) : & F_8_0;
 -
 -  tab_double (t, col + 0, row + 0, TAB_RIGHT, m->n, wfmt);
 -
 -  tab_double (t, col + 2, row + 0, TAB_RIGHT, m->n_missing, wfmt);
 -
 -  tab_double (t, col + 4, row + 0, TAB_RIGHT, total, wfmt);
 -
 -
 -  if ( total > 0 ) {
 -    tab_text (t, col + 1, row + 0, TAB_RIGHT | TAT_PRINTF, "%2.0f%%",
 -            100.0 * m->n / total );
 -
 -    tab_text (t, col + 3, row + 0, TAB_RIGHT | TAT_PRINTF, "%2.0f%%",
 -            100.0 * m->n_missing / total );
 -
 -    /* This seems a bit pointless !!! */
 -    tab_text (t, col + 5, row + 0, TAB_RIGHT | TAT_PRINTF, "%2.0f%%",
 -            100.0 * total / total );
 -  }
 +  tab_submit (tbl);
  }
  
 -
 +#define DESCRIPTIVE_ROWS 13
  
  static void
 -show_extremes (const struct variable **dependent_var, int n_dep_var,
 -             const struct factor *fctr,
 -             int n_extremities)
 +show_descriptives (const struct variable **dependent_var,
 +                 int n_dep_var,
 +                 const struct xfactor *fctr)
  {
 -  int i;
 -  int heading_columns ;
 +  int v;
 +  int heading_columns = 3;
    int n_cols;
    const int heading_rows = 1;
    struct tab_table *tbl;
  
 -
 -
 -  int n_factors = 1;
    int n_rows ;
 +  n_rows = n_dep_var;
  
 -  if ( fctr )
 -    {
 -      heading_columns = 2;
 -      n_factors = hsh_count (fctr->fstats);
 +  assert (fctr);
  
 -      n_rows = n_dep_var * 2 * n_extremities * n_factors;
 +  if ( fctr->indep_var[0] )
 +    {
 +      heading_columns = 4;
  
        if ( fctr->indep_var[1] )
 -      heading_columns = 3;
 -    }
 -  else
 -    {
 -      heading_columns = 1;
 -      n_rows = n_dep_var * 2 * n_extremities;
 +      {
 +        heading_columns = 5;
 +      }
      }
  
 +  n_rows *= ll_count (&fctr->result_list) * DESCRIPTIVE_ROWS;
    n_rows += heading_rows;
  
 -  heading_columns += 2;
    n_cols = heading_columns + 2;
  
 -  tbl = tab_create (n_cols,n_rows,0);
 +  tbl = tab_create (n_cols, n_rows, 0);
    tab_headers (tbl, heading_columns, 0, heading_rows, 0);
  
 -  tab_dim (tbl, tab_natural_dimensions);
 +  tab_dim (tbl, tab_natural_dimensions, NULL);
  
 -  /* Outline the box, No internal lines*/
 +  /* Outline the box */
    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_title (tbl, _ ("Extreme Values"));
 +  tab_hline (tbl, TAL_2, 0, n_cols - 1, heading_rows );
 +  tab_hline (tbl, TAL_2, 1, n_cols - 1, heading_rows );
  
 -  tab_vline (tbl, TAL_2, n_cols - 2, 0, n_rows -1);
 -  tab_vline (tbl, TAL_1, n_cols - 1, 0, n_rows -1);
 +  tab_vline (tbl, TAL_1, n_cols - 1, 0, n_rows - 1);
  
 -  if ( fctr )
 -    {
 -      tab_text (tbl, 1, heading_rows - 1, TAB_CENTER | TAT_TITLE,
 -              var_to_string (fctr->indep_var[0]));
  
 -      if ( fctr->indep_var[1] )
 -      tab_text (tbl, 2, heading_rows - 1, TAB_CENTER | TAT_TITLE,
 -                var_to_string (fctr->indep_var[1]));
 -    }
 +  if ( fctr->indep_var[0])
 +    tab_text (tbl, 1, 0, TAT_TITLE, var_to_string (fctr->indep_var[0]));
  
 -  tab_text (tbl, n_cols - 1, 0, TAB_CENTER | TAT_TITLE, _ ("Value"));
 -  tab_text (tbl, n_cols - 2, 0, TAB_CENTER | TAT_TITLE, _ ("Case Number"));
 +  if ( fctr->indep_var[1])
 +    tab_text (tbl, 2, 0, TAT_TITLE, var_to_string (fctr->indep_var[1]));
  
 -  for ( i = 0 ; i < n_dep_var ; ++i )
 +  for (v = 0 ; v < n_dep_var ; ++v )
      {
 +      struct ll *ll;
 +      int i = 0;
  
 -      if ( i > 0 )
 -      tab_hline (tbl, TAL_1, 0, n_cols -1 ,
 -                i * 2 * n_extremities * n_factors + heading_rows);
 +      const int row_var_start =
 +      v * DESCRIPTIVE_ROWS * ll_count(&fctr->result_list);
  
 -      tab_text (tbl, 0,
 -              i * 2 * n_extremities * n_factors  + heading_rows,
 +      tab_text (tbl,
 +              0,
 +              heading_rows + row_var_start,
                TAB_LEFT | TAT_TITLE,
 -              var_to_string (dependent_var[i])
 +              var_to_string (dependent_var[v])
                );
  
 -
 -      if ( !fctr )
 -      populate_extremes (tbl, heading_columns - 2,
 -                         i * 2 * n_extremities * n_factors  + heading_rows,
 -                         n_extremities,
 -                         dependent_var[i],
 -                         &totals[i]);
 -      else
 +      for (ll = ll_head (&fctr->result_list);
 +         ll != ll_null (&fctr->result_list); i++, ll = ll_next (ll))
        {
 -        struct factor_statistics **fs = fctr->fs;
 -        int count = 0 ;
 -        const union value *prev  = NULL;
 -
 -        while (*fs)
 -          {
 -            const int row = heading_rows + ( 2 * n_extremities )  *
 -               ( ( i  * n_factors  ) +  count );
 +        const struct factor_result *result =
 +          ll_data (ll, struct factor_result, ll);
  
 +        const double t =
 +          gsl_cdf_tdist_Qinv ((1 - cmd.n_cinterval[0] / 100.0) / 2.0,
 +                                      result->metrics[v].n - 1);
  
 -            if ( !prev || 0 != compare_values (prev, (*fs)->id[0],
 -                                        var_get_width (fctr->indep_var[0])))
 -              {
 -                struct string vstr;
 -                ds_init_empty (&vstr);
 -                var_append_value_name (fctr->indep_var[0],
 -                                    (*fs)->id[0], &vstr);
 -
 -                if ( count > 0 )
 -                  tab_hline (tbl, TAL_1, 1, n_cols - 1, row);
 -
 -                tab_text (tbl,
 -                          1, row,
 -                          TAB_LEFT | TAT_TITLE,
 -                          ds_cstr (&vstr)
 -                          );
 -
 -                ds_destroy (&vstr);
 -              }
 -
 -            prev = (*fs)->id[0];
 -
 -            if (fctr->indep_var[1] && count > 0 )
 -              tab_hline (tbl, TAL_1, 2, n_cols - 1, row);
 -
 -            if ( fctr->indep_var[1])
 -              {
 -                struct string vstr;
 -                ds_init_empty (&vstr);
 -                var_append_value_name (fctr->indep_var[1], (*fs)->id[1], &vstr);
 -
 -              tab_text (tbl, 2, row,
 -                        TAB_LEFT | TAT_TITLE,
 -                          ds_cstr (&vstr)
 -                        );
 -
 -                ds_destroy (&vstr);
 -              }
 -
 -            populate_extremes (tbl, heading_columns - 2,
 -                               row, n_extremities,
 -                               dependent_var[i],
 -                               & (*fs)->m[i]);
 -
 -            count++ ;
 -            fs++;
 +        if ( i > 0 || v > 0 )
 +          {
 +            const int left_col = (i == 0) ? 0 : 1;
 +            tab_hline (tbl, TAL_1, left_col, n_cols - 1,
 +                       heading_rows + row_var_start + i * DESCRIPTIVE_ROWS);
            }
 -      }
 -    }
 -
 -  tab_submit (tbl);
 -}
 -
 -
 -
 -/* Fill in the extremities table */
 -static void
 -populate_extremes (struct tab_table *t,
 -                 int col, int row, int n,
 -                 const struct variable *var,
 -                 const struct metrics *m)
 -{
 -  int extremity;
 -  int idx=0;
 -
 -  tab_text (t, col, row,
 -         TAB_RIGHT | TAT_TITLE ,
 -         _ ("Highest")
 -         );
 -
 -  tab_text (t, col, row + n ,
 -         TAB_RIGHT | TAT_TITLE ,
 -         _ ("Lowest")
 -         );
 -
 -
 -  tab_hline (t, TAL_1, col, col + 3, row + n );
  
 -  for (extremity = 0; extremity < n ; ++extremity )
 -    {
 -      /* Highest */
 -      tab_fixed (t, col + 1, row + extremity,
 -              TAB_RIGHT,
 -              extremity + 1, 8, 0);
 -
 -
 -      /* Lowest */
 -      tab_fixed (t, col + 1, row + extremity + n,
 -              TAB_RIGHT,
 -              extremity + 1, 8, 0);
 -
 -    }
 -
 -
 -  /* Lowest */
 -  for (idx = 0, extremity = 0; extremity < n && idx < m->n_data ; ++idx )
 -    {
 -      int j;
 -      const struct weighted_value *wv = m->wvp[idx];
 -      struct case_node *cn = wv->case_nos;
 -
 -
 -      for (j = 0 ; j < wv->w ; ++j  )
 -      {
 -        if ( extremity + j >= n )
 -          break ;
 +        if ( fctr->indep_var[0])
 +          {
 +            struct string vstr;
 +            ds_init_empty (&vstr);
 +            var_append_value_name (fctr->indep_var[0],
 +                                   &result->value[0], &vstr);
 +
 +            tab_text (tbl, 1,
 +                      heading_rows + row_var_start + i * DESCRIPTIVE_ROWS,
 +                      TAB_LEFT,
 +                      ds_cstr (&vstr)
 +                      );
  
 -        tab_value (t, col + 3, row + extremity + j  + n,
 -                   TAB_RIGHT,
 -                   &wv->v, var_get_print_format (var));
 +            ds_destroy (&vstr);
 +          }
  
 -        tab_fixed (t, col + 2, row + extremity + j  + n,
 -                    TAB_RIGHT,
 -                    cn->num, 10, 0);
  
 -        if ( cn->next )
 -          cn = cn->next;
 +        tab_text (tbl, n_cols - 4,
 +                  heading_rows + row_var_start + i * DESCRIPTIVE_ROWS,
 +                  TAB_LEFT,
 +                  _("Mean"));
 +
 +        tab_text_format (tbl, n_cols - 4,
 +                           heading_rows + row_var_start + 1 + i * DESCRIPTIVE_ROWS,
 +                           TAB_LEFT,
 +                           _("%g%% Confidence Interval for Mean"),
 +                           cmd.n_cinterval[0]);
 +
 +        tab_text (tbl, n_cols - 3,
 +                  heading_rows + row_var_start + 1 + i * DESCRIPTIVE_ROWS,
 +                  TAB_LEFT,
 +                  _("Lower Bound"));
  
 +        tab_text (tbl, n_cols - 3,
 +                  heading_rows + row_var_start + 2 + i * DESCRIPTIVE_ROWS,
 +                  TAB_LEFT,
 +                  _("Upper Bound"));
 +
 +        tab_text (tbl, n_cols - 4,
 +                    heading_rows + row_var_start + 3 + i * DESCRIPTIVE_ROWS,
 +                    TAB_LEFT, _("5% Trimmed Mean"));
 +
 +        tab_text (tbl, n_cols - 4,
 +                  heading_rows + row_var_start + 4 + i * DESCRIPTIVE_ROWS,
 +                  TAB_LEFT,
 +                  _("Median"));
 +
 +        tab_text (tbl, n_cols - 4,
 +                  heading_rows + row_var_start + 5 + i * DESCRIPTIVE_ROWS,
 +                  TAB_LEFT,
 +                  _("Variance"));
 +
 +        tab_text (tbl, n_cols - 4,
 +                  heading_rows + row_var_start + 6 + i * DESCRIPTIVE_ROWS,
 +                  TAB_LEFT,
 +                  _("Std. Deviation"));
 +
 +        tab_text (tbl, n_cols - 4,
 +                  heading_rows + row_var_start + 7 + i * DESCRIPTIVE_ROWS,
 +                  TAB_LEFT,
 +                  _("Minimum"));
 +
 +        tab_text (tbl, n_cols - 4,
 +                  heading_rows + row_var_start + 8 + i * DESCRIPTIVE_ROWS,
 +                  TAB_LEFT,
 +                  _("Maximum"));
 +
 +        tab_text (tbl, n_cols - 4,
 +                  heading_rows + row_var_start + 9 + i * DESCRIPTIVE_ROWS,
 +                  TAB_LEFT,
 +                  _("Range"));
 +
 +        tab_text (tbl, n_cols - 4,
 +                  heading_rows + row_var_start + 10 + i * DESCRIPTIVE_ROWS,
 +                  TAB_LEFT,
 +                  _("Interquartile Range"));
 +
 +
 +        tab_text (tbl, n_cols - 4,
 +                  heading_rows + row_var_start + 11 + i * DESCRIPTIVE_ROWS,
 +                  TAB_LEFT,
 +                  _("Skewness"));
 +
 +        tab_text (tbl, n_cols - 4,
 +                  heading_rows + row_var_start + 12 + i * DESCRIPTIVE_ROWS,
 +                  TAB_LEFT,
 +                  _("Kurtosis"));
 +
 +
 +        /* Now the statistics ... */
 +
 +        tab_double (tbl, n_cols - 2,
 +                  heading_rows + row_var_start + i * DESCRIPTIVE_ROWS,
 +                   TAB_CENTER,
 +                   result->metrics[v].mean,
 +                   NULL);
 +
 +        tab_double (tbl, n_cols - 1,
 +                  heading_rows + row_var_start + i * DESCRIPTIVE_ROWS,
 +                   TAB_CENTER,
 +                   result->metrics[v].se_mean,
 +                   NULL);
 +
 +
 +        tab_double (tbl, n_cols - 2,
 +                   heading_rows + row_var_start + 1 + i * DESCRIPTIVE_ROWS,
 +                   TAB_CENTER,
 +                   result->metrics[v].mean - t *
 +                    result->metrics[v].se_mean,
 +                   NULL);
 +
 +        tab_double (tbl, n_cols - 2,
 +                   heading_rows + row_var_start + 2 + i * DESCRIPTIVE_ROWS,
 +                   TAB_CENTER,
 +                   result->metrics[v].mean + t *
 +                    result->metrics[v].se_mean,
 +                   NULL);
 +
 +
 +        tab_double (tbl, n_cols - 2,
 +                   heading_rows + row_var_start + 3 + i * DESCRIPTIVE_ROWS,
 +                   TAB_CENTER,
 +                   trimmed_mean_calculate ((struct trimmed_mean *) result->metrics[v].trimmed_mean),
 +                   NULL);
 +
 +
 +        tab_double (tbl, n_cols - 2,
 +                   heading_rows + row_var_start + 4 + i * DESCRIPTIVE_ROWS,
 +                   TAB_CENTER,
 +                   percentile_calculate (result->metrics[v].quartiles[1], percentile_algorithm),
 +                   NULL);
 +
 +
 +        tab_double (tbl, n_cols - 2,
 +                   heading_rows + row_var_start + 5 + i * DESCRIPTIVE_ROWS,
 +                   TAB_CENTER,
 +                   result->metrics[v].variance,
 +                   NULL);
 +
 +        tab_double (tbl, n_cols - 2,
 +                   heading_rows + row_var_start + 6 + i * DESCRIPTIVE_ROWS,
 +                   TAB_CENTER,
 +                   sqrt (result->metrics[v].variance),
 +                   NULL);
 +
 +        tab_double (tbl, n_cols - 2,
 +                   heading_rows + row_var_start + 10 + i * DESCRIPTIVE_ROWS,
 +                   TAB_CENTER,
 +                   percentile_calculate (result->metrics[v].quartiles[2],
 +                                         percentile_algorithm) -
 +                   percentile_calculate (result->metrics[v].quartiles[0],
 +                                         percentile_algorithm),
 +                   NULL);
 +
 +
 +        tab_double (tbl, n_cols - 2,
 +                   heading_rows + row_var_start + 11 + i * DESCRIPTIVE_ROWS,
 +                   TAB_CENTER,
 +                   result->metrics[v].skewness,
 +                   NULL);
 +
 +        tab_double (tbl, n_cols - 2,
 +                   heading_rows + row_var_start + 12 + i * DESCRIPTIVE_ROWS,
 +                   TAB_CENTER,
 +                   result->metrics[v].kurtosis,
 +                   NULL);
 +
 +        tab_double (tbl, n_cols - 1,
 +                   heading_rows + row_var_start + 11 + i * DESCRIPTIVE_ROWS,
 +                   TAB_CENTER,
 +                   calc_seskew (result->metrics[v].n),
 +                   NULL);
 +
 +        tab_double (tbl, n_cols - 1,
 +                   heading_rows + row_var_start + 12 + i * DESCRIPTIVE_ROWS,
 +                   TAB_CENTER,
 +                   calc_sekurt (result->metrics[v].n),
 +                   NULL);
 +
 +        {
 +          struct extremum *minimum, *maximum ;
 +
 +          struct ll *max_ll = ll_head (extrema_list (result->metrics[v].maxima));
 +          struct ll *min_ll = ll_head (extrema_list (result->metrics[v].minima));
 +
 +          maximum = ll_data (max_ll, struct extremum, ll);
 +          minimum = ll_data (min_ll, struct extremum, ll);
 +
 +          tab_double (tbl, n_cols - 2,
 +                     heading_rows + row_var_start + 7 + i * DESCRIPTIVE_ROWS,
 +                     TAB_CENTER,
 +                     minimum->value,
 +                     NULL);
 +
 +          tab_double (tbl, n_cols - 2,
 +                     heading_rows + row_var_start + 8 + i * DESCRIPTIVE_ROWS,
 +                     TAB_CENTER,
 +                     maximum->value,
 +                     NULL);
 +
 +          tab_double (tbl, n_cols - 2,
 +                     heading_rows + row_var_start + 9 + i * DESCRIPTIVE_ROWS,
 +                     TAB_CENTER,
 +                     maximum->value - minimum->value,
 +                     NULL);
 +        }
        }
 -
 -      extremity +=  wv->w ;
      }
  
 +  tab_vline (tbl, TAL_2, heading_columns, 0, n_rows - 1);
  
 -  /* Highest */
 -  for (idx = m->n_data - 1, extremity = 0; extremity < n && idx >= 0; --idx )
 -    {
 -      int j;
 -      const struct weighted_value *wv = m->wvp[idx];
 -      struct case_node *cn = wv->case_nos;
 -
 -      for (j = 0 ; j < wv->w ; ++j  )
 -      {
 -        if ( extremity + j >= n )
 -          break ;
 -
 -        tab_value (t, col + 3, row + extremity + j,
 -                   TAB_RIGHT,
 -                   &wv->v, var_get_print_format (var));
 -
 -        tab_fixed (t, col + 2, row + extremity + j,
 -                  TAB_RIGHT,
 -                    cn->num, 10, 0);
 +  tab_title (tbl, _("Descriptives"));
  
 -        if ( cn->next )
 -          cn = cn->next;
 +  tab_text (tbl, n_cols - 2, 0, TAB_CENTER | TAT_TITLE,
 +          _("Statistic"));
  
 -      }
 +  tab_text (tbl, n_cols - 1, 0, TAB_CENTER | TAT_TITLE,
 +          _("Std. Error"));
  
 -      extremity +=  wv->w ;
 -    }
 +  tab_submit (tbl);
  }
  
  
 -/* Show the descriptives table */
 -void
 -show_descriptives (const struct variable **dependent_var,
 -                int n_dep_var,
 -                struct factor *fctr)
 +
 +static void
 +show_extremes (const struct variable **dependent_var,
 +             int n_dep_var,
 +             const struct xfactor *fctr)
  {
 -  int i;
 -  int heading_columns ;
 +  int v;
 +  int heading_columns = 3;
    int n_cols;
 -  const int n_stat_rows = 13;
 -
    const int heading_rows = 1;
 -
    struct tab_table *tbl;
  
 -  int n_factors = 1;
    int n_rows ;
 +  n_rows = n_dep_var;
  
 -  if ( fctr )
 +  assert (fctr);
 +
 +  if ( fctr->indep_var[0] )
      {
        heading_columns = 4;
 -      n_factors = hsh_count (fctr->fstats);
 -
 -      n_rows = n_dep_var * n_stat_rows * n_factors;
  
        if ( fctr->indep_var[1] )
 -      heading_columns = 5;
 -    }
 -  else
 -    {
 -      heading_columns = 3;
 -      n_rows = n_dep_var * n_stat_rows;
 +      {
 +        heading_columns = 5;
 +      }
      }
  
 +  n_rows *= ll_count (&fctr->result_list) * cmd.st_n * 2;
    n_rows += heading_rows;
  
    n_cols = heading_columns + 2;
  
 -
    tbl = tab_create (n_cols, n_rows, 0);
 +  tab_headers (tbl, heading_columns, 0, heading_rows, 0);
  
 -  tab_headers (tbl, heading_columns + 1, 0, heading_rows, 0);
 -
 -  tab_dim (tbl, tab_natural_dimensions);
 +  tab_dim (tbl, tab_natural_dimensions, NULL);
  
 -  /* Outline the box and have no internal lines*/
 +  /* Outline the box */
    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_1, heading_columns, 0, n_rows - 1);
 -  tab_vline (tbl, TAL_2, n_cols - 2, 0, n_rows - 1);
 +  tab_hline (tbl, TAL_2, 0, n_cols - 1, heading_rows );
 +  tab_hline (tbl, TAL_2, 1, n_cols - 1, heading_rows );
    tab_vline (tbl, TAL_1, n_cols - 1, 0, n_rows - 1);
  
 -  tab_text (tbl, n_cols - 2, 0, TAB_CENTER | TAT_TITLE, _ ("Statistic"));
 -  tab_text (tbl, n_cols - 1, 0, TAB_CENTER | TAT_TITLE, _ ("Std. Error"));
 +  if ( fctr->indep_var[0])
 +    tab_text (tbl, 1, 0, TAT_TITLE, var_to_string (fctr->indep_var[0]));
  
 -  tab_title (tbl, _ ("Descriptives"));
 +  if ( fctr->indep_var[1])
 +    tab_text (tbl, 2, 0, TAT_TITLE, var_to_string (fctr->indep_var[1]));
  
 -
 -  for ( i = 0 ; i < n_dep_var ; ++i )
 +  for (v = 0 ; v < n_dep_var ; ++v )
      {
 -      const int row = heading_rows + i * n_stat_rows * n_factors ;
 -
 -      if ( i > 0 )
 -      tab_hline (tbl, TAL_1, 0, n_cols - 1, row );
 +      struct ll *ll;
 +      int i = 0;
 +      const int row_var_start = v * cmd.st_n * 2 * ll_count(&fctr->result_list);
  
 -      tab_text (tbl, 0,
 -              i * n_stat_rows * n_factors  + heading_rows,
 +      tab_text (tbl,
 +              0,
 +              heading_rows + row_var_start,
                TAB_LEFT | TAT_TITLE,
 -              var_to_string (dependent_var[i])
 +              var_to_string (dependent_var[v])
                );
  
 -
 -      if ( fctr  )
 +      for (ll = ll_head (&fctr->result_list);
 +         ll != ll_null (&fctr->result_list); i++, ll = ll_next (ll))
        {
 -        const union value *prev = NULL;
 +        int e ;
 +        struct ll *min_ll;
 +        struct ll *max_ll;
 +        const int row_result_start = i * cmd.st_n * 2;
  
 -        struct factor_statistics **fs = fctr->fs;
 -        int count = 0;
 +        const struct factor_result *result =
 +          ll_data (ll, struct factor_result, ll);
  
 -        tab_text (tbl, 1, heading_rows - 1, TAB_CENTER | TAT_TITLE,
 -                  var_to_string (fctr->indep_var[0]));
 +        if (i > 0 || v > 0)
 +          tab_hline (tbl, TAL_1, 1, n_cols - 1,
 +                     heading_rows + row_var_start + row_result_start);
  
 +        tab_hline (tbl, TAL_1, heading_columns - 2, n_cols - 1,
 +                   heading_rows + row_var_start + row_result_start + cmd.st_n);
  
 -        if ( fctr->indep_var[1])
 -          tab_text (tbl, 2, heading_rows - 1, TAB_CENTER | TAT_TITLE,
 -                    var_to_string (fctr->indep_var[1]));
 -
 -        while ( *fs )
 +        for ( e = 1; e <= cmd.st_n; ++e )
            {
 -            const int row = heading_rows + n_stat_rows  *
 -               ( ( i  * n_factors  ) +  count );
 -
 -
 -            if ( !prev || 0 != compare_values (prev, (*fs)->id[0],
 -                                        var_get_width (fctr->indep_var[0])))
 -              {
 -                struct string vstr;
 -                ds_init_empty (&vstr);
 -                var_append_value_name (fctr->indep_var[0],
 -                                    (*fs)->id[0], &vstr);
 +            tab_text_format (tbl, n_cols - 3,
 +                               heading_rows + row_var_start + row_result_start + e - 1,
 +                               TAB_RIGHT,
 +                               "%d", e);
 +
 +            tab_text_format (tbl, n_cols - 3,
 +                               heading_rows + row_var_start + row_result_start + cmd.st_n + e - 1,
 +                               TAB_RIGHT,
 +                               "%d", e);
 +          }
  
 -                if ( count > 0 )
 -                  tab_hline (tbl, TAL_1, 1, n_cols - 1, row);
  
 -                tab_text (tbl,
 -                          1, row,
 -                          TAB_LEFT | TAT_TITLE,
 -                          ds_cstr (&vstr)
 -                          );
 +        min_ll = ll_head (extrema_list (result->metrics[v].minima));
 +        for (e = 0; e < cmd.st_n;)
 +          {
 +            struct extremum *minimum = ll_data (min_ll, struct extremum, ll);
 +            double weight = minimum->weight;
  
 -                ds_destroy (&vstr);
 +            while (weight-- > 0 && e < cmd.st_n)
 +              {
 +                tab_double (tbl, n_cols - 1,
 +                           heading_rows + row_var_start + row_result_start + cmd.st_n + e,
 +                           TAB_RIGHT,
 +                           minimum->value,
 +                           NULL);
 +
 +
 +                tab_fixed (tbl, n_cols - 2,
 +                           heading_rows + row_var_start +
 +                           row_result_start + cmd.st_n + e,
 +                           TAB_RIGHT,
 +                           minimum->location,
 +                           10, 0);
 +                ++e;
                }
  
 -            prev = (*fs)->id[0];
 +            min_ll = ll_next (min_ll);
 +          }
  
 -            if (fctr->indep_var[1] && count > 0 )
 -              tab_hline (tbl, TAL_1, 2, n_cols - 1, row);
 +        max_ll = ll_head (extrema_list (result->metrics[v].maxima));
 +        for (e = 0; e < cmd.st_n;)
 +          {
 +            struct extremum *maximum = ll_data (max_ll, struct extremum, ll);
 +            double weight = maximum->weight;
  
 -            if ( fctr->indep_var[1])
 +            while (weight-- > 0 && e < cmd.st_n)
                {
 -                struct string vstr;
 -                ds_init_empty (&vstr);
 -                var_append_value_name (fctr->indep_var[1], (*fs)->id[1], &vstr);
 -
 -              tab_text (tbl, 2, row,
 -                        TAB_LEFT | TAT_TITLE,
 -                          ds_cstr (&vstr)
 -                        );
 -
 -                ds_destroy (&vstr);
 +                tab_double (tbl, n_cols - 1,
 +                           heading_rows + row_var_start +
 +                            row_result_start + e,
 +                           TAB_RIGHT,
 +                           maximum->value,
 +                           NULL);
 +
 +
 +                tab_fixed (tbl, n_cols - 2,
 +                           heading_rows + row_var_start +
 +                           row_result_start + e,
 +                           TAB_RIGHT,
 +                           maximum->location,
 +                           10, 0);
 +                ++e;
                }
  
 -            populate_descriptives (tbl, heading_columns - 2,
 -                                   row,
 -                                   dependent_var[i],
 -                                   & (*fs)->m[i]);
 -
 -            count++ ;
 -            fs++;
 +            max_ll = ll_next (max_ll);
            }
  
 -      }
 -
 -      else
 -      {
 -
 -        populate_descriptives (tbl, heading_columns - 2,
 -                               i * n_stat_rows * n_factors  + heading_rows,
 -                               dependent_var[i],
 -                               &totals[i]);
 -      }
 -    }
 -
 -  tab_submit (tbl);
 -}
 -
 -
 -/* Fill in the descriptives data */
 -static void
 -populate_descriptives (struct tab_table *tbl, int col, int row,
 -                     const struct variable *var,
 -                     const struct metrics *m)
 -{
 -  const double t = gsl_cdf_tdist_Qinv ((1 - cmd.n_cinterval[0] / 100.0)/2.0,
 -                                     m->n -1);
 -
 -  tab_text (tbl, col,
 -          row,
 -          TAB_LEFT | TAT_TITLE,
 -          _ ("Mean"));
 -
 -  tab_double (tbl, col + 2,
 -            row,
 -            TAB_CENTER,
 -            m->mean,
 -            NULL);
 -
 -  tab_double (tbl, col + 3,
 -            row,
 -            TAB_CENTER,
 -            m->se_mean,
 -            NULL);
 -
 -
 -  tab_text (tbl, col,
 -          row + 1,
 -          TAB_LEFT | TAT_TITLE | TAT_PRINTF,
 -          _ ("%g%% Confidence Interval for Mean"), cmd.n_cinterval[0]);
 -
 -
 -  tab_text (tbl, col + 1,
 -          row  + 1,
 -          TAB_LEFT | TAT_TITLE,
 -          _ ("Lower Bound"));
 -
 -  tab_double (tbl, col + 2,
 -            row + 1,
 -            TAB_CENTER,
 -            m->mean - t * m->se_mean,
 -            NULL);
 -
 -  tab_text (tbl, col + 1,
 -          row + 2,
 -          TAB_LEFT | TAT_TITLE,
 -          _ ("Upper Bound"));
 -
 -
 -  tab_double (tbl, col + 2,
 -            row + 2,
 -            TAB_CENTER,
 -            m->mean + t * m->se_mean,
 -            NULL);
 -
 -  tab_text (tbl, col,
 -          row + 3,
 -          TAB_LEFT | TAT_TITLE | TAT_PRINTF,
 -          _ ("5%% Trimmed Mean"));
 -
 -  tab_double (tbl, col + 2,
 -            row + 3,
 -            TAB_CENTER,
 -            m->trimmed_mean,
 -            NULL);
 -
 -  tab_text (tbl, col,
 -          row + 4,
 -          TAB_LEFT | TAT_TITLE,
 -          _ ("Median"));
 -
 -  {
 -    struct percentile *p;
 -    double d = 50;
 -
 -    p = hsh_find (m->ptile_hash, &d);
 -
 -    assert (p);
 -
 -
 -    tab_double (tbl, col + 2,
 -              row + 4,
 -              TAB_CENTER,
 -              p->v,
 -              NULL);
 -  }
 -
 -
 -  tab_text (tbl, col,
 -          row + 5,
 -          TAB_LEFT | TAT_TITLE,
 -          _ ("Variance"));
 -
 -  tab_double (tbl, col + 2,
 -            row + 5,
 -            TAB_CENTER,
 -            m->var,
 -            NULL);
 -
 -
 -  tab_text (tbl, col,
 -          row + 6,
 -          TAB_LEFT | TAT_TITLE,
 -          _ ("Std. Deviation"));
 -
 -
 -  tab_double (tbl, col + 2,
 -            row + 6,
 -            TAB_CENTER,
 -            m->stddev,
 -            NULL);
 -
 -
 -  tab_text (tbl, col,
 -          row + 7,
 -          TAB_LEFT | TAT_TITLE,
 -          _ ("Minimum"));
 -
 -  tab_double (tbl, col + 2,
 -            row + 7,
 -            TAB_CENTER,
 -            m->min, var_get_print_format (var));
 -
 -  tab_text (tbl, col,
 -          row + 8,
 -          TAB_LEFT | TAT_TITLE,
 -          _ ("Maximum"));
 -
 -  tab_double (tbl, col + 2,
 -            row + 8,
 -            TAB_CENTER,
 -            m->max, var_get_print_format (var));
 -
 -  tab_text (tbl, col,
 -          row + 9,
 -          TAB_LEFT | TAT_TITLE,
 -          _ ("Range"));
 -
 -
 -  tab_double (tbl, col + 2,
 -            row + 9,
 -            TAB_CENTER,
 -            m->max - m->min,
 -            NULL);
 -
 -  tab_text (tbl, col,
 -          row + 10,
 -          TAB_LEFT | TAT_TITLE,
 -          _ ("Interquartile Range"));
 -
 -  {
 -    struct percentile *p1;
 -    struct percentile *p2;
 -
 -    double d = 75;
 -    p1 = hsh_find (m->ptile_hash, &d);
 -
 -    d = 25;
 -    p2 = hsh_find (m->ptile_hash, &d);
 -
 -    assert (p1);
 -    assert (p2);
 -
 -    tab_double (tbl, col + 2,
 -              row + 10,
 -              TAB_CENTER,
 -              p1->v - p2->v,
 -              NULL);
 -  }
 -
 -  tab_text (tbl, col,
 -          row + 11,
 -          TAB_LEFT | TAT_TITLE,
 -          _ ("Skewness"));
 -
 -
 -  tab_double (tbl, col + 2,
 -            row + 11,
 -            TAB_CENTER,
 -            m->skewness,
 -            NULL);
 -
 -  /* stderr of skewness */
 -  tab_double (tbl, col + 3,
 -            row + 11,
 -            TAB_CENTER,
 -            calc_seskew (m->n),
 -            NULL);
 -
 -  tab_text (tbl, col,
 -          row + 12,
 -          TAB_LEFT | TAT_TITLE,
 -          _ ("Kurtosis"));
 -
 -
 -  tab_double (tbl, col + 2,
 -            row + 12,
 -            TAB_CENTER,
 -            m->kurtosis,
 -            NULL);
 -
 -  /* stderr of kurtosis */
 -  tab_double (tbl, col + 3,
 -            row + 12,
 -            TAB_CENTER,
 -            calc_sekurt (m->n),
 -            NULL);
 -}
 -
 -
 -
 -void
 -box_plot_variables (const struct factor *fctr,
 -                 const struct variable **vars, int n_vars,
 -                 const struct variable *id)
 -{
 -
 -  int i;
 -  struct factor_statistics **fs ;
 -
 -  if ( ! fctr )
 -    {
 -      box_plot_group (fctr, vars, n_vars, id);
 -      return;
 -    }
 -
 -  for ( fs = fctr->fs ; *fs ; ++fs )
 -    {
 -      struct string str;
 -      double y_min = DBL_MAX;
 -      double y_max = -DBL_MAX;
 -      struct chart *ch = chart_create ();
 -      if (ch == NULL)
 -        break;
 -
 -      ds_init_empty (&str);
 -      factor_to_string (fctr, *fs, 0, &str );
 -
 -      chart_write_title (ch, "%s", ds_cstr (&str));
 -
 -      for ( i = 0 ; i < n_vars ; ++i )
 -      {
 -        y_max = MAX (y_max, (*fs)->m[i].max);
 -        y_min = MIN (y_min, (*fs)->m[i].min);
 -      }
 -
 -      boxplot_draw_yscale (ch, y_max, y_min);
 -
 -      for ( i = 0 ; i < n_vars ; ++i )
 -      {
 -
 -        const double box_width = (ch->data_right - ch->data_left)
 -          / (n_vars * 2.0 ) ;
 -
 -        const double box_centre = ( i * 2 + 1) * box_width
 -          + ch->data_left;
 -
 -        boxplot_draw_boxplot (ch,
 -                             box_centre, box_width,
 -                             & (*fs)->m[i],
 -                             var_to_string (vars[i]));
  
 -
 -      }
 -
 -      chart_submit (ch);
 -      ds_destroy (&str);
 -    }
 -}
 -
 -
 -
 -/* Do a box plot, grouping all factors into one plot ;
 -   each dependent variable has its own plot.
 -*/
 -void
 -box_plot_group (const struct factor *fctr,
 -             const struct variable **vars,
 -             int n_vars,
 -             const struct variable *id UNUSED)
 -{
 -
 -  int i;
 -
 -  for ( i = 0 ; i < n_vars ; ++i )
 -    {
 -      struct factor_statistics **fs ;
 -      struct chart *ch;
 -
 -      ch = chart_create ();
 -      if (ch == NULL)
 -        break;
 -
 -      boxplot_draw_yscale (ch, totals[i].max, totals[i].min);
 -
 -      if ( fctr )
 -      {
 -        int n_factors = 0;
 -        int f=0;
 -        for ( fs = fctr->fs ; *fs ; ++fs )
 -          ++n_factors;
 -
 -        chart_write_title (ch, _ ("Boxplot of %s vs. %s"),
 -                          var_to_string (vars[i]), var_to_string (fctr->indep_var[0]) );
 -
 -        for ( fs = fctr->fs ; *fs ; ++fs )
 +        if ( fctr->indep_var[0])
            {
 -            struct string str;
 -            const double box_width = (ch->data_right - ch->data_left)
 -              / (n_factors * 2.0 ) ;
 -
 -            const double box_centre = ( f++ * 2 + 1) * box_width
 -              + ch->data_left;
 -
 -            ds_init_empty (&str);
 -            factor_to_string_concise (fctr, *fs, &str);
 +            struct string vstr;
 +            ds_init_empty (&vstr);
 +            var_append_value_name (fctr->indep_var[0],
 +                                   &result->value[0], &vstr);
 +
 +            tab_text (tbl, 1,
 +                      heading_rows + row_var_start + row_result_start,
 +                      TAB_LEFT,
 +                      ds_cstr (&vstr)
 +                      );
  
 -            boxplot_draw_boxplot (ch,
 -                                 box_centre, box_width,
 -                                 & (*fs)->m[i],
 -                                 ds_cstr (&str));
 -              ds_destroy (&str);
 +            ds_destroy (&vstr);
            }
 -      }
 -      else if ( ch )
 -      {
 -        const double box_width = (ch->data_right - ch->data_left) / 3.0;
 -        const double box_centre = (ch->data_right + ch->data_left) / 2.0;
  
 -        chart_write_title (ch, _ ("Boxplot"));
  
 -        boxplot_draw_boxplot (ch,
 -                             box_centre,    box_width,
 -                             &totals[i],
 -                             var_to_string (vars[i]) );
 +        tab_text (tbl, n_cols - 4,
 +                  heading_rows + row_var_start + row_result_start,
 +                  TAB_RIGHT,
 +                  _("Highest"));
  
 +        tab_text (tbl, n_cols - 4,
 +                  heading_rows + row_var_start + row_result_start + cmd.st_n,
 +                  TAB_RIGHT,
 +                  _("Lowest"));
        }
 -
 -      chart_submit (ch);
      }
 -}
 -
 -
 -/* Plot the normal and detrended normal plots for m
 -   Label the plots with factorname */
 -void
 -np_plot (const struct metrics *m, const char *factorname)
 -{
 -  int i;
 -  double yfirst=0, ylast=0;
 -
 -  /* Normal Plot */
 -  struct chart *np_chart;
 -
 -  /* Detrended Normal Plot */
 -  struct chart *dnp_chart;
 -
 -  /* The slope and intercept of the ideal normal probability line */
 -  const double slope = 1.0 / m->stddev;
 -  const double intercept = - m->mean / m->stddev;
 -
 -  /* Cowardly refuse to plot an empty data set */
 -  if ( m->n_data == 0 )
 -    return ;
 -
 -  np_chart = chart_create ();
 -  dnp_chart = chart_create ();
 -
 -  if ( !np_chart || ! dnp_chart )
 -    return ;
 -
 -  chart_write_title (np_chart, _ ("Normal Q-Q Plot of %s"), factorname);
 -  chart_write_xlabel (np_chart, _ ("Observed Value"));
 -  chart_write_ylabel (np_chart, _ ("Expected Normal"));
 -
 -
 -  chart_write_title (dnp_chart, _ ("Detrended Normal Q-Q Plot of %s"),
 -                  factorname);
 -  chart_write_xlabel (dnp_chart, _ ("Observed Value"));
 -  chart_write_ylabel (dnp_chart, _ ("Dev from Normal"));
 -
 -  yfirst = gsl_cdf_ugaussian_Pinv (m->wvp[0]->rank / ( m->n + 1));
 -  ylast =  gsl_cdf_ugaussian_Pinv (m->wvp[m->n_data-1]->rank / ( m->n + 1));
 -
 -
 -  {
 -    /* Need to make sure that both the scatter plot and the ideal fit into the
 -       plot */
 -    double x_lower = MIN (m->min, (yfirst - intercept) / slope) ;
 -    double x_upper = MAX (m->max, (ylast  - intercept) / slope) ;
 -    double slack = (x_upper - x_lower)  * 0.05 ;
 -
 -    chart_write_xscale (np_chart, x_lower - slack, x_upper + slack, 5);
 -
 -    chart_write_xscale (dnp_chart, m->min, m->max, 5);
 -
 -  }
  
 -  chart_write_yscale (np_chart, yfirst, ylast, 5);
 -
 -  {
 -    /* We have to cache the detrended data, beacause we need to
 -       find its limits before we can plot it */
 -    double *d_data = xnmalloc (m->n_data, sizeof *d_data);
 -    double d_max = -DBL_MAX;
 -    double d_min = DBL_MAX;
 -    for ( i = 0 ; i < m->n_data; ++i )
 -      {
 -      const double ns = gsl_cdf_ugaussian_Pinv (m->wvp[i]->rank / ( m->n + 1));
 +  tab_vline (tbl, TAL_2, heading_columns, 0, n_rows - 1);
  
 -      chart_datum (np_chart, 0, m->wvp[i]->v.f, ns);
  
 -      d_data[i] = (m->wvp[i]->v.f - m->mean) / m->stddev  - ns;
 +  tab_title (tbl, _("Extreme Values"));
  
 -      if ( d_data[i] < d_min ) d_min = d_data[i];
 -      if ( d_data[i] > d_max ) d_max = d_data[i];
 -      }
 -    chart_write_yscale (dnp_chart, d_min, d_max, 5);
  
 -    for ( i = 0 ; i < m->n_data; ++i )
 -      chart_datum (dnp_chart, 0, m->wvp[i]->v.f, d_data[i]);
 +  tab_text (tbl, n_cols - 2, 0, TAB_CENTER | TAT_TITLE,
 +          _("Case Number"));
  
 -    free (d_data);
 -  }
  
 -  chart_line (np_chart, slope, intercept, yfirst, ylast , CHART_DIM_Y);
 -  chart_line (dnp_chart, 0, 0, m->min, m->max , CHART_DIM_X);
 +  tab_text (tbl, n_cols - 1, 0, TAB_CENTER | TAT_TITLE,
 +          _("Value"));
  
 -  chart_submit (np_chart);
 -  chart_submit (dnp_chart);
 +  tab_submit (tbl);
  }
  
 +#define PERCENTILE_ROWS 2
  
 -
 -
 -/* Show the percentiles */
 -void
 +static void
  show_percentiles (const struct variable **dependent_var,
 -               int n_dep_var,
 -               struct factor *fctr)
 +                int n_dep_var,
 +                const struct xfactor *fctr)
  {
 -  struct tab_table *tbl;
    int i;
 +  int v;
 +  int heading_columns = 2;
 +  int n_cols;
 +  const int n_percentiles = subc_list_double_count (&percentile_list);
 +  const int heading_rows = 2;
 +  struct tab_table *tbl;
  
 -  int n_cols, n_rows;
 -  int n_factors;
 -
 -  struct hsh_table *ptiles ;
 -
 -  int n_heading_columns;
 -  const int n_heading_rows = 2;
 -  const int n_stat_rows = 2;
 +  int n_rows ;
 +  n_rows = n_dep_var;
  
 -  int n_ptiles ;
 +  assert (fctr);
  
 -  if ( fctr )
 +  if ( fctr->indep_var[0] )
      {
 -      struct factor_statistics **fs = fctr->fs ;
 -      n_heading_columns = 3;
 -      n_factors = hsh_count (fctr->fstats);
 -
 -      ptiles = (*fs)->m[0].ptile_hash;
 +      heading_columns = 3;
  
        if ( fctr->indep_var[1] )
 -      n_heading_columns = 4;
 -    }
 -  else
 -    {
 -      n_factors = 1;
 -      n_heading_columns = 2;
 -
 -      ptiles = totals[0].ptile_hash;
 +      {
 +        heading_columns = 4;
 +      }
      }
  
 -  n_ptiles = hsh_count (ptiles);
 -
 -  n_rows = n_heading_rows + n_dep_var * n_stat_rows * n_factors;
 +  n_rows *= ll_count (&fctr->result_list) * PERCENTILE_ROWS;
 +  n_rows += heading_rows;
  
 -  n_cols = n_heading_columns + n_ptiles ;
 +  n_cols = heading_columns + n_percentiles;
  
    tbl = tab_create (n_cols, n_rows, 0);
 +  tab_headers (tbl, heading_columns, 0, heading_rows, 0);
  
 -  tab_headers (tbl, n_heading_columns + 1, 0, n_heading_rows, 0);
 -
 -  tab_dim (tbl, tab_natural_dimensions);
 +  tab_dim (tbl, tab_natural_dimensions, NULL);
  
 -  /* Outline the box and have no internal lines*/
 +  /* Outline the box */
    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, n_heading_rows );
 -
 -  tab_vline (tbl, TAL_2, n_heading_columns, 0, n_rows - 1);
 -
 -
 -  tab_title (tbl, _ ("Percentiles"));
 -
 -
 -  tab_hline (tbl, TAL_1, n_heading_columns, n_cols - 1, 1 );
 -
 -
 -  tab_box (tbl,
 -         -1, -1,
 -         -1, TAL_1,
 -         0, n_heading_rows,
 -         n_heading_columns - 1, n_rows - 1);
 -
 -
 -  tab_box (tbl,
 -         -1, -1,
 -         -1, TAL_1,
 -         n_heading_columns, n_heading_rows - 1,
 -         n_cols - 1, n_rows - 1);
 -
 -  tab_joint_text (tbl, n_heading_columns + 1, 0,
 -               n_cols - 1 , 0,
 -               TAB_CENTER | TAT_TITLE ,
 -               _ ("Percentiles"));
  
 +  tab_hline (tbl, TAL_2, 0, n_cols - 1, heading_rows );
 +  tab_hline (tbl, TAL_2, 1, n_cols - 1, heading_rows );
  
 -  {
 -    /* Put in the percentile break points as headings */
 +  if ( fctr->indep_var[0])
 +    tab_text (tbl, 1, 1, TAT_TITLE, var_to_string (fctr->indep_var[0]));
  
 -    struct percentile **p = (struct percentile **) hsh_sort (ptiles);
 +  if ( fctr->indep_var[1])
 +    tab_text (tbl, 2, 1, TAT_TITLE, var_to_string (fctr->indep_var[1]));
  
 -    i = 0;
 -    while ( (*p)  )
 -      {
 -      tab_fixed (tbl, n_heading_columns + i++ , 1,
 -                  TAB_CENTER,
 -                  (*p)->p,
 -                  8, 0);
 -      p++;
 -      }
 -
 -  }
 -
 -  for ( i = 0 ; i < n_dep_var ; ++i )
 +  for (v = 0 ; v < n_dep_var ; ++v )
      {
 -      const int n_stat_rows = 2;
 -      const int row = n_heading_rows + i * n_stat_rows * n_factors ;
 +      double hinges[3];
 +      struct ll *ll;
 +      int i = 0;
  
 -      if ( i > 0 )
 -      tab_hline (tbl, TAL_1, 0, n_cols - 1, row );
 +      const int row_var_start =
 +      v * PERCENTILE_ROWS * ll_count(&fctr->result_list);
  
 -      tab_text (tbl, 0,
 -              i * n_stat_rows * n_factors  + n_heading_rows,
 +      tab_text (tbl,
 +              0,
 +              heading_rows + row_var_start,
                TAB_LEFT | TAT_TITLE,
 -              var_to_string (dependent_var[i])
 +              var_to_string (dependent_var[v])
                );
  
 -      if ( fctr  )
 +      for (ll = ll_head (&fctr->result_list);
 +         ll != ll_null (&fctr->result_list); i++, ll = ll_next (ll))
        {
 -        const union value *prev  = NULL ;
 -        struct factor_statistics **fs = fctr->fs;
 -        int count = 0;
 +        int j;
 +        const struct factor_result *result =
 +          ll_data (ll, struct factor_result, ll);
  
 -        tab_text (tbl, 1, n_heading_rows - 1,
 -                  TAB_CENTER | TAT_TITLE,
 -                  var_to_string (fctr->indep_var[0]));
 +        if ( i > 0 || v > 0 )
 +          {
 +            const int left_col = (i == 0) ? 0 : 1;
 +            tab_hline (tbl, TAL_1, left_col, n_cols - 1,
 +                       heading_rows + row_var_start + i * PERCENTILE_ROWS);
 +          }
  
 +        if ( fctr->indep_var[0])
 +          {
 +            struct string vstr;
 +            ds_init_empty (&vstr);
 +            var_append_value_name (fctr->indep_var[0],
 +                                   &result->value[0], &vstr);
 +
 +            tab_text (tbl, 1,
 +                      heading_rows + row_var_start + i * PERCENTILE_ROWS,
 +                      TAB_LEFT,
 +                      ds_cstr (&vstr)
 +                      );
  
 -        if ( fctr->indep_var[1])
 -          tab_text (tbl, 2, n_heading_rows - 1, TAB_CENTER | TAT_TITLE,
 -                    var_to_string (fctr->indep_var[1]));
 +            ds_destroy (&vstr);
 +          }
  
 -        while ( *fs )
 -          {
 -            const int row = n_heading_rows + n_stat_rows  *
 -               ( ( i  * n_factors  ) +  count );
  
 +        tab_text (tbl, n_cols - n_percentiles - 1,
 +                  heading_rows + row_var_start + i * PERCENTILE_ROWS,
 +                  TAB_LEFT,
 +                  ptile_alg_desc [percentile_algorithm]);
  
 -            if ( !prev || 0 != compare_values (prev, (*fs)->id[0],
 -                                        var_get_width (fctr->indep_var[0])))
 -              {
 -                struct string vstr;
 -                ds_init_empty (&vstr);
 -                var_append_value_name (fctr->indep_var[0],
 -                                    (*fs)->id[0], &vstr);
  
 +        tab_text (tbl, n_cols - n_percentiles - 1,
 +                  heading_rows + row_var_start + 1 + i * PERCENTILE_ROWS,
 +                  TAB_LEFT,
 +                  _("Tukey's Hinges"));
  
 -                if ( count > 0 )
 -                  tab_hline (tbl, TAL_1, 1, n_cols - 1, row);
  
 -                tab_text (tbl,
 -                          1, row,
 -                          TAB_LEFT | TAT_TITLE,
 -                          ds_cstr (&vstr)
 -                          );
 +        tab_vline (tbl, TAL_1, n_cols - n_percentiles -1, heading_rows, n_rows - 1);
  
 -                ds_destroy (&vstr);
 -              }
 +        tukey_hinges_calculate ((struct tukey_hinges *) result->metrics[v].tukey_hinges,
 +                                hinges);
  
 -            prev = (*fs)->id[0];
 +        for (j = 0; j < n_percentiles; ++j)
 +          {
 +            double hinge = SYSMIS;
 +            tab_double (tbl, n_cols - n_percentiles + j,
 +                       heading_rows + row_var_start + i * PERCENTILE_ROWS,
 +                       TAB_CENTER,
 +                       percentile_calculate (result->metrics[v].ptl[j],
 +                                             percentile_algorithm),
 +                       NULL
 +                       );
 +
 +            if ( result->metrics[v].ptl[j]->ptile == 0.5)
 +              hinge = hinges[1];
 +            else if ( result->metrics[v].ptl[j]->ptile == 0.25)
 +              hinge = hinges[0];
 +            else if ( result->metrics[v].ptl[j]->ptile == 0.75)
 +              hinge = hinges[2];
 +
 +            if ( hinge != SYSMIS)
 +              tab_double (tbl, n_cols - n_percentiles + j,
 +                         heading_rows + row_var_start + 1 + i * PERCENTILE_ROWS,
 +                         TAB_CENTER,
 +                         hinge,
 +                         NULL
 +                         );
  
 -            if (fctr->indep_var[1] && count > 0 )
 -              tab_hline (tbl, TAL_1, 2, n_cols - 1, row);
 +          }
 +      }
 +    }
  
 -            if ( fctr->indep_var[1])
 -              {
 -                struct string vstr;
 -                ds_init_empty (&vstr);
 -                var_append_value_name (fctr->indep_var[1], (*fs)->id[1], &vstr);
 +  tab_vline (tbl, TAL_2, heading_columns, 0, n_rows - 1);
  
 -              tab_text (tbl, 2, row,
 -                        TAB_LEFT | TAT_TITLE,
 -                          ds_cstr (&vstr)
 -                        );
 +  tab_title (tbl, _("Percentiles"));
  
 -                ds_destroy (&vstr);
 -              }
  
 +  for (i = 0 ; i < n_percentiles; ++i )
 +    {
 +      tab_text_format (tbl, n_cols - n_percentiles + i, 1,
 +                       TAB_CENTER | TAT_TITLE,
 +                       _("%g"),
 +                       subc_list_double_at (&percentile_list, i));
  
 -            populate_percentiles (tbl, n_heading_columns - 1,
 -                                  row,
 -                                  & (*fs)->m[i]);
  
 +    }
  
 -            count++ ;
 -            fs++;
 -          }
 +  tab_joint_text (tbl,
 +                n_cols - n_percentiles, 0,
 +                n_cols - 1, 0,
 +                TAB_CENTER | TAT_TITLE,
 +                _("Percentiles"));
  
 +  /* Vertical lines for the data only */
 +  tab_box (tbl,
 +         -1, -1,
 +         -1, TAL_1,
 +         n_cols - n_percentiles, 1,
 +         n_cols - 1, n_rows - 1);
  
 -      }
 -      else
 -      {
 -        populate_percentiles (tbl, n_heading_columns - 1,
 -                              i * n_stat_rows * n_factors  + n_heading_rows,
 -                              &totals[i]);
 -      }
 -    }
 +  tab_hline (tbl, TAL_1, n_cols - n_percentiles, n_cols - 1, 1);
  
  
    tab_submit (tbl);
  }
  
  
 -
 -
  static void
 -populate_percentiles (struct tab_table *tbl, int col, int row,
 -                    const struct metrics *m)
 +factor_to_string_concise (const struct xfactor *fctr,
 +                        const struct factor_result *result,
 +                        struct string *str
 +                        )
  {
 -  int i;
 -
 -  struct percentile **p = (struct percentile **) hsh_sort (m->ptile_hash);
 -
 -  tab_text (tbl,
 -          col, row + 1,
 -          TAB_LEFT | TAT_TITLE,
 -          _ ("Tukey\'s Hinges")
 -          );
 +  if (fctr->indep_var[0])
 +    {
 +      var_append_value_name (fctr->indep_var[0], &result->value[0], str);
  
 -  tab_text (tbl,
 -          col, row,
 -          TAB_LEFT | TAT_TITLE,
 -          ptile_alg_desc[m->ptile_alg]
 -          );
 +      if ( fctr->indep_var[1] )
 +      {
 +        ds_put_cstr (str, ",");
  
 +        var_append_value_name (fctr->indep_var[1], &result->value[1], str);
  
 -  i = 0;
 -  while ( (*p)  )
 -    {
 -      tab_double (tbl, col + i + 1 , row,
 -                TAB_CENTER,
 -                (*p)->v,
 -                NULL);
 -
 -      if ( (*p)->p == 25 )
 -      tab_double (tbl, col + i + 1 , row + 1,
 -                  TAB_CENTER,
 -                  m->hinge[0],
 -                  NULL);
 -
 -      if ( (*p)->p == 50 )
 -      tab_double (tbl, col + i + 1 , row + 1,
 -                  TAB_CENTER,
 -                  m->hinge[1],
 -                  NULL);
 -
 -
 -      if ( (*p)->p == 75 )
 -      tab_double (tbl, col + i + 1 , row + 1,
 -                  TAB_CENTER,
 -                  m->hinge[2],
 -                  NULL);
 -      i++;
 -      p++;
 +        ds_put_cstr (str, ")");
 +      }
      }
  }
  
 +
  static void
 -factor_to_string (const struct factor *fctr,
 -                const struct factor_statistics *fs,
 -                const struct variable *var,
 +factor_to_string (const struct xfactor *fctr,
 +                const struct factor_result *result,
                  struct string *str
                  )
  {
 -  if (var)
 -    ds_put_format (str, "%s (",var_to_string (var) );
 -
 -
 -  ds_put_format (str,  "%s = ",
 -               var_to_string (fctr->indep_var[0]));
 +  if (fctr->indep_var[0])
 +    {
 +      ds_put_format (str, "(%s = ", var_get_name (fctr->indep_var[0]));
  
 -  var_append_value_name (fctr->indep_var[0], fs->id[0], str);
 +      var_append_value_name (fctr->indep_var[0], &result->value[0], str);
  
 -  if ( fctr->indep_var[1] )
 -    {
 -      ds_put_format (str, "; %s = )",
 -                   var_to_string (fctr->indep_var[1]));
 +      if ( fctr->indep_var[1] )
 +      {
 +        ds_put_cstr (str, ",");
 +        ds_put_format (str, "%s = ", var_get_name (fctr->indep_var[1]));
  
 -      var_append_value_name (fctr->indep_var[1], fs->id[1], str);
 -    }
 -  else
 -    {
 -      if ( var )
 -      ds_put_cstr (str, ")");
 +        var_append_value_name (fctr->indep_var[1], &result->value[1], str);
 +      }
 +      ds_put_cstr (str, ")");
      }
  }
  
  
 -static void
 -factor_to_string_concise (const struct factor *fctr,
 -                        const struct factor_statistics *fs,
 -                        struct string *str
 -                        )
  
 -{
 -  var_append_value_name (fctr->indep_var[0], fs->id[0], str);
 -
 -  if ( fctr->indep_var[1] )
 -    {
 -      ds_put_cstr (str, ",");
 -
 -      var_append_value_name (fctr->indep_var[1],fs->id[1], str);
 -
 -      ds_put_cstr (str, ")");
 -    }
 -}
  
  /*
    Local Variables:
index 2c259d0f904c1834c7d7e1614fe4f47084bab5ee,32ac1815ea95c42d471ba3a4c280469a14a36113..41a23f3e2580ebd9a9aac6ff289b89803c6f8da2
@@@ -150,7 -150,7 +150,7 @@@ reg_stats_r (pspp_linreg_cache * c
    adjrsq = 1.0 - (1.0 - rsq) * (c->n_obs - 1.0) / (c->n_obs - c->n_indeps);
    std_error = sqrt (pspp_linreg_mse (c));
    t = tab_create (n_cols, n_rows, 0);
 -  tab_dim (t, tab_natural_dimensions);
 +  tab_dim (t, tab_natural_dimensions, NULL);
    tab_box (t, TAL_2, TAL_2, -1, TAL_1, 0, 0, n_cols - 1, n_rows - 1);
    tab_hline (t, TAL_2, 0, n_cols - 1, 1);
    tab_vline (t, TAL_2, 2, 0, n_rows - 1);
@@@ -193,7 -193,7 +193,7 @@@ reg_stats_coeff (pspp_linreg_cache * c
  
    t = tab_create (n_cols, n_rows, 0);
    tab_headers (t, 2, 0, 1, 0);
 -  tab_dim (t, tab_natural_dimensions);
 +  tab_dim (t, tab_natural_dimensions, NULL);
    tab_box (t, TAL_2, TAL_2, -1, TAL_1, 0, 0, n_cols - 1, n_rows - 1);
    tab_hline (t, TAL_2, 0, n_cols - 1, 1);
    tab_vline (t, TAL_2, 2, 0, n_rows - 1);
@@@ -290,7 -290,7 +290,7 @@@ reg_stats_anova (pspp_linreg_cache * c
    assert (c != NULL);
    t = tab_create (n_cols, n_rows, 0);
    tab_headers (t, 2, 0, 1, 0);
 -  tab_dim (t, tab_natural_dimensions);
 +  tab_dim (t, tab_natural_dimensions, NULL);
  
    tab_box (t, TAL_2, TAL_2, -1, TAL_1, 0, 0, n_cols - 1, n_rows - 1);
  
  
  
    /* Degrees of freedom */
 -  tab_text (t, 3, 1, TAB_RIGHT | TAT_PRINTF, "%g", c->dfm);
 -  tab_text (t, 3, 2, TAB_RIGHT | TAT_PRINTF, "%g", c->dfe);
 -  tab_text (t, 3, 3, TAB_RIGHT | TAT_PRINTF, "%g", c->dft);
 +  tab_text_format (t, 3, 1, TAB_RIGHT, "%g", c->dfm);
 +  tab_text_format (t, 3, 2, TAB_RIGHT, "%g", c->dfe);
 +  tab_text_format (t, 3, 3, TAB_RIGHT, "%g", c->dft);
  
    /* Mean Squares */
    tab_double (t, 4, 1, TAB_RIGHT, msm, NULL);
@@@ -381,7 -381,7 +381,7 @@@ reg_stats_bcov (pspp_linreg_cache * c
    n_rows = 2 * (c->n_indeps + 1);
    t = tab_create (n_cols, n_rows, 0);
    tab_headers (t, 2, 0, 1, 0);
 -  tab_dim (t, tab_natural_dimensions);
 +  tab_dim (t, tab_natural_dimensions, NULL);
    tab_box (t, TAL_2, TAL_2, -1, TAL_1, 0, 0, n_cols - 1, n_rows - 1);
    tab_hline (t, TAL_2, 0, n_cols - 1, 1);
    tab_vline (t, TAL_2, 2, 0, n_rows - 1);
@@@ -542,7 -542,7 +542,7 @@@ regression_trns_free (void *t_
    Gets the predicted values.
   */
  static int
 -regression_trns_pred_proc (void *t_, struct ccase *c,
 +regression_trns_pred_proc (void *t_, struct ccase **c,
                           casenumber case_idx UNUSED)
  {
    size_t i;
    n_vals = (*model->get_vars) (model, vars);
  
    vals = xnmalloc (n_vals, sizeof (*vals));
 -  output = case_data_rw (c, model->pred);
 -  assert (output != NULL);
 +  *c = case_unshare (*c);
 +  output = case_data_rw (*c, model->pred);
  
    for (i = 0; i < n_vals; i++)
      {
 -      vals[i] = case_data (c, vars[i]);
 +      vals[i] = case_data (*c, vars[i]);
      }
    output->f = (*model->predict) ((const struct variable **) vars,
                                 vals, model, n_vals);
    Gets the residuals.
   */
  static int
 -regression_trns_resid_proc (void *t_, struct ccase *c,
 +regression_trns_resid_proc (void *t_, struct ccase **c,
                            casenumber case_idx UNUSED)
  {
    size_t i;
    n_vals = (*model->get_vars) (model, vars);
  
    vals = xnmalloc (n_vals, sizeof (*vals));
 -  output = case_data_rw (c, model->resid);
 +  *c = case_unshare (*c);
 +  output = case_data_rw (*c, model->resid);
    assert (output != NULL);
  
    for (i = 0; i < n_vals; i++)
      {
 -      vals[i] = case_data (c, vars[i]);
 +      vals[i] = case_data (*c, vars[i]);
      }
 -  obs = case_data (c, model->depvar);
 +  obs = case_data (*c, model->depvar);
    output->f = (*model->residual) ((const struct variable **) vars,
                                  vals, obs, model, n_vals);
    free (vals);
@@@ -689,21 -688,17 +689,21 @@@ subcommand_save (struct dataset *ds, in
  
        for (lc = models; lc < models + cmd.n_dependent; lc++)
        {
 -        assert (*lc != NULL);
 -        assert ((*lc)->depvar != NULL);
 -        if (cmd.a_save[REGRESSION_SV_RESID])
 -          {
 -            reg_save_var (ds, "RES", regression_trns_resid_proc, *lc,
 -                          &(*lc)->resid, n_trns);
 -          }
 -        if (cmd.a_save[REGRESSION_SV_PRED])
 +        if (*lc != NULL)
            {
 -            reg_save_var (ds, "PRED", regression_trns_pred_proc, *lc,
 -                          &(*lc)->pred, n_trns);
 +            if ((*lc)->depvar != NULL)
 +              {
 +                if (cmd.a_save[REGRESSION_SV_RESID])
 +                  {
 +                    reg_save_var (ds, "RES", regression_trns_resid_proc, *lc,
 +                                  &(*lc)->resid, n_trns);
 +                  }
 +                if (cmd.a_save[REGRESSION_SV_PRED])
 +                  {
 +                    reg_save_var (ds, "PRED", regression_trns_pred_proc, *lc,
 +                                  &(*lc)->pred, n_trns);
 +                  }
 +              }
            }
        }
      }
@@@ -826,7 -821,7 +826,7 @@@ prepare_categories (struct casereader *
                    struct moments_var *mom)
  {
    int n_data;
 -  struct ccase c;
 +  struct ccase *c;
    size_t i;
  
    assert (vars != NULL);
        cat_stored_values_create (vars[i]);
  
    n_data = 0;
 -  for (; casereader_read (input, &c); case_destroy (&c))
 +  for (; (c = casereader_read (input)) != NULL; case_unref (c))
      {
        /*
           The second condition ensures the program will run even if
         */
        for (i = 0; i < n_vars; i++)
        {
 -        const union value *val = case_data (&c, vars[i]);
 +        const union value *val = case_data (c, vars[i]);
          if (var_is_alpha (vars[i]))
            cat_value_update (vars[i], val);
          else
@@@ -866,6 -861,39 +866,6 @@@ coeff_init (pspp_linreg_cache * c, stru
    pspp_coeff_init (c->coeff, dm);
  }
  
 -/*
 -  Put the moments in the linreg cache.
 - */
 -static void
 -compute_moments (pspp_linreg_cache * c, struct moments_var *mom,
 -               struct design_matrix *dm, size_t n)
 -{
 -  size_t i;
 -  size_t j;
 -  double weight;
 -  double mean;
 -  double variance;
 -  double skewness;
 -  double kurtosis;
 -  /*
 -     Scan the variable names in the columns of the design matrix.
 -     When we find the variable we need, insert its mean in the cache.
 -   */
 -  for (i = 0; i < dm->m->size2; i++)
 -    {
 -      for (j = 0; j < n; j++)
 -      {
 -        if (design_matrix_col_to_var (dm, i) == (mom + j)->v)
 -          {
 -            moments1_calculate ((mom + j)->m, &weight, &mean, &variance,
 -                                &skewness, &kurtosis);
 -            pspp_linreg_set_indep_variable_mean (c, (mom + j)->v, mean);
 -            pspp_linreg_set_indep_variable_sd (c, (mom + j)->v, sqrt (variance));
 -          }
 -      }
 -    }
 -}
 -
  static bool
  run_regression (struct casereader *input, struct cmd_regression *cmd,
                struct dataset *ds, pspp_linreg_cache **models)
    size_t i;
    int n_indep = 0;
    int k;
 -  struct ccase c;
 +  struct ccase *c;
    const struct variable **indep_vars;
    struct design_matrix *X;
    struct moments_var *mom;
  
    assert (models != NULL);
  
 -  if (!casereader_peek (input, 0, &c))
 +  c = casereader_peek (input, 0);
 +  if (c == NULL)
      {
        casereader_destroy (input);
        return true;
      }
 -  output_split_file_values (ds, &c);
 -  case_destroy (&c);
 +  output_split_file_values (ds, c);
 +  case_unref (c);
  
    if (!v_variables)
      {
      }
    lopts.get_depvar_mean_std = 1;
  
-   lopts.get_indep_mean_std = xnmalloc (n_variables, sizeof (int));
    indep_vars = xnmalloc (n_variables, sizeof *indep_vars);
  
    for (k = 0; k < cmd->n_dependent; k++)
        const struct variable *dep_var;
        struct casereader *reader;
        casenumber row;
 -      struct ccase c;
 +      struct ccase *c;
        size_t n_data;          /* Number of valid cases. */
  
        dep_var = cmd->v_dependent[k];
        n_indep = identify_indep_vars (indep_vars, dep_var);
        reader = casereader_clone (input);
        reader = casereader_create_filter_missing (reader, indep_vars, n_indep,
 -                                               MV_ANY, NULL);
 +                                               MV_ANY, NULL, NULL);
        reader = casereader_create_filter_missing (reader, &dep_var, 1,
 -                                               MV_ANY, NULL);
 +                                               MV_ANY, NULL, NULL);
        n_data = prepare_categories (casereader_clone (reader),
                                   indep_vars, n_indep, mom);
  
            design_matrix_create (n_indep,
                                  (const struct variable **) indep_vars,
                                  n_data);
+         lopts.get_indep_mean_std = xnmalloc (X->m->size2, sizeof (int));
          for (i = 0; i < X->m->size2; i++)
            {
              lopts.get_indep_mean_std[i] = 1;
            }
 -        models[k] = pspp_linreg_cache_alloc (X->m->size1, X->m->size2);
 +        models[k] = pspp_linreg_cache_alloc (dep_var, (const struct variable **) indep_vars,
 +                                             X->m->size1, n_indep);
          models[k]->depvar = dep_var;
          /*
             For large data sets, use QR decomposition.
             The second pass fills the design matrix.
           */
          reader = casereader_create_counter (reader, &row, -1);
 -        for (; casereader_read (reader, &c); case_destroy (&c))
 +        for (; (c = casereader_read (reader)) != NULL; case_unref (c))
            {
              for (i = 0; i < n_indep; ++i)
                {
                  const struct variable *v = indep_vars[i];
 -                const union value *val = case_data (&c, v);
 +                const union value *val = case_data (c, v);
                  if (var_is_alpha (v))
                    design_matrix_set_categorical (X, row, v, val);
                  else
                    design_matrix_set_numeric (X, row, v, val);
                }
 -            gsl_vector_set (Y, row, case_num (&c, dep_var));
 +            gsl_vector_set (Y, row, case_num (c, dep_var));
            }
          /*
             Now that we know the number of coefficients, allocate space
  
          gsl_vector_free (Y);
          design_matrix_destroy (X);
+         free (lopts.get_indep_mean_std);
        }
        else
        {
      }
    free (mom);
    free (indep_vars);
-   free (lopts.get_indep_mean_std);
    casereader_destroy (input);
  
    return true;
index d25477cdd153f27a55b8170f89bdf75e6020b75a,ba7e9388a95274560589d30605ce2be3e0720718..c448d52ea035c3d163bef9bf343a8d6b9609f904
@@@ -32,8 -32,6 +32,8 @@@
  #include <language/command.h>
  #include <language/dictionary/split-file.h>
  #include <language/lexer/lexer.h>
 +#include <language/lexer/value-parser.h>
 +#include <libpspp/array.h>
  #include <libpspp/assertion.h>
  #include <libpspp/compiler.h>
  #include <libpspp/hash.h>
@@@ -47,8 -45,7 +47,9 @@@
  #include <output/table.h>
  #include <data/format.h>
  
++#include "minmax.h"
  #include "xalloc.h"
 +#include "xmemdup0.h"
  
  #include "gettext.h"
  #define _(msgid) gettext (msgid)
  
  /* (specification)
     "T-TEST" (tts_):
 -     +groups=custom;
 -     testval=double;
 -     +variables=varlist("PV_NO_SCRATCH | PV_NUMERIC");
 -     +pairs=custom;
 -     missing=miss:!analysis/listwise,
 -            incl:include/!exclude;
 -     +format=fmt:!labels/nolabels;
 -     criteria=:cin(d:criteria,"%s > 0. && %s < 1.").
 +   +groups=custom;
 +   testval=double;
 +   +variables=varlist("PV_NO_SCRATCH | PV_NUMERIC");
 +   +pairs=custom;
 +   missing=miss:!analysis/listwise,
 +   incl:include/!exclude;
 +   +format=fmt:!labels/nolabels;
 +   criteria=:cin(d:criteria,"%s > 0. && %s < 1.").
  */
  /* (declarations) */
  /* (functions) */
  
 -
 -/* Variable for the GROUPS subcommand, if given. */
 -static struct variable *indep_var;
 -
  enum comparison
    {
 -    CMP_LE = -2,
 -    CMP_EQ = 0,
 +    CMP_LE,
 +    CMP_EQ,
    };
  
 -struct group_properties
 -{
 -  /* The comparison criterion */
 -  enum comparison criterion;
 -
 -  /* The width of the independent variable */
 -  int indep_width ;
 -
 -  union {
 -    /* The value of the independent variable at which groups are determined to
 -       belong to one group or the other */
 -    double critical_value;
 -
 -
 -    /* The values of the independent variable for each group */
 -    union value g_value[2];
 -  } v ;
 -
 -};
 -
 -
 -static struct group_properties gp ;
 -
 -
 -
 -/* PAIRS: Number of pairs to be compared ; each pair. */
 -static int n_pairs = 0 ;
 +/* A pair of variables to be compared. */
  struct pair
 -{
 -  /* The variables comprising the pair */
 -  const struct variable *v[2];
 -
 -  /* The number of valid variable pairs */
 -  double n;
 -
 -  /* The sum of the members */
 -  double sum[2];
 -
 -  /* sum of squares of the members */
 -  double ssq[2];
 -
 -  /* Std deviation of the members */
 -  double std_dev[2];
 -
 -
 -  /* Sample Std deviation of the members */
 -  double s_std_dev[2];
 -
 -  /* The means of the members */
 -  double mean[2];
 -
 -  /* The correlation coefficient between the variables */
 -  double correlation;
 -
 -  /* The sum of the differences */
 -  double sum_of_diffs;
 -
 -  /* The sum of the products */
 -  double sum_of_prod;
 -
 -  /* The mean of the differences */
 -  double mean_diff;
 -
 -  /* The sum of the squares of the differences */
 -  double ssq_diffs;
 +  {
 +    const struct variable *v[2]; /* The paired variables. */
 +    double n;             /* The number of valid variable pairs */
 +    double sum[2];        /* The sum of the members */
 +    double ssq[2];        /* sum of squares of the members */
 +    double std_dev[2];    /* Std deviation of the members */
 +    double s_std_dev[2];  /* Sample Std deviation of the members */
 +    double mean[2];       /* The means of the members */
 +    double correlation;   /* Correlation coefficient between the variables. */
 +    double sum_of_diffs;  /* The sum of the differences */
 +    double sum_of_prod;   /* The sum of the products */
 +    double mean_diff;     /* The mean of the differences */
 +    double ssq_diffs;     /* The sum of the squares of the differences */
 +    double std_dev_diff;  /* The std deviation of the differences */
 +  };
  
 -  /* The std deviation of the differences */
 -  double std_dev_diff;
 +/* Which mode was T-TEST invoked */
 +enum t_test_mode {
 +  T_1_SAMPLE,                   /* One-sample tests. */
 +  T_IND_SAMPLES,                /* Independent-sample tests. */
 +  T_PAIRED                      /* Paired-sample tests. */
  };
  
 -static struct pair *pairs=0;
 -
 -static int parse_value (struct lexer *lexer, union value * v, enum val_type);
 -
 -/* Structures and Functions for the Statistics Summary Box */
 -struct ssbox;
 -typedef void populate_ssbox_func (struct ssbox *ssb,
 -                                const struct dictionary *,
 -                                struct cmd_t_test *cmd);
 -typedef void finalize_ssbox_func (struct ssbox *ssb);
 +/* Total state of a T-TEST procedure. */
 +struct t_test_proc
 +  {
 +    enum t_test_mode mode;      /* Mode that T-TEST was invoked in. */
 +    double criteria;            /* Confidence interval in (0, 1). */
 +    enum mv_class exclude;      /* Classes of missing values to exclude. */
 +    bool listwise_missing;      /* Drop whole case if one missing var? */
 +    struct fmt_spec weight_format; /* Format of weight variable. */
 +
 +    /* Dependent variables. */
 +    const struct variable **vars;
 +    size_t n_vars;
 +
 +    /* For mode == T_1_SAMPLE. */
 +    double testval;
 +
 +    /* For mode == T_PAIRED only. */
 +    struct pair *pairs;
 +    size_t n_pairs;
 +
 +    /* For mode == T_IND_SAMPLES only. */
 +    struct variable *indep_var; /* Independent variable. */
 +    enum comparison criterion;  /* Type of comparison. */
 +    double critical_value;      /* CMP_LE only: Grouping threshold value. */
 +    union value g_value[2];     /* CMP_EQ only: Per-group indep var values. */
 +  };
  
 +/* Statistics Summary Box */
  struct ssbox
 -{
 -  struct tab_table *t;
 -
 -  populate_ssbox_func *populate;
 -  finalize_ssbox_func *finalize;
 -
 -};
 -
 -/* Create a ssbox */
 -void ssbox_create (struct ssbox *ssb,   struct cmd_t_test *cmd, int mode);
 -
 -/* Populate a ssbox according to cmd */
 -void ssbox_populate (struct ssbox *ssb, const struct dictionary *dict,
 -                   struct cmd_t_test *cmd);
 -
 -/* Submit and destroy a ssbox */
 -void ssbox_finalize (struct ssbox *ssb);
 -
 -/* A function to create, populate and submit the Paired Samples Correlation
 -   box */
 -static void pscbox (const struct dictionary *);
 +  {
 +    struct tab_table *t;
 +    void (*populate) (struct ssbox *, struct t_test_proc *);
 +    void (*finalize) (struct ssbox *);
 +  };
  
 +static void ssbox_create (struct ssbox *, struct t_test_proc *);
 +static void ssbox_populate (struct ssbox *, struct t_test_proc *);
 +static void ssbox_finalize (struct ssbox *);
  
 -/* Structures and Functions for the Test Results Box */
 -struct trbox;
 +/* Paired Samples Correlation box */
 +static void pscbox (struct t_test_proc *);
  
 -typedef void populate_trbox_func (struct trbox *trb,
 -                                const struct dictionary *dict,
 -                                struct cmd_t_test *cmd);
 -typedef void finalize_trbox_func (struct trbox *trb);
  
 +/* Test Results Box. */
  struct trbox {
    struct tab_table *t;
 -  populate_trbox_func *populate;
 -  finalize_trbox_func *finalize;
 -};
 -
 -/* Create a trbox */
 -void trbox_create (struct trbox *trb,   struct cmd_t_test *cmd, int mode);
 -
 -/* Populate a ssbox according to cmd */
 -static void trbox_populate (struct trbox *trb, const struct dictionary *dict,
 -                   struct cmd_t_test *cmd);
 -
 -/* Submit and destroy a ssbox */
 -void trbox_finalize (struct trbox *trb);
 -
 -/* Which mode was T-TEST invoked */
 -enum {
 -  T_1_SAMPLE = 0 ,
 -  T_IND_SAMPLES,
 -  T_PAIRED
 -};
 -
 -
 -static int common_calc (const struct dictionary *dict,
 -                      const struct ccase *, void *,
 -                      enum mv_class);
 -static void common_precalc (struct cmd_t_test *);
 -static void common_postcalc (struct cmd_t_test *);
 -
 -static int one_sample_calc (const struct dictionary *dict, const struct ccase *, void *, enum mv_class);
 -static void one_sample_precalc (struct cmd_t_test *);
 -static void one_sample_postcalc (struct cmd_t_test *);
 -
 -static int  paired_calc (const struct dictionary *dict, const struct ccase *,
 -                       struct cmd_t_test*, enum mv_class);
 -static void paired_precalc (struct cmd_t_test *);
 -static void paired_postcalc (struct cmd_t_test *);
 -
 -static void group_precalc (struct cmd_t_test *);
 -static int  group_calc (const struct dictionary *dict, const struct ccase *,
 -                      struct cmd_t_test *, enum mv_class);
 -static void group_postcalc (struct cmd_t_test *);
 -
 -
 -static void calculate (struct cmd_t_test *,
 -                      struct casereader *,
 -                    const struct dataset *);
 -
 -static  int mode;
 -
 -static struct cmd_t_test cmd;
 +  void (*populate) (struct trbox *, struct t_test_proc *);
 +  void (*finalize) (struct trbox *);
 +  };
  
 -static bool bad_weight_warn = false;
 +static void trbox_create (struct trbox *, struct t_test_proc *);
 +static void trbox_populate (struct trbox *, struct t_test_proc *);
 +static void trbox_finalize (struct trbox *);
  
 +static void calculate (struct t_test_proc *, struct casereader *,
 +                       const struct dataset *);
  
  static int compare_group_binary (const struct group_statistics *a,
 -                              const struct group_statistics *b,
 -                              const struct group_properties *p);
 -
 -
 -static unsigned  hash_group_binary (const struct group_statistics *g,
 -                                 const struct group_properties *p);
 -
 -
 +                                 const struct group_statistics *b,
 +                                 const struct t_test_proc *);
 +static unsigned hash_group_binary (const struct group_statistics *g,
 +                                 const struct t_test_proc *p);
  
  int
  cmd_t_test (struct lexer *lexer, struct dataset *ds)
  {
 +  struct cmd_t_test cmd;
 +  struct t_test_proc proc;
    struct casegrouper *grouper;
    struct casereader *group;
 -  bool ok;
 +  struct variable *wv;
 +  bool ok = false;
  
 -  if ( !parse_t_test (lexer, ds, &cmd, NULL) )
 -    return CMD_FAILURE;
 +  proc.pairs = NULL;
 +  proc.n_pairs = 0;
 +  proc.vars = NULL;
 +  proc.indep_var = NULL;
 +  if (!parse_t_test (lexer, ds, &cmd, &proc))
 +    goto parse_failed;
  
 -  if (! cmd.sbc_criteria)
 -    cmd.criteria=0.95;
 +  wv = dict_get_weight (dataset_dict (ds));
 +  proc.weight_format = wv ? *var_get_print_format (wv) : F_8_0;
  
 -  {
 -    int m=0;
 -    if (cmd.sbc_testval) ++m;
 -    if (cmd.sbc_groups) ++m;
 -    if (cmd.sbc_pairs) ++m;
 -
 -    if ( m != 1)
 -      {
 -      msg (SE,
 -          _ ("TESTVAL, GROUPS and PAIRS subcommands are mutually exclusive.")
 -          );
 -        free_t_test (&cmd);
 -      return CMD_FAILURE;
 -      }
 -  }
 -
 -  if (cmd.sbc_testval)
 -    mode=T_1_SAMPLE;
 -  else if (cmd.sbc_groups)
 -    mode=T_IND_SAMPLES;
 -  else
 -    mode=T_PAIRED;
 -
 -  if ( mode == T_PAIRED)
 +  if ((cmd.sbc_testval != 0) + (cmd.sbc_groups != 0) + (cmd.sbc_pairs != 0)
 +      != 1)
      {
 -      if (cmd.sbc_variables)
 -      {
 -        msg (SE, _ ("VARIABLES subcommand is not appropriate with PAIRS"));
 -          free_t_test (&cmd);
 -        return CMD_FAILURE;
 -      }
 -      else
 -      {
 -        /* Iterate through the pairs and put each variable that is a
 -           member of a pair into cmd.v_variables */
 +      msg (SE, _("Exactly one of TESTVAL, GROUPS and PAIRS subcommands "
 +                 "must be specified."));
 +      goto done;
 +    }
  
 -        int i;
 -        struct hsh_iterator hi;
 -        struct const_hsh_table *hash;
 -        const struct variable *v;
 +  proc.mode = (cmd.sbc_testval ? T_1_SAMPLE
 +               : cmd.sbc_groups ? T_IND_SAMPLES
 +               : T_PAIRED);
 +  proc.criteria = cmd.sbc_criteria ? cmd.criteria : 0.95;
 +  proc.exclude = cmd.incl != TTS_INCLUDE ? MV_ANY : MV_SYSTEM;
 +  proc.listwise_missing = cmd.miss == TTS_LISTWISE;
  
 -        hash = const_hsh_create (n_pairs, compare_vars_by_name, hash_var_by_name,
 -          0, 0);
 +  if (proc.mode == T_1_SAMPLE)
 +    proc.testval = cmd.n_testval[0];
  
 -        for (i=0; i < n_pairs; ++i)
 -          {
 -            const_hsh_insert (hash, pairs[i].v[0]);
 -            const_hsh_insert (hash, pairs[i].v[1]);
 -          }
 +  if (proc.mode == T_PAIRED)
 +    {
 +      size_t i, j;
  
 -        assert (cmd.n_variables == 0);
 -        cmd.n_variables = const_hsh_count (hash);
 -
 -        cmd.v_variables = xnrealloc (cmd.v_variables, cmd.n_variables,
 -                                       sizeof *cmd.v_variables);
 -        /* Iterate through the hash */
 -        for (i=0,v = const_hsh_first (hash, &hi);
 -             v != 0;
 -             v = const_hsh_next (hash, &hi) )
 -          cmd.v_variables[i++]=v;
 -        const_hsh_destroy (hash);
 +      if (cmd.sbc_variables)
 +      {
 +        msg (SE, _("VARIABLES subcommand may not be used with PAIRS."));
 +          goto done;
        }
 +
 +      /* Fill proc.vars with the unique variables from pairs. */
 +      proc.n_vars = proc.n_pairs * 2;
 +      proc.vars = xmalloc (sizeof *proc.vars * proc.n_vars);
 +      for (i = j = 0; i < proc.n_pairs; i++)
 +        {
 +          proc.vars[j++] = proc.pairs[i].v[0];
 +          proc.vars[j++] = proc.pairs[i].v[1];
 +        }
 +      proc.n_vars = sort_unique (proc.vars, proc.n_vars, sizeof *proc.vars,
 +                                 compare_var_ptrs_by_name, NULL);
      }
 -  else if ( !cmd.sbc_variables)
 +  else
      {
 -      msg (SE, _ ("One or more VARIABLES must be specified."));
 -      free_t_test (&cmd);
 -      return CMD_FAILURE;
 +      if (!cmd.n_variables)
 +        {
 +          msg (SE, _("One or more VARIABLES must be specified."));
 +          goto done;
 +        }
 +      proc.n_vars = cmd.n_variables;
 +      proc.vars = cmd.v_variables;
 +      cmd.v_variables = NULL;
      }
  
 -  bad_weight_warn = true;
 -
    /* Data pass. */
    grouper = casegrouper_create_splits (proc_open (ds), dataset_dict (ds));
    while (casegrouper_get_next_group (grouper, &group))
 -    calculate (&cmd, group, ds);
 +    calculate (&proc, group, ds);
    ok = casegrouper_destroy (grouper);
    ok = proc_commit (ds) && ok;
  
 -  n_pairs=0;
 -  free (pairs);
 -  pairs=0;
 -
 -  if ( mode == T_IND_SAMPLES)
 +  if (proc.mode == T_IND_SAMPLES)
      {
        int v;
        /* Destroy any group statistics we created */
 -      for (v = 0 ; v < cmd.n_variables ; ++v )
 +      for (v = 0; v < proc.n_vars; v++)
        {
 -        struct group_proc *grpp = group_proc_get (cmd.v_variables[v]);
 +        struct group_proc *grpp = group_proc_get (proc.vars[v]);
          hsh_destroy (grpp->group_hash);
        }
      }
  
 +done:
    free_t_test (&cmd);
 -  return ok ? CMD_SUCCESS : CMD_CASCADING_FAILURE;
 +parse_failed:
 +  if (proc.indep_var != NULL)
 +    {
 +      int width = var_get_width (proc.indep_var);
 +      value_destroy (&proc.g_value[0], width);
 +      value_destroy (&proc.g_value[1], width);
 +    }
 +  free (proc.vars);
 +  free (proc.pairs);
 +  return ok ? CMD_SUCCESS : CMD_FAILURE;
  }
  
  static int
 -tts_custom_groups (struct lexer *lexer, struct dataset *ds, struct cmd_t_test *cmd UNUSED, 
 -      void *aux UNUSED)
 +tts_custom_groups (struct lexer *lexer, struct dataset *ds,
 +                   struct cmd_t_test *cmd UNUSED, void *proc_)
  {
 -  int n_group_values=0;
 +  struct t_test_proc *proc = proc_;
 +  int n_values;
 +  int width;
  
    lex_match (lexer, '=');
  
 -  indep_var = parse_variable (lexer, dataset_dict (ds));
 -  if (!indep_var)
 +  proc->indep_var = parse_variable (lexer, dataset_dict (ds));
 +  if (proc->indep_var == NULL)
      {
        lex_error (lexer, "expecting variable name in GROUPS subcommand");
        return 0;
      }
 -
 -  if (var_is_long_string (indep_var))
 -    {
 -      msg (SE, _ ("Long string variable %s is not valid here."),
 -         var_get_name (indep_var));
 -      return 0;
 -    }
 +  width = var_get_width (proc->indep_var);
 +  value_init (&proc->g_value[0], width);
 +  value_init (&proc->g_value[1], width);
  
    if (!lex_match (lexer, '('))
 +    n_values = 0;
 +  else
      {
 -      if (var_is_numeric (indep_var))
 -      {
 -        gp.v.g_value[0].f = 1;
 -        gp.v.g_value[1].f = 2;
 -
 -        gp.criterion = CMP_EQ;
 -
 -        n_group_values = 2;
 -
 -        return 1;
 -      }
 +      if (!parse_value (lexer, &proc->g_value[0], width))
 +        return 0;
 +      lex_match (lexer, ',');
 +      if (lex_match (lexer, ')'))
 +        n_values = 1;
        else
 -      {
 -        msg (SE, _ ("When applying GROUPS to a string variable, two "
 -                   "values must be specified."));
 -        return 0;
 -      }
 +        {
 +          if (!parse_value (lexer, &proc->g_value[1], width)
 +              || !lex_force_match (lexer, ')'))
 +            return 0;
 +          n_values = 2;
 +        }
      }
  
 -  if (!parse_value (lexer, &gp.v.g_value[0], var_get_type (indep_var)))
 -      return 0;
 -
 -  lex_match (lexer, ',');
 -  if (lex_match (lexer, ')'))
 +  if (var_is_numeric (proc->indep_var))
      {
 -      if (var_is_alpha (indep_var))
 +      proc->criterion = n_values == 1 ? CMP_LE : CMP_EQ;
 +      if (n_values == 1)
 +        proc->critical_value = proc->g_value[0].f;
 +      else if (n_values == 0)
        {
 -        msg (SE, _ ("When applying GROUPS to a string variable, two "
 -                   "values must be specified."));
 -        return 0;
 +        proc->g_value[0].f = 1;
 +        proc->g_value[1].f = 2;
        }
 -      gp.criterion = CMP_LE;
 -      gp.v.critical_value = gp.v.g_value[0].f;
 -
 -      n_group_values = 1;
 -      return 1;
      }
 -
 -  if (!parse_value (lexer, &gp.v.g_value[1], var_get_type (indep_var)))
 -    return 0;
 -
 -  n_group_values = 2;
 -  if (!lex_force_match (lexer, ')'))
 -    return 0;
 -
 -  if ( n_group_values == 2 )
 -    gp.criterion = CMP_EQ ;
    else
 -    gp.criterion = CMP_LE ;
 -
 -
 -  if ( var_is_alpha (indep_var))
      {
 -      buf_copy_rpad (gp.v.g_value [0].s, var_get_width (indep_var),
 -                   gp.v.g_value [0].s, strlen (gp.v.g_value[0].s));
 -
 -      buf_copy_rpad (gp.v.g_value [1].s, var_get_width (indep_var),
 -                   gp.v.g_value [1].s, strlen (gp.v.g_value[1].s));
 +      proc->criterion = CMP_EQ;
 +      if (n_values != 2)
 +      {
 +        msg (SE, _("When applying GROUPS to a string variable, two "
 +                     "values must be specified."));
 +        return 0;
 +      }
      }
 -
    return 1;
  }
  
 +static void
 +add_pair (struct t_test_proc *proc,
 +          const struct variable *v0, const struct variable *v1)
 +{
 +  struct pair *p = &proc->pairs[proc->n_pairs++];
 +  p->v[0] = v0;
 +  p->v[1] = v1;
 +}
  
  static int
 -tts_custom_pairs (struct lexer *lexer, struct dataset *ds, struct cmd_t_test *cmd UNUSED, void *aux UNUSED)
 +tts_custom_pairs (struct lexer *lexer, struct dataset *ds,
 +                  struct cmd_t_test *cmd UNUSED, void *proc_)
  {
 -  const struct variable **vars;
 -  size_t n_vars;
 -  size_t n_pairs_local;
 -
 -  size_t n_before_WITH;
 -  size_t n_after_WITH = SIZE_MAX;
 -  int paired ; /* Was the PAIRED keyword given ? */
 -
 -  lex_match (lexer, '=');
 +  struct t_test_proc *proc = proc_;
  
 -  n_vars=0;
 -  if (!parse_variables_const (lexer, dataset_dict (ds), &vars, &n_vars,
 -                      PV_DUPLICATE | PV_NUMERIC | PV_NO_SCRATCH))
 -    {
 -      free (vars);
 -      return 0;
 -    }
 -  assert (n_vars);
 -
 -  n_before_WITH = 0;
 -  if (lex_match (lexer, T_WITH))
 -    {
 -      n_before_WITH = n_vars;
 -      if (!parse_variables_const (lexer, dataset_dict (ds), &vars, &n_vars,
 -                          PV_DUPLICATE | PV_APPEND
 -                          | PV_NUMERIC | PV_NO_SCRATCH))
 -      {
 -        free (vars);
 -        return 0;
 -      }
 -      n_after_WITH = n_vars - n_before_WITH;
 -    }
 +  const struct variable **vars1 = NULL;
 +  size_t n_vars1 = 0;
  
 -  paired = (lex_match (lexer, '(') && lex_match_id (lexer, "PAIRED") && lex_match (lexer, ')'));
 +  const struct variable **vars2 = NULL;
 +  size_t n_vars2 = 0;
  
 -  /* Determine the number of pairs needed */
 -  if (paired)
 -    {
 -      if (n_before_WITH != n_after_WITH)
 -      {
 -        free (vars);
 -        msg (SE, _ ("PAIRED was specified but the number of variables "
 -                   "preceding WITH (%zu) did not match the number "
 -                   "following (%zu)."),
 -               n_before_WITH, n_after_WITH);
 -        return 0;
 -      }
 -      n_pairs_local = n_before_WITH;
 -    }
 -  else if (n_before_WITH > 0) /* WITH keyword given, but not PAIRED keyword */
 -    {
 -      n_pairs_local = n_before_WITH * n_after_WITH ;
 -    }
 -  else /* Neither WITH nor PAIRED keyword given */
 -    {
 -      if (n_vars < 2)
 -      {
 -        free (vars);
 -        msg (SE, _ ("At least two variables must be specified "
 -                   "on PAIRS."));
 -        return 0;
 -      }
 -
 -      /* how many ways can you pick 2 from n_vars ? */
 -      n_pairs_local = n_vars * (n_vars - 1) / 2;
 -    }
 +  bool paired = false;
  
 +  size_t n_total_pairs;
 +  size_t i, j;
  
 -  /* Allocate storage for the pairs */
 -  pairs = xnrealloc (pairs, n_pairs + n_pairs_local, sizeof *pairs);
 +  lex_match (lexer, '=');
  
 -  /* Populate the pairs with the appropriate variables */
 -  if ( paired )
 -    {
 -      int i;
 +  if (!parse_variables_const (lexer, dataset_dict (ds), &vars1, &n_vars1,
 +                              PV_DUPLICATE | PV_NUMERIC | PV_NO_SCRATCH))
 +    return 0;
  
 -      assert (n_pairs_local == n_vars / 2);
 -      for (i = 0; i < n_pairs_local; ++i)
 -      {
 -        pairs[i].v[n_pairs] = vars[i];
 -        pairs[i].v[n_pairs + 1] = vars[i + n_pairs_local];
 -      }
 -    }
 -  else if (n_before_WITH > 0) /* WITH keyword given, but not PAIRED keyword */
 +  if (lex_match (lexer, T_WITH))
      {
 -      int i,j;
 -      size_t p = n_pairs;
 -
 -      for (i=0 ; i < n_before_WITH ; ++i )
 -      {
 -        for (j=0 ; j < n_after_WITH ; ++j)
 -          {
 -            pairs[p].v[0] = vars[i];
 -            pairs[p].v[1] = vars[j+n_before_WITH];
 -            ++p;
 -          }
 -      }
 +      if (!parse_variables_const (lexer, dataset_dict (ds), &vars2, &n_vars2,
 +                                  PV_DUPLICATE | PV_NUMERIC | PV_NO_SCRATCH))
 +        {
 +          free (vars1);
 +          return 0;
 +        }
 +
 +      if (lex_match (lexer, '(')
 +          && lex_match_id (lexer, "PAIRED")
 +          && lex_match (lexer, ')'))
 +        {
 +          paired = true;
 +          if (n_vars1 != n_vars2)
 +            {
 +              msg (SE, _("PAIRED was specified but the number of variables "
 +                         "preceding WITH (%zu) did not match the number "
 +                         "following (%zu)."),
 +                   n_vars1, n_vars2);
 +              free (vars1);
 +              free (vars2);
 +              return 0;
 +            }
 +        }
      }
 -  else /* Neither WITH nor PAIRED given */
 +  else
      {
 -      size_t i,j;
 -      size_t p=n_pairs;
 -
 -      for (i=0 ; i < n_vars ; ++i )
 +      if (n_vars1 < 2)
        {
 -        for (j=i+1 ; j < n_vars ; ++j)
 -          {
 -            pairs[p].v[0] = vars[i];
 -            pairs[p].v[1] = vars[j];
 -            ++p;
 -          }
 +        free (vars1);
 +        msg (SE, _("At least two variables must be specified on PAIRS."));
 +        return 0;
        }
      }
  
 -  n_pairs+=n_pairs_local;
 +  /* Allocate storage for the new pairs. */
 +  n_total_pairs = proc->n_pairs + (paired ? n_vars1
 +                                   : n_vars2 > 0 ? n_vars1 * n_vars2
 +                                   : n_vars1 * (n_vars1 - 1) / 2);
 +  proc->pairs = xnrealloc (proc->pairs, n_total_pairs, sizeof *proc->pairs);
  
 -  free (vars);
 -  return 1;
 -}
 -
 -/* Parses the current token (numeric or string, depending on type)
 -    value v and returns success. */
 -static int
 -parse_value (struct lexer *lexer, union value * v, enum val_type type)
 -{
 -  if (type == VAL_NUMERIC)
 -    {
 -      if (!lex_force_num (lexer))
 -      return 0;
 -      v->f = lex_tokval (lexer);
 -    }
 +  /* Populate the pairs with the appropriate variables. */
 +  if (paired)
 +    for (i = 0; i < n_vars1; i++)
 +      add_pair (proc, vars1[i], vars2[i]);
 +  else if (n_vars2 > 0)
 +    for (i = 0; i < n_vars1; i++)
 +      for (j = 0; j < n_vars2; j++)
 +        add_pair (proc, vars1[i], vars2[j]);
    else
 -    {
 -      if (!lex_force_string (lexer))
 -      return 0;
 -      memset  (v->s, ' ', MAX_SHORT_STRING);
 -      strncpy (v->s, ds_cstr (lex_tokstr (lexer)), ds_length (lex_tokstr (lexer)));
 -    }
 -
 -  lex_get (lexer);
 +    for (i = 0; i < n_vars1; i++)
 +      for (j = i + 1; j < n_vars1; j++)
 +        add_pair (proc, vars1[i], vars1[j]);
 +  assert (proc->n_pairs == n_total_pairs);
  
 +  free (vars1);
 +  free (vars2);
    return 1;
  }
 +\f
 +/* Implementation of the SSBOX object. */
  
 +static void ssbox_base_init (struct ssbox *, int cols, int rows);
 +static void ssbox_base_finalize (struct ssbox *);
 +static void ssbox_one_sample_init (struct ssbox *, struct t_test_proc *);
 +static void ssbox_independent_samples_init (struct ssbox *, struct t_test_proc *);
 +static void ssbox_paired_init (struct ssbox *, struct t_test_proc *);
  
 -/* Implementation of the SSBOX object */
 -
 -void ssbox_base_init (struct ssbox *this, int cols,int rows);
 -
 -void ssbox_base_finalize (struct ssbox *ssb);
 -
 -void ssbox_one_sample_init (struct ssbox *this,
 -                         struct cmd_t_test *cmd );
 -
 -void ssbox_independent_samples_init (struct ssbox *this,
 -                                  struct cmd_t_test *cmd);
 -
 -void ssbox_paired_init (struct ssbox *this,
 -                         struct cmd_t_test *cmd);
 -
 -
 -/* Factory to create an ssbox */
 -void
 -ssbox_create (struct ssbox *ssb, struct cmd_t_test *cmd, int mode)
 +/* Factory to create an ssbox. */
 +static void
 +ssbox_create (struct ssbox *ssb, struct t_test_proc *proc)
  {
 -    switch (mode)
 -      {
 -      case T_1_SAMPLE:
 -      ssbox_one_sample_init (ssb,cmd);
 -      break;
 -      case T_IND_SAMPLES:
 -      ssbox_independent_samples_init (ssb,cmd);
 -      break;
 -      case T_PAIRED:
 -      ssbox_paired_init (ssb,cmd);
 -      break;
 -      default:
 -      NOT_REACHED ();
 -      }
 +  switch (proc->mode)
 +    {
 +    case T_1_SAMPLE:
 +      ssbox_one_sample_init (ssb, proc);
 +      break;
 +    case T_IND_SAMPLES:
 +      ssbox_independent_samples_init (ssb, proc);
 +      break;
 +    case T_PAIRED:
 +      ssbox_paired_init (ssb, proc);
 +      break;
 +    default:
 +      NOT_REACHED ();
 +    }
  }
  
 -
 -
  /* Despatcher for the populate method */
 -void
 -ssbox_populate (struct ssbox *ssb, const struct dictionary *dict,
 -              struct cmd_t_test *cmd)
 +static void
 +ssbox_populate (struct ssbox *ssb, struct t_test_proc *proc)
  {
 -  ssb->populate (ssb, dict, cmd);
 +  ssb->populate (ssb, proc);
  }
  
 -
  /* Despatcher for finalize */
 -void
 +static void
  ssbox_finalize (struct ssbox *ssb)
  {
    ssb->finalize (ssb);
  }
  
 -
  /* Submit the box and clear up */
 -void
 +static void
  ssbox_base_finalize (struct ssbox *ssb)
  {
    tab_submit (ssb->t);
  }
  
 -
 -
  /* Initialize a ssbox struct */
 -void
 -ssbox_base_init (struct ssbox *this, int cols,int rows)
 +static void
 +ssbox_base_init (struct ssbox *this, int cols, int rows)
  {
    this->finalize = ssbox_base_finalize;
    this->t = tab_create (cols, rows, 0);
  
    tab_columns (this->t, SOM_COL_DOWN, 1);
 -  tab_headers (this->t,0,0,1,0);
 -  tab_box (this->t, TAL_2, TAL_2, TAL_0, TAL_1, 0, 0, cols -1, rows -1 );
 -  tab_hline (this->t, TAL_2,0,cols-1,1);
 -  tab_dim (this->t, tab_natural_dimensions);
 +  tab_headers (this->t, 0, 0, 1, 0);
 +  tab_box (this->t, TAL_2, TAL_2, TAL_0, TAL_1, 0, 0, cols - 1, rows - 1);
 +  tab_hline (this->t, TAL_2, 0, cols- 1, 1);
 +  tab_dim (this->t, tab_natural_dimensions, NULL);
  }
 +\f
 +/* ssbox implementations. */
  
 -void  ssbox_one_sample_populate (struct ssbox *ssb,
 -                               const struct dictionary *,
 -                               struct cmd_t_test *cmd);
 +static void ssbox_one_sample_populate (struct ssbox *, struct t_test_proc *);
 +static void ssbox_independent_samples_populate (struct ssbox *,
 +                                                struct t_test_proc *);
 +static void ssbox_paired_populate (struct ssbox *, struct t_test_proc *);
  
  /* Initialize the one_sample ssbox */
 -void
 -ssbox_one_sample_init (struct ssbox *this,
 -                         struct cmd_t_test *cmd )
 +static void
 +ssbox_one_sample_init (struct ssbox *this, struct t_test_proc *proc)
  {
 -  const int hsize=5;
 -  const int vsize=cmd->n_variables+1;
 +  const int hsize = 5;
 +  const int vsize = proc->n_vars + 1;
  
    this->populate = ssbox_one_sample_populate;
  
 -  ssbox_base_init (this, hsize,vsize);
 -  tab_title (this->t, _ ("One-Sample Statistics"));
 -  tab_vline (this->t, TAL_2, 1,0,vsize - 1);
 -  tab_text (this->t, 1, 0, TAB_CENTER | TAT_TITLE, _ ("N"));
 -  tab_text (this->t, 2, 0, TAB_CENTER | TAT_TITLE, _ ("Mean"));
 -  tab_text (this->t, 3, 0, TAB_CENTER | TAT_TITLE, _ ("Std. Deviation"));
 -  tab_text (this->t, 4, 0, TAB_CENTER | TAT_TITLE, _ ("SE. Mean"));
 +  ssbox_base_init (this, hsize, vsize);
 +  tab_title (this->t, _("One-Sample Statistics"));
 +  tab_vline (this->t, TAL_2, 1, 0, vsize - 1);
 +  tab_text (this->t, 1, 0, TAB_CENTER | TAT_TITLE, _("N"));
 +  tab_text (this->t, 2, 0, TAB_CENTER | TAT_TITLE, _("Mean"));
 +  tab_text (this->t, 3, 0, TAB_CENTER | TAT_TITLE, _("Std. Deviation"));
 +  tab_text (this->t, 4, 0, TAB_CENTER | TAT_TITLE, _("S.E. Mean"));
  }
  
 -static void ssbox_independent_samples_populate (struct ssbox *ssb,
 -                                              const struct dictionary *,
 -                                              struct cmd_t_test *cmd);
 -
  /* Initialize the independent samples ssbox */
 -void
 -ssbox_independent_samples_init (struct ssbox *this,
 -      struct cmd_t_test *cmd)
 +static void
 +ssbox_independent_samples_init (struct ssbox *this, struct t_test_proc *proc)
  {
    int hsize=6;
 -  int vsize = cmd->n_variables*2 +1;
 +  int vsize = proc->n_vars * 2 + 1;
  
    this->populate = ssbox_independent_samples_populate;
  
 -  ssbox_base_init (this, hsize,vsize);
 -  tab_vline (this->t, TAL_GAP, 1, 0,vsize - 1);
 -  tab_title (this->t, _ ("Group Statistics"));
 -  tab_text (this->t, 1, 0, TAB_CENTER | TAT_TITLE, var_get_name (indep_var));
 -  tab_text (this->t, 2, 0, TAB_CENTER | TAT_TITLE, _ ("N"));
 -  tab_text (this->t, 3, 0, TAB_CENTER | TAT_TITLE, _ ("Mean"));
 -  tab_text (this->t, 4, 0, TAB_CENTER | TAT_TITLE, _ ("Std. Deviation"));
 -  tab_text (this->t, 5, 0, TAB_CENTER | TAT_TITLE, _ ("SE. Mean"));
 +  ssbox_base_init (this, hsize, vsize);
 +  tab_vline (this->t, TAL_GAP, 1, 0, vsize - 1);
 +  tab_title (this->t, _("Group Statistics"));
 +  tab_text (this->t, 1, 0, TAB_CENTER | TAT_TITLE,
 +            var_get_name (proc->indep_var));
 +  tab_text (this->t, 2, 0, TAB_CENTER | TAT_TITLE, _("N"));
 +  tab_text (this->t, 3, 0, TAB_CENTER | TAT_TITLE, _("Mean"));
 +  tab_text (this->t, 4, 0, TAB_CENTER | TAT_TITLE, _("Std. Deviation"));
 +  tab_text (this->t, 5, 0, TAB_CENTER | TAT_TITLE, _("S.E. Mean"));
  }
  
 -
  /* Populate the ssbox for independent samples */
  static void
  ssbox_independent_samples_populate (struct ssbox *ssb,
 -                                  const struct dictionary *dict,
 -                                  struct cmd_t_test *cmd)
 +                                    struct t_test_proc *proc)
  {
    int i;
  
 -  const struct variable *wv = dict_get_weight (dict);
 -  const struct fmt_spec *wfmt = wv ? var_get_print_format (wv) : &F_8_0;
 -
 -  char *val_lab[2] = {NULL, NULL};
 +  char *val_lab[2];
    double indep_value[2];
  
 -  char prefix[2][3]={"",""};
 +  char prefix[2][3];
  
 -  if ( var_is_numeric (indep_var) )
 +  for (i = 0; i < 2; i++)
      {
 -      const char *s;
 +      union value *value = &proc->g_value[i];
 +      int width = var_get_width (proc->indep_var);
  
 -      s = var_lookup_value_label (indep_var, &gp.v.g_value[0]);
 -      val_lab[0] = s ? strdup (s) : NULL;
 +      indep_value[i] = (proc->criterion == CMP_LE ? proc->critical_value
 +                        : value->f);
  
 -      s = var_lookup_value_label (indep_var, &gp.v.g_value[1]);
 -      val_lab[1] = s ? strdup (s) : NULL;
 -    }
 -  else
 -    {
 -      val_lab[0] = calloc (sizeof (char), MAX_SHORT_STRING + 1);
 -      val_lab[1] = calloc (sizeof (char), MAX_SHORT_STRING + 1);
 -      memcpy (val_lab[0], gp.v.g_value[0].s, MAX_SHORT_STRING);
 -      memcpy (val_lab[1], gp.v.g_value[1].s, MAX_SHORT_STRING);
 +      if (val_type_from_width (width) == VAL_NUMERIC)
 +        {
 +          const char *s = var_lookup_value_label (proc->indep_var, value);
 +          val_lab[i] = s ? xstrdup (s) : xasprintf ("%g", indep_value[i]);
 +        }
 +      else
 +        val_lab[i] = xmemdup0 (value_str (value, width), width);
      }
  
 -  if (gp.criterion == CMP_LE )
 +  if (proc->criterion == CMP_LE)
      {
 -      strcpy (prefix[0],">=");
 -      strcpy (prefix[1],"<");
 -      indep_value[0] = gp.v.critical_value;
 -      indep_value[1] = gp.v.critical_value;
 +      strcpy (prefix[0], ">=");
 +      strcpy (prefix[1], "<");
      }
    else
      {
 -      indep_value[0] = gp.v.g_value[0].f;
 -      indep_value[1] = gp.v.g_value[1].f;
 +      strcpy (prefix[0], "");
 +      strcpy (prefix[1], "");
      }
  
 -  assert (ssb->t);
 -
 -  for (i=0; i < cmd->n_variables; ++i)
 +  for (i = 0; i < proc->n_vars; i++)
      {
 -      const struct variable *var = cmd->v_variables[i];
 +      const struct variable *var = proc->vars[i];
        struct hsh_table *grp_hash = group_proc_get (var)->group_hash;
        int count=0;
  
 -      tab_text (ssb->t, 0, i*2+1, TAB_LEFT,
 -                var_get_name (cmd->v_variables[i]));
 -
 -      if (val_lab[0])
 -      tab_text (ssb->t, 1, i*2+1, TAB_LEFT | TAT_PRINTF,
 -                "%s%s", prefix[0], val_lab[0]);
 -      else
 -        tab_text (ssb->t, 1, i*2+1, TAB_LEFT | TAT_PRINTF,
 -                  "%s%g", prefix[0], indep_value[0]);
 -
 -
 -      if (val_lab[1])
 -      tab_text (ssb->t, 1, i*2+1+1, TAB_LEFT | TAT_PRINTF,
 -                "%s%s", prefix[1], val_lab[1]);
 -      else
 -        tab_text (ssb->t, 1, i*2+1+1, TAB_LEFT | TAT_PRINTF,
 -                  "%s%g", prefix[1], indep_value[1]);
 -
 +      tab_text (ssb->t, 0, i * 2 + 1, TAB_LEFT,
 +                var_get_name (proc->vars[i]));
 +      tab_text_format (ssb->t, 1, i * 2 + 1, TAB_LEFT,
 +                       "%s%s", prefix[0], val_lab[0]);
 +      tab_text_format (ssb->t, 1, i * 2 + 1+ 1, TAB_LEFT,
 +                       "%s%s", prefix[1], val_lab[1]);
  
        /* Fill in the group statistics */
 -      for ( count = 0 ; count < 2 ; ++count )
 +      for (count = 0; count < 2; count++)
        {
          union value search_val;
 -
          struct group_statistics *gs;
  
 -        if ( gp.criterion == CMP_LE )
 -          {
 -            if ( count == 0 )
 -              {
 -                /* >= case  */
 -                search_val.f = gp.v.critical_value + 1.0;
 -              }
 -            else
 -              {
 -                /*  less than ( < )  case */
 -                search_val.f = gp.v.critical_value - 1.0;
 -              }
 -          }
 +        if (proc->criterion == CMP_LE)
 +            search_val.f = proc->critical_value + (count == 0 ? 1.0 : -1.0);
          else
 -          {
 -            search_val = gp.v.g_value[count];
 -          }
 +            search_val = proc->g_value[count];
  
 -        gs = hsh_find (grp_hash, (void *) &search_val);
 +        gs = hsh_find (grp_hash, &search_val);
          assert (gs);
  
 -        tab_double (ssb->t, 2, i*2+count+1, TAB_RIGHT, gs->n, wfmt);
 -        tab_double (ssb->t, 3, i*2+count+1, TAB_RIGHT, gs->mean, NULL);
 -        tab_double (ssb->t, 4, i*2+count+1, TAB_RIGHT, gs->std_dev, NULL);
 -        tab_double (ssb->t, 5, i*2+count+1, TAB_RIGHT, gs->se_mean, NULL);
 +        tab_double (ssb->t, 2, i * 2 + count+ 1, TAB_RIGHT, gs->n,
 +                      &proc->weight_format);
 +        tab_double (ssb->t, 3, i * 2 + count+ 1, TAB_RIGHT, gs->mean, NULL);
 +        tab_double (ssb->t, 4, i * 2 + count+ 1, TAB_RIGHT, gs->std_dev,
 +                      NULL);
 +        tab_double (ssb->t, 5, i * 2 + count+ 1, TAB_RIGHT, gs->se_mean,
 +                      NULL);
        }
      }
    free (val_lab[0]);
    free (val_lab[1]);
  }
  
 -
 -static void ssbox_paired_populate (struct ssbox *ssb,
 -                                 const struct dictionary *dict,
 -                                 struct cmd_t_test *cmd);
 -
  /* Initialize the paired values ssbox */
 -void
 -ssbox_paired_init (struct ssbox *this, struct cmd_t_test *cmd UNUSED)
 +static void
 +ssbox_paired_init (struct ssbox *this, struct t_test_proc *proc)
  {
 -  int hsize=6;
 -
 -  int vsize = n_pairs*2+1;
 +  int hsize = 6;
 +  int vsize = proc->n_pairs * 2 + 1;
  
    this->populate = ssbox_paired_populate;
  
 -  ssbox_base_init (this, hsize,vsize);
 -  tab_title (this->t, _ ("Paired Sample Statistics"));
 -  tab_vline (this->t,TAL_GAP,1,0,vsize-1);
 -  tab_vline (this->t,TAL_2,2,0,vsize-1);
 -  tab_text (this->t, 2, 0, TAB_CENTER | TAT_TITLE, _ ("Mean"));
 -  tab_text (this->t, 3, 0, TAB_CENTER | TAT_TITLE, _ ("N"));
 -  tab_text (this->t, 4, 0, TAB_CENTER | TAT_TITLE, _ ("Std. Deviation"));
 -  tab_text (this->t, 5, 0, TAB_CENTER | TAT_TITLE, _ ("SE. Mean"));
 +  ssbox_base_init (this, hsize, vsize);
 +  tab_title (this->t, _("Paired Sample Statistics"));
 +  tab_vline (this->t, TAL_GAP, 1, 0, vsize - 1);
 +  tab_vline (this->t, TAL_2, 2, 0, vsize - 1);
 +  tab_text (this->t, 2, 0, TAB_CENTER | TAT_TITLE, _("Mean"));
 +  tab_text (this->t, 3, 0, TAB_CENTER | TAT_TITLE, _("N"));
 +  tab_text (this->t, 4, 0, TAB_CENTER | TAT_TITLE, _("Std. Deviation"));
 +  tab_text (this->t, 5, 0, TAB_CENTER | TAT_TITLE, _("S.E. Mean"));
  }
  
 -
  /* Populate the ssbox for paired values */
 -void
 -ssbox_paired_populate (struct ssbox *ssb, const struct dictionary *dict,
 -                     struct cmd_t_test *cmd UNUSED)
 +static void
 +ssbox_paired_populate (struct ssbox *ssb, struct t_test_proc *proc)
  {
    int i;
  
 -  const struct variable *wv = dict_get_weight (dict);
 -  const struct fmt_spec *wfmt = wv ? var_get_print_format (wv) : &F_8_0;
 -
 -  assert (ssb->t);
 -
 -  for (i=0; i < n_pairs; ++i)
 +  for (i = 0; i < proc->n_pairs; i++)
      {
 +      struct pair *p = &proc->pairs[i];
        int j;
  
 -      tab_text (ssb->t, 0, i*2+1, TAB_LEFT | TAT_PRINTF , _ ("Pair %d"),i);
 -
 -      for (j=0 ; j < 2 ; ++j)
 +      tab_text_format (ssb->t, 0, i * 2 + 1, TAB_LEFT, _("Pair %d"), i);
 +      for (j=0; j < 2; j++)
        {
 -        struct group_statistics *gs;
 -
 -        gs = &group_proc_get (pairs[i].v[j])->ugs;
 -
          /* Titles */
 -
 -        tab_text (ssb->t, 1, i*2+j+1, TAB_LEFT,
 -                    var_get_name (pairs[i].v[j]));
 +        tab_text (ssb->t, 1, i * 2 + j + 1, TAB_LEFT,
 +                    var_get_name (p->v[j]));
  
          /* Values */
 -        tab_double (ssb->t,2, i*2+j+1, TAB_RIGHT, pairs[i].mean[j], NULL);
 -        tab_double (ssb->t,3, i*2+j+1, TAB_RIGHT, pairs[i].n, wfmt);
 -        tab_double (ssb->t,4, i*2+j+1, TAB_RIGHT, pairs[i].std_dev[j], NULL);
 -        tab_double (ssb->t,5, i*2+j+1, TAB_RIGHT,
 -                    pairs[i].std_dev[j]/sqrt (pairs[i].n), NULL);
 -
 +        tab_double (ssb->t, 2, i * 2 + j + 1, TAB_RIGHT, p->mean[j], NULL);
 +        tab_double (ssb->t, 3, i * 2 + j + 1, TAB_RIGHT, p->n,
 +                      &proc->weight_format);
 +        tab_double (ssb->t, 4, i * 2 + j + 1, TAB_RIGHT, p->std_dev[j],
 +                      NULL);
 +        tab_double (ssb->t, 5, i * 2 + j + 1, TAB_RIGHT,
 +                     p->std_dev[j] /sqrt (p->n), NULL);
        }
      }
  }
  
  /* Populate the one sample ssbox */
 -void
 -ssbox_one_sample_populate (struct ssbox *ssb, const struct dictionary *dict,
 -                         struct cmd_t_test *cmd)
 +static void
 +ssbox_one_sample_populate (struct ssbox *ssb, struct t_test_proc *proc)
  {
    int i;
  
 -  const struct variable *wv = dict_get_weight (dict);
 -  const struct fmt_spec *wfmt = wv ? var_get_print_format (wv) : &F_8_0;
 -
 -  assert (ssb->t);
 -
 -  for (i=0; i < cmd->n_variables; ++i)
 +  for (i = 0; i < proc->n_vars; i++)
      {
 -      struct group_statistics *gs = &group_proc_get (cmd->v_variables[i])->ugs;
 +      struct group_statistics *gs = &group_proc_get (proc->vars[i])->ugs;
  
 -      tab_text (ssb->t, 0, i+1, TAB_LEFT, var_get_name (cmd->v_variables[i]));
 -      tab_double (ssb->t,1, i+1, TAB_RIGHT, gs->n, wfmt);
 -      tab_double (ssb->t,2, i+1, TAB_RIGHT, gs->mean, NULL);
 -      tab_double (ssb->t,3, i+1, TAB_RIGHT, gs->std_dev, NULL);
 -      tab_double (ssb->t,4, i+1, TAB_RIGHT, gs->se_mean, NULL);
 +      tab_text (ssb->t, 0, i + 1, TAB_LEFT, var_get_name (proc->vars[i]));
 +      tab_double (ssb->t, 1, i + 1, TAB_RIGHT, gs->n, &proc->weight_format);
 +      tab_double (ssb->t, 2, i + 1, TAB_RIGHT, gs->mean, NULL);
 +      tab_double (ssb->t, 3, i + 1, TAB_RIGHT, gs->std_dev, NULL);
 +      tab_double (ssb->t, 4, i + 1, TAB_RIGHT, gs->se_mean, NULL);
      }
  }
 -
 -
 -
 +\f
  /* Implementation of the Test Results box struct */
  
 -void trbox_base_init (struct trbox *self,size_t n_vars, int cols);
 -void trbox_base_finalize (struct trbox *trb);
 -
 -void trbox_independent_samples_init (struct trbox *trb,
 -                                  struct cmd_t_test *cmd );
 -
 -static void trbox_independent_samples_populate (struct trbox *trb,
 -                                       const struct dictionary *dict,
 -                                       struct cmd_t_test *cmd);
 -
 -void trbox_one_sample_init (struct trbox *self,
 -                    struct cmd_t_test *cmd );
 -
 -static void trbox_one_sample_populate (struct trbox *trb,
 -                              const struct dictionary *,
 -                              struct cmd_t_test *cmd);
 -
 -void trbox_paired_init (struct trbox *self,
 -                     struct cmd_t_test *cmd );
 -
 -static void trbox_paired_populate (struct trbox *trb,
 -                                 const struct dictionary *,
 -                                 struct cmd_t_test *cmd);
 -
 -
 +static void trbox_base_init (struct trbox *, size_t n_vars, int cols);
 +static void trbox_base_finalize (struct trbox *);
 +static void trbox_independent_samples_init (struct trbox *,
 +                                            struct t_test_proc *);
 +static void trbox_independent_samples_populate (struct trbox *,
 +                                                struct t_test_proc *);
 +static void trbox_one_sample_init (struct trbox *, struct t_test_proc *);
 +static void trbox_one_sample_populate (struct trbox *, struct t_test_proc *);
 +static void trbox_paired_init (struct trbox *, struct t_test_proc *);
 +static void trbox_paired_populate (struct trbox *, struct t_test_proc *);
  
  /* Create a trbox according to mode*/
 -void
 -trbox_create (struct trbox *trb,
 -           struct cmd_t_test *cmd, int mode)
 +static void
 +trbox_create (struct trbox *trb, struct t_test_proc *proc)
  {
 -    switch (mode)
 -      {
 -      case T_1_SAMPLE:
 -      trbox_one_sample_init (trb,cmd);
 -      break;
 -      case T_IND_SAMPLES:
 -      trbox_independent_samples_init (trb,cmd);
 -      break;
 -      case T_PAIRED:
 -      trbox_paired_init (trb,cmd);
 -      break;
 -      default:
 -        NOT_REACHED ();
 -      }
 +  switch (proc->mode)
 +    {
 +    case T_1_SAMPLE:
 +      trbox_one_sample_init (trb, proc);
 +      break;
 +    case T_IND_SAMPLES:
 +      trbox_independent_samples_init (trb, proc);
 +      break;
 +    case T_PAIRED:
 +      trbox_paired_init (trb, proc);
 +      break;
 +    default:
 +      NOT_REACHED ();
 +    }
  }
  
 -/* Populate a trbox according to cmd */
 +/* Populate a trbox according to proc */
  static void
 -trbox_populate (struct trbox *trb, const struct dictionary *dict,
 -              struct cmd_t_test *cmd)
 +trbox_populate (struct trbox *trb, struct t_test_proc *proc)
  {
 -  trb->populate (trb, dict, cmd);
 +  trb->populate (trb, proc);
  }
  
  /* Submit and destroy a trbox */
 -void
 +static void
  trbox_finalize (struct trbox *trb)
  {
    trb->finalize (trb);
  }
  
  /* Initialize the independent samples trbox */
 -void
 +static void
  trbox_independent_samples_init (struct trbox *self,
 -                         struct cmd_t_test *cmd UNUSED)
 +                                struct t_test_proc *proc)
  {
 -  const int hsize=11;
 -  const int vsize=cmd->n_variables*2+3;
 +  const int hsize = 11;
 +  const int vsize = proc->n_vars * 2 + 3;
  
    assert (self);
    self->populate = trbox_independent_samples_populate;
  
 -  trbox_base_init (self,cmd->n_variables*2,hsize);
 -  tab_title (self->t,("Independent Samples Test"));
 -  tab_hline (self->t,TAL_1,2,hsize-1,1);
 -  tab_vline (self->t,TAL_2,2,0,vsize-1);
 -  tab_vline (self->t,TAL_1,4,0,vsize-1);
 -  tab_box (self->t,-1,-1,-1,TAL_1, 2,1,hsize-2,vsize-1);
 -  tab_hline (self->t,TAL_1, hsize-2,hsize-1,2);
 -  tab_box (self->t,-1,-1,-1,TAL_1, hsize-2,2,hsize-1,vsize-1);
 +  trbox_base_init (self, proc->n_vars * 2, hsize);
 +  tab_title (self->t, _("Independent Samples Test"));
 +  tab_hline (self->t, TAL_1, 2, hsize - 1, 1);
 +  tab_vline (self->t, TAL_2, 2, 0, vsize - 1);
 +  tab_vline (self->t, TAL_1, 4, 0, vsize - 1);
 +  tab_box (self->t, -1, -1, -1, TAL_1, 2, 1, hsize - 2, vsize - 1);
 +  tab_hline (self->t, TAL_1, hsize - 2, hsize - 1, 2);
 +  tab_box (self->t, -1, -1, -1, TAL_1, hsize - 2, 2, hsize - 1, vsize - 1);
    tab_joint_text (self->t, 2, 0, 3, 0,
 -               TAB_CENTER,_ ("Levene's Test for Equality of Variances"));
 -  tab_joint_text (self->t, 4,0,hsize-1,0,
 -               TAB_CENTER,_ ("t-test for Equality of Means"));
 -
 -  tab_text (self->t,2,2, TAB_CENTER | TAT_TITLE,_ ("F"));
 -  tab_text (self->t,3,2, TAB_CENTER | TAT_TITLE,_ ("Sig."));
 -  tab_text (self->t,4,2, TAB_CENTER | TAT_TITLE,_ ("t"));
 -  tab_text (self->t,5,2, TAB_CENTER | TAT_TITLE,_ ("df"));
 -  tab_text (self->t,6,2, TAB_CENTER | TAT_TITLE,_ ("Sig. (2-tailed)"));
 -  tab_text (self->t,7,2, TAB_CENTER | TAT_TITLE,_ ("Mean Difference"));
 -  tab_text (self->t,8,2, TAB_CENTER | TAT_TITLE,_ ("Std. Error Difference"));
 -  tab_text (self->t,9,2, TAB_CENTER | TAT_TITLE,_ ("Lower"));
 -  tab_text (self->t,10,2, TAB_CENTER | TAT_TITLE,_ ("Upper"));
 -
 -  tab_joint_text (self->t, 9, 1, 10, 1, TAB_CENTER | TAT_PRINTF,
 -               _ ("%g%% Confidence Interval of the Difference"),
 -               cmd->criteria*100.0);
 -
 +                  TAB_CENTER, _("Levene's Test for Equality of Variances"));
 +  tab_joint_text (self->t, 4, 0, hsize- 1, 0,
 +                  TAB_CENTER, _("t-test for Equality of Means"));
 +
 +  tab_text (self->t, 2, 2, TAB_CENTER | TAT_TITLE, _("F"));
 +  tab_text (self->t, 3, 2, TAB_CENTER | TAT_TITLE, _("Sig."));
 +  tab_text (self->t, 4, 2, TAB_CENTER | TAT_TITLE, _("t"));
 +  tab_text (self->t, 5, 2, TAB_CENTER | TAT_TITLE, _("df"));
 +  tab_text (self->t, 6, 2, TAB_CENTER | TAT_TITLE, _("Sig. (2-tailed)"));
 +  tab_text (self->t, 7, 2, TAB_CENTER | TAT_TITLE, _("Mean Difference"));
 +  tab_text (self->t, 8, 2, TAB_CENTER | TAT_TITLE, _("Std. Error Difference"));
 +  tab_text (self->t, 9, 2, TAB_CENTER | TAT_TITLE, _("Lower"));
 +  tab_text (self->t, 10, 2, TAB_CENTER | TAT_TITLE, _("Upper"));
 +
 +  tab_joint_text_format (self->t, 9, 1, 10, 1, TAB_CENTER,
 +                         _("%g%% Confidence Interval of the Difference"),
 +                         proc->criteria * 100.0);
  }
  
  /* Populate the independent samples trbox */
  static void
  trbox_independent_samples_populate (struct trbox *self,
 -                                  const struct dictionary *dict UNUSED,
 -                                  struct cmd_t_test *cmd)
 +                                    struct t_test_proc *proc)
  {
    int i;
  
 -  assert (self);
 -  for (i=0; i < cmd->n_variables; ++i)
 +  for (i = 0; i < proc->n_vars; i++)
      {
 -      double p,q;
 +      double p, q;
  
        double t;
        double df;
        double std_err_diff;
        double mean_diff;
  
 -      const struct variable *var = cmd->v_variables[i];
 +      double se2;
 +
 +      const struct variable *var = proc->vars[i];
        struct group_proc *grp_data = group_proc_get (var);
  
        struct hsh_table *grp_hash = grp_data->group_hash;
  
 -      struct group_statistics *gs0 ;
 -      struct group_statistics *gs1 ;
 +      struct group_statistics *gs0;
 +      struct group_statistics *gs1;
  
        union value search_val;
  
 -      if ( gp.criterion == CMP_LE )
 -      search_val.f = gp.v.critical_value - 1.0;
 +      if (proc->criterion == CMP_LE)
 +      search_val.f = proc->critical_value - 1.0;
        else
 -      search_val = gp.v.g_value[0];
 +      search_val = proc->g_value[0];
  
 -      gs0 = hsh_find (grp_hash, (void *) &search_val);
 +      gs0 = hsh_find (grp_hash, &search_val);
        assert (gs0);
  
 -      if ( gp.criterion == CMP_LE )
 -      search_val.f = gp.v.critical_value + 1.0;
 +      if (proc->criterion == CMP_LE)
 +      search_val.f = proc->critical_value + 1.0;
        else
 -      search_val = gp.v.g_value[1];
 +      search_val = proc->g_value[1];
  
 -      gs1 = hsh_find (grp_hash, (void *) &search_val);
 +      gs1 = hsh_find (grp_hash, &search_val);
        assert (gs1);
  
  
 -      tab_text (self->t, 0, i*2+3, TAB_LEFT, var_get_name (cmd->v_variables[i]));
 -
 -      tab_text (self->t, 1, i*2+3, TAB_LEFT, _ ("Equal variances assumed"));
 -
 -
 -      tab_double (self->t, 2, i*2+3, TAB_CENTER, grp_data->levene, NULL);
 +      tab_text (self->t, 0, i * 2 + 3, TAB_LEFT, var_get_name (proc->vars[i]));
 +      tab_text (self->t, 1, i * 2 + 3, TAB_LEFT, _("Equal variances assumed"));
 +      tab_double (self->t, 2, i * 2 + 3, TAB_CENTER, grp_data->levene, NULL);
  
        /* Now work out the significance of the Levene test */
 -      df1 = 1; df2 = grp_data->ugs.n - 2;
 +      df1 = 1;
 +      df2 = grp_data->ugs.n - 2;
        q = gsl_cdf_fdist_Q (grp_data->levene, df1, df2);
 +      tab_double (self->t, 3, i * 2 + 3, TAB_CENTER, q, NULL);
  
 -      tab_double (self->t, 3, i*2+3, TAB_CENTER, q, NULL);
 -
 -      df = gs0->n + gs1->n - 2.0 ;
 -      tab_double (self->t, 5, i*2+3, TAB_RIGHT, df, NULL);
 +      df = gs0->n + gs1->n - 2.0;
 +      tab_double (self->t, 5, i * 2 + 3, TAB_RIGHT, df, NULL);
  
 -      pooled_variance = ( (gs0->n )*pow2 (gs0->s_std_dev)
 -                        +
 -                        (gs1->n )*pow2 (gs1->s_std_dev)
 -                      ) / df  ;
 +      pooled_variance = (gs0->n * pow2 (gs0->s_std_dev)
 +                         + gs1->n *pow2 (gs1->s_std_dev)) / df ;
  
 -      t = (gs0->mean - gs1->mean) / sqrt (pooled_variance) ;
 -      t /= sqrt ((gs0->n + gs1->n)/ (gs0->n*gs1->n));
 +      t = (gs0->mean - gs1->mean) / sqrt (pooled_variance);
 +      t /= sqrt ((gs0->n + gs1->n) / (gs0->n * gs1->n));
  
 -      tab_double (self->t, 4, i*2+3, TAB_RIGHT, t, NULL);
 +      tab_double (self->t, 4, i * 2 + 3, TAB_RIGHT, t, NULL);
  
        p = gsl_cdf_tdist_P (t, df);
        q = gsl_cdf_tdist_Q (t, df);
  
 -      tab_double (self->t, 6, i*2+3, TAB_RIGHT, 2.0* (t>0?q:p), NULL);
 +      tab_double (self->t, 6, i * 2 + 3, TAB_RIGHT, 2.0 * (t > 0 ? q : p),
 +                  NULL);
  
        mean_diff = gs0->mean - gs1->mean;
 -      tab_double (self->t, 7, i*2+3, TAB_RIGHT, mean_diff, NULL);
 +      tab_double (self->t, 7, i * 2 + 3, TAB_RIGHT, mean_diff, NULL);
  
  
 -      std_err_diff = sqrt ( pow2 (gs0->se_mean) + pow2 (gs1->se_mean));
 -      tab_double (self->t, 8, i*2+3, TAB_RIGHT, std_err_diff, NULL);
 -
 +      std_err_diff = sqrt (pow2 (gs0->se_mean) + pow2 (gs1->se_mean));
 +      tab_double (self->t, 8, i * 2 + 3, TAB_RIGHT, std_err_diff, NULL);
  
        /* Now work out the confidence interval */
 -      q = (1 - cmd->criteria)/2.0;  /* 2-tailed test */
 +      q = (1 - proc->criteria)/2.0;  /* 2-tailed test */
  
 -      t = gsl_cdf_tdist_Qinv (q,df);
 -      tab_double (self->t, 9, i*2+3, TAB_RIGHT,
 -              mean_diff - t * std_err_diff, NULL);
 +      t = gsl_cdf_tdist_Qinv (q, df);
 +      tab_double (self->t, 9, i * 2 + 3, TAB_RIGHT,
 +                 mean_diff - t * std_err_diff, NULL);
  
 -      tab_double (self->t, 10, i*2+3, TAB_RIGHT,
 -              mean_diff + t * std_err_diff, NULL);
 +      tab_double (self->t, 10, i * 2 + 3, TAB_RIGHT,
 +                 mean_diff + t * std_err_diff, NULL);
  
  
 -      {
 -      double se2;
        /* Now for the \sigma_1 != \sigma_2 case */
 -      tab_text (self->t, 1, i*2+3+1,
 -              TAB_LEFT, _ ("Equal variances not assumed"));
 -
 -
 -      se2 = (pow2 (gs0->s_std_dev)/ (gs0->n -1) ) +
 -       (pow2 (gs1->s_std_dev)/ (gs1->n -1) );
 +      tab_text (self->t, 1, i * 2 + 3 + 1,
 +                TAB_LEFT, _("Equal variances not assumed"));
  
 -      t = mean_diff / sqrt (se2) ;
 -      tab_double (self->t, 4, i*2+3+1, TAB_RIGHT, t, NULL);
 +      se2 = ((pow2 (gs0->s_std_dev) / (gs0->n - 1)) +
 +             (pow2 (gs1->s_std_dev) / (gs1->n - 1)));
  
 -      df = pow2 (se2) / (
 -                     (pow2 (pow2 (gs0->s_std_dev)/ (gs0->n - 1 ))
 -                      / (gs0->n -1 )
 -                      )
 -                     +
 -                     (pow2 (pow2 (gs1->s_std_dev)/ (gs1->n - 1 ))
 -                      / (gs1->n -1 )
 -                      )
 -                     ) ;
 +      t = mean_diff / sqrt (se2);
 +      tab_double (self->t, 4, i * 2 + 3 + 1, TAB_RIGHT, t, NULL);
  
 -      tab_double (self->t, 5, i*2+3+1, TAB_RIGHT, df, NULL);
 +      df = pow2 (se2) / ((pow2 (pow2 (gs0->s_std_dev) / (gs0->n - 1))
 +                          / (gs0->n - 1))
 +                         + (pow2 (pow2 (gs1->s_std_dev) / (gs1->n - 1))
 +                            / (gs1->n - 1)));
 +      tab_double (self->t, 5, i * 2 + 3 + 1, TAB_RIGHT, df, NULL);
  
        p = gsl_cdf_tdist_P (t, df);
        q = gsl_cdf_tdist_Q (t, df);
  
 -      tab_double (self->t, 6, i*2+3+1, TAB_RIGHT, 2.0* (t>0?q:p), NULL);
 +      tab_double (self->t, 6, i * 2 + 3 + 1, TAB_RIGHT, 2.0 * (t > 0 ? q : p),
 +                  NULL);
  
        /* Now work out the confidence interval */
 -      q = (1 - cmd->criteria)/2.0;  /* 2-tailed test */
 +      q = (1 - proc->criteria) / 2.0;  /* 2-tailed test */
  
        t = gsl_cdf_tdist_Qinv (q, df);
  
 -      tab_double (self->t, 7, i*2+3+1, TAB_RIGHT, mean_diff, NULL);
 -
 -
 -      tab_double (self->t, 8, i*2+3+1, TAB_RIGHT, std_err_diff, NULL);
 -
 -
 -      tab_double (self->t, 9, i*2+3+1, TAB_RIGHT,
 -              mean_diff - t * std_err_diff, NULL);
 -
 -      tab_double (self->t, 10, i*2+3+1, TAB_RIGHT,
 -              mean_diff + t * std_err_diff, NULL);
 -      }
 +      tab_double (self->t, 7, i * 2 + 3 + 1, TAB_RIGHT, mean_diff, NULL);
 +      tab_double (self->t, 8, i * 2 + 3 + 1, TAB_RIGHT, std_err_diff, NULL);
 +      tab_double (self->t, 9, i * 2 + 3 + 1, TAB_RIGHT,
 +                 mean_diff - t * std_err_diff, NULL);
 +      tab_double (self->t, 10, i * 2 + 3 + 1, TAB_RIGHT,
 +                 mean_diff + t * std_err_diff, NULL);
      }
  }
  
  /* Initialize the paired samples trbox */
 -void
 -trbox_paired_init (struct trbox *self,
 -                         struct cmd_t_test *cmd UNUSED)
 +static void
 +trbox_paired_init (struct trbox *self, struct t_test_proc *proc)
  {
 -
    const int hsize=10;
 -  const int vsize=n_pairs+3;
 +  const int vsize=proc->n_pairs+ 3;
  
    self->populate = trbox_paired_populate;
  
 -  trbox_base_init (self,n_pairs,hsize);
 -  tab_title (self->t, _ ("Paired Samples Test"));
 -  tab_hline (self->t,TAL_1,2,6,1);
 -  tab_vline (self->t,TAL_2,2,0,vsize - 1);
 -  tab_joint_text (self->t,2,0,6,0,TAB_CENTER,_ ("Paired Differences"));
 -  tab_box (self->t,-1,-1,-1,TAL_1, 2,1,6,vsize-1);
 -  tab_box (self->t,-1,-1,-1,TAL_1, 6,0,hsize-1,vsize-1);
 -  tab_hline (self->t,TAL_1,5,6, 2);
 -  tab_vline (self->t,TAL_GAP,6,0,1);
 -
 -  tab_joint_text (self->t, 5, 1, 6, 1, TAB_CENTER | TAT_PRINTF,
 -               ("%g%% Confidence Interval of the Difference"),
 -               cmd->criteria*100.0);
 -
 -  tab_text (self->t, 2, 2, TAB_CENTER | TAT_TITLE, _ ("Mean"));
 -  tab_text (self->t, 3, 2, TAB_CENTER | TAT_TITLE, _ ("Std. Deviation"));
 -  tab_text (self->t, 4, 2, TAB_CENTER | TAT_TITLE, _ ("Std. Error Mean"));
 -  tab_text (self->t, 5, 2, TAB_CENTER | TAT_TITLE, _ ("Lower"));
 -  tab_text (self->t, 6, 2, TAB_CENTER | TAT_TITLE, _ ("Upper"));
 -  tab_text (self->t, 7, 2, TAB_CENTER | TAT_TITLE, _ ("t"));
 -  tab_text (self->t, 8, 2, TAB_CENTER | TAT_TITLE, _ ("df"));
 -  tab_text (self->t, 9, 2, TAB_CENTER | TAT_TITLE, _ ("Sig. (2-tailed)"));
 +  trbox_base_init (self, proc->n_pairs, hsize);
 +  tab_title (self->t, _("Paired Samples Test"));
 +  tab_hline (self->t, TAL_1, 2, 6, 1);
 +  tab_vline (self->t, TAL_2, 2, 0, vsize - 1);
 +  tab_joint_text (self->t, 2, 0, 6, 0, TAB_CENTER, _("Paired Differences"));
 +  tab_box (self->t, -1, -1, -1, TAL_1, 2, 1, 6, vsize - 1);
 +  tab_box (self->t, -1, -1, -1, TAL_1, 6, 0, hsize - 1, vsize - 1);
 +  tab_hline (self->t, TAL_1, 5, 6, 2);
 +  tab_vline (self->t, TAL_GAP, 6, 0, 1);
 +
 +  tab_joint_text_format (self->t, 5, 1, 6, 1, TAB_CENTER,
 +                         _("%g%% Confidence Interval of the Difference"),
 +                         proc->criteria*100.0);
 +
 +  tab_text (self->t, 2, 2, TAB_CENTER | TAT_TITLE, _("Mean"));
 +  tab_text (self->t, 3, 2, TAB_CENTER | TAT_TITLE, _("Std. Deviation"));
 +  tab_text (self->t, 4, 2, TAB_CENTER | TAT_TITLE, _("Std. Error Mean"));
 +  tab_text (self->t, 5, 2, TAB_CENTER | TAT_TITLE, _("Lower"));
 +  tab_text (self->t, 6, 2, TAB_CENTER | TAT_TITLE, _("Upper"));
 +  tab_text (self->t, 7, 2, TAB_CENTER | TAT_TITLE, _("t"));
 +  tab_text (self->t, 8, 2, TAB_CENTER | TAT_TITLE, _("df"));
 +  tab_text (self->t, 9, 2, TAB_CENTER | TAT_TITLE, _("Sig. (2-tailed)"));
  }
  
  /* Populate the paired samples trbox */
  static void
  trbox_paired_populate (struct trbox *trb,
 -                     const struct dictionary *dict,
 -                     struct cmd_t_test *cmd UNUSED)
 +                       struct t_test_proc *proc)
  {
    int i;
  
 -  const struct variable *wv = dict_get_weight (dict);
 -  const struct fmt_spec *wfmt = wv ? var_get_print_format (wv) : &F_8_0;
 -
 -  for (i=0; i < n_pairs; ++i)
 +  for (i = 0; i < proc->n_pairs; i++)
      {
 -      double p,q;
 +      struct pair *pair = &proc->pairs[i];
 +      double p, q;
        double se_mean;
  
 -      double n = pairs[i].n;
 +      double n = pair->n;
        double t;
        double df = n - 1;
  
 -      tab_text (trb->t, 0, i+3, TAB_LEFT | TAT_PRINTF, _ ("Pair %d"),i);
 -
 -      tab_text (trb->t, 1, i+3, TAB_LEFT | TAT_PRINTF, "%s - %s",
 -              var_get_name (pairs[i].v[0]),
 -                var_get_name (pairs[i].v[1]));
 -
 -      tab_double (trb->t, 2, i+3, TAB_RIGHT, pairs[i].mean_diff, NULL);
 -
 -      tab_double (trb->t, 3, i+3, TAB_RIGHT, pairs[i].std_dev_diff, NULL);
 +      tab_text_format (trb->t, 0, i + 3, TAB_LEFT, _("Pair %d"), i);
 +      tab_text_format (trb->t, 1, i + 3, TAB_LEFT, "%s - %s",
 +                       var_get_name (pair->v[0]),
 +                       var_get_name (pair->v[1]));
 +      tab_double (trb->t, 2, i + 3, TAB_RIGHT, pair->mean_diff, NULL);
 +      tab_double (trb->t, 3, i + 3, TAB_RIGHT, pair->std_dev_diff, NULL);
  
        /* SE Mean */
 -      se_mean = pairs[i].std_dev_diff / sqrt (n) ;
 -      tab_double (trb->t, 4, i+3, TAB_RIGHT, se_mean, NULL);
 +      se_mean = pair->std_dev_diff / sqrt (n);
 +      tab_double (trb->t, 4, i + 3, TAB_RIGHT, se_mean, NULL);
  
        /* Now work out the confidence interval */
 -      q = (1 - cmd->criteria)/2.0;  /* 2-tailed test */
 +      q = (1 - proc->criteria) / 2.0;  /* 2-tailed test */
  
        t = gsl_cdf_tdist_Qinv (q, df);
  
 -      tab_double (trb->t, 5, i+3, TAB_RIGHT,
 -              pairs[i].mean_diff - t * se_mean , NULL);
 -
 -      tab_double (trb->t, 6, i+3, TAB_RIGHT,
 -              pairs[i].mean_diff + t * se_mean , NULL);
 +      tab_double (trb->t, 5, i + 3, TAB_RIGHT,
 +                 pair->mean_diff - t * se_mean, NULL);
 +      tab_double (trb->t, 6, i + 3, TAB_RIGHT,
 +                 pair->mean_diff + t * se_mean, NULL);
  
 -      t = (pairs[i].mean[0] - pairs[i].mean[1])
 -      / sqrt (
 -              ( pow2 (pairs[i].s_std_dev[0]) + pow2 (pairs[i].s_std_dev[1]) -
 -                2 * pairs[i].correlation *
 -                pairs[i].s_std_dev[0] * pairs[i].s_std_dev[1] )
 -              / (n - 1)
 -              );
 +      t = ((pair->mean[0] - pair->mean[1])
 +           / sqrt ((pow2 (pair->s_std_dev[0]) + pow2 (pair->s_std_dev[1])
 +                    - (2 * pair->correlation
 +                       * pair->s_std_dev[0] * pair->s_std_dev[1]))
 +                   / (n - 1)));
  
 -      tab_double (trb->t, 7, i+3, TAB_RIGHT, t, NULL);
 +      tab_double (trb->t, 7, i + 3, TAB_RIGHT, t, NULL);
  
        /* Degrees of freedom */
 -      tab_double (trb->t, 8, i+3, TAB_RIGHT, df, wfmt);
 +      tab_double (trb->t, 8, i + 3, TAB_RIGHT, df, &proc->weight_format);
  
        p = gsl_cdf_tdist_P (t,df);
        q = gsl_cdf_tdist_Q (t,df);
  
 -      tab_double (trb->t, 9, i+3, TAB_RIGHT, 2.0* (t>0?q:p), NULL);
 -
 +      tab_double (trb->t, 9, i + 3, TAB_RIGHT, 2.0 * (t > 0 ? q : p), NULL);
      }
  }
  
  /* Initialize the one sample trbox */
 -void
 -trbox_one_sample_init (struct trbox *self, struct cmd_t_test *cmd )
 +static void
 +trbox_one_sample_init (struct trbox *self, struct t_test_proc *proc)
  {
 -  const int hsize=7;
 -  const int vsize=cmd->n_variables+3;
 +  const int hsize = 7;
 +  const int vsize = proc->n_vars + 3;
  
    self->populate = trbox_one_sample_populate;
  
 -  trbox_base_init (self, cmd->n_variables,hsize);
 -  tab_title (self->t, _ ("One-Sample Test"));
 +  trbox_base_init (self, proc->n_vars, hsize);
 +  tab_title (self->t, _("One-Sample Test"));
    tab_hline (self->t, TAL_1, 1, hsize - 1, 1);
    tab_vline (self->t, TAL_2, 1, 0, vsize - 1);
  
 -  tab_joint_text (self->t, 1, 0, hsize-1,0, TAB_CENTER | TAT_PRINTF,
 -               _ ("Test Value = %f"), cmd->n_testval[0]);
 +  tab_joint_text_format (self->t, 1, 0, hsize - 1, 0, TAB_CENTER,
 +                         _("Test Value = %f"), proc->testval);
  
 -  tab_box (self->t, -1, -1, -1, TAL_1, 1,1,hsize-1,vsize-1);
 +  tab_box (self->t, -1, -1, -1, TAL_1, 1, 1, hsize - 1, vsize - 1);
  
  
 -  tab_joint_text (self->t,5,1,6,1,TAB_CENTER  | TAT_PRINTF,
 -               _ ("%g%% Confidence Interval of the Difference"),
 -               cmd->criteria*100.0);
 -
 -  tab_vline (self->t,TAL_GAP,6,1,1);
 -  tab_hline (self->t,TAL_1,5,6,2);
 -  tab_text (self->t, 1, 2, TAB_CENTER | TAT_TITLE, _ ("t"));
 -  tab_text (self->t, 2, 2, TAB_CENTER | TAT_TITLE, _ ("df"));
 -  tab_text (self->t, 3, 2, TAB_CENTER | TAT_TITLE, _ ("Sig. (2-tailed)"));
 -  tab_text (self->t, 4, 2, TAB_CENTER | TAT_TITLE, _ ("Mean Difference"));
 -  tab_text (self->t, 5, 2, TAB_CENTER | TAT_TITLE, _ ("Lower"));
 -  tab_text (self->t, 6, 2, TAB_CENTER | TAT_TITLE, _ ("Upper"));
 +  tab_joint_text_format (self->t, 5, 1, 6, 1, TAB_CENTER,
 +                         _("%g%% Confidence Interval of the Difference"),
 +                         proc->criteria * 100.0);
  
 +  tab_vline (self->t, TAL_GAP, 6, 1, 1);
 +  tab_hline (self->t, TAL_1, 5, 6, 2);
 +  tab_text (self->t, 1, 2, TAB_CENTER | TAT_TITLE, _("t"));
 +  tab_text (self->t, 2, 2, TAB_CENTER | TAT_TITLE, _("df"));
 +  tab_text (self->t, 3, 2, TAB_CENTER | TAT_TITLE, _("Sig. (2-tailed)"));
 +  tab_text (self->t, 4, 2, TAB_CENTER | TAT_TITLE, _("Mean Difference"));
 +  tab_text (self->t, 5, 2, TAB_CENTER | TAT_TITLE, _("Lower"));
 +  tab_text (self->t, 6, 2, TAB_CENTER | TAT_TITLE, _("Upper"));
  }
  
 -
  /* Populate the one sample trbox */
  static void
 -trbox_one_sample_populate (struct trbox *trb,
 -                         const struct dictionary *dict,
 -                         struct cmd_t_test *cmd)
 +trbox_one_sample_populate (struct trbox *trb, struct t_test_proc *proc)
  {
    int i;
  
 -  const struct variable *wv = dict_get_weight (dict);
 -  const struct fmt_spec *wfmt = wv ? var_get_print_format (wv) : &F_8_0;
 -
    assert (trb->t);
  
 -  for (i=0; i < cmd->n_variables; ++i)
 +  for (i = 0; i < proc->n_vars; i++)
      {
        double t;
 -      double p,q;
 +      double p, q;
        double df;
 -      struct group_statistics *gs = &group_proc_get (cmd->v_variables[i])->ugs;
 +      struct group_statistics *gs = &group_proc_get (proc->vars[i])->ugs;
  
 +      tab_text (trb->t, 0, i + 3, TAB_LEFT, var_get_name (proc->vars[i]));
  
 -      tab_text (trb->t, 0, i+3, TAB_LEFT, var_get_name (cmd->v_variables[i]));
 +      t = (gs->mean - proc->testval) * sqrt (gs->n) / gs->std_dev;
  
 -      t = (gs->mean - cmd->n_testval[0] ) * sqrt (gs->n) / gs->std_dev ;
 -
 -      tab_double (trb->t, 1, i+3, TAB_RIGHT, t, NULL);
 +      tab_double (trb->t, 1, i + 3, TAB_RIGHT, t, NULL);
  
        /* degrees of freedom */
        df = gs->n - 1;
  
 -      tab_double (trb->t, 2, i+3, TAB_RIGHT, df, wfmt);
 +      tab_double (trb->t, 2, i + 3, TAB_RIGHT, df, &proc->weight_format);
  
        p = gsl_cdf_tdist_P (t, df);
        q = gsl_cdf_tdist_Q (t, df);
  
        /* Multiply by 2 to get 2-tailed significance, makeing sure we've got
         the correct tail*/
 -      tab_double (trb->t, 3, i+3, TAB_RIGHT, 2.0* (t>0?q:p), NULL);
 -
 -      tab_double (trb->t, 4, i+3, TAB_RIGHT, gs->mean_diff, NULL);
 +      tab_double (trb->t, 3, i + 3, TAB_RIGHT, 2.0 * (t > 0 ? q : p), NULL);
 +      tab_double (trb->t, 4, i + 3, TAB_RIGHT, gs->mean_diff, NULL);
  
  
 -      q = (1 - cmd->criteria)/2.0;  /* 2-tailed test */
 +      q = (1 - proc->criteria) / 2.0;  /* 2-tailed test */
        t = gsl_cdf_tdist_Qinv (q, df);
  
 -      tab_double (trb->t, 5, i+3, TAB_RIGHT,
 +      tab_double (trb->t, 5, i + 3, TAB_RIGHT,
                 gs->mean_diff - t * gs->se_mean, NULL);
 -
 -      tab_double (trb->t, 6, i+3, TAB_RIGHT,
 +      tab_double (trb->t, 6, i + 3, TAB_RIGHT,
                 gs->mean_diff + t * gs->se_mean, NULL);
      }
  }
  
  /* Base initializer for the generalized trbox */
 -void
 +static void
  trbox_base_init (struct trbox *self, size_t data_rows, int cols)
  {
    const size_t rows = 3 + data_rows;
  
    self->finalize = trbox_base_finalize;
    self->t = tab_create (cols, rows, 0);
 -  tab_headers (self->t,0,0,3,0);
 -  tab_box (self->t, TAL_2, TAL_2, TAL_0, TAL_0, 0, 0, cols -1, rows -1);
 -  tab_hline (self->t, TAL_2,0,cols-1,3);
 -  tab_dim (self->t, tab_natural_dimensions);
 +  tab_headers (self->t, 0, 0, 3, 0);
 +  tab_box (self->t, TAL_2, TAL_2, TAL_0, TAL_0, 0, 0, cols - 1, rows - 1);
 +  tab_hline (self->t, TAL_2, 0, cols- 1, 3);
 +  tab_dim (self->t, tab_natural_dimensions, NULL);
  }
  
 -
  /* Base finalizer for the trbox */
 -void
 +static void
  trbox_base_finalize (struct trbox *trb)
  {
    tab_submit (trb->t);
  }
  
 -
 -/* Create , populate and submit the Paired Samples Correlation box */
 +/* Create, populate and submit the Paired Samples Correlation box */
  static void
 -pscbox (const struct dictionary *dict)
 +pscbox (struct t_test_proc *proc)
  {
 -  const struct variable *wv = dict_get_weight (dict);
 -  const struct fmt_spec *wfmt = wv ? var_get_print_format (wv) : &F_8_0;
 -
 -  const int rows = 1 + n_pairs;
 -  const int cols = 5;
 +  const int rows=1+proc->n_pairs;
 +  const int cols=5;
    int i;
  
    struct tab_table *table;
  
 -  table = tab_create (cols,rows,0);
 +  table = tab_create (cols, rows, 0);
  
    tab_columns (table, SOM_COL_DOWN, 1);
 -  tab_headers (table,0,0,1,0);
 -  tab_box (table, TAL_2, TAL_2, TAL_0, TAL_1, 0, 0, cols -1, rows -1 );
 +  tab_headers (table, 0, 0, 1, 0);
 +  tab_box (table, TAL_2, TAL_2, TAL_0, TAL_1, 0, 0, cols - 1, rows - 1);
    tab_hline (table, TAL_2, 0, cols - 1, 1);
    tab_vline (table, TAL_2, 2, 0, rows - 1);
 -  tab_dim (table, tab_natural_dimensions);
 -  tab_title (table, _ ("Paired Samples Correlations"));
 +  tab_dim (table, tab_natural_dimensions, NULL);
 +  tab_title (table, _("Paired Samples Correlations"));
  
    /* column headings */
 -  tab_text (table, 2,0, TAB_CENTER | TAT_TITLE, _ ("N"));
 -  tab_text (table, 3,0, TAB_CENTER | TAT_TITLE, _ ("Correlation"));
 -  tab_text (table, 4,0, TAB_CENTER | TAT_TITLE, _ ("Sig."));
 +  tab_text (table, 2, 0, TAB_CENTER | TAT_TITLE, _("N"));
 +  tab_text (table, 3, 0, TAB_CENTER | TAT_TITLE, _("Correlation"));
 +  tab_text (table, 4, 0, TAB_CENTER | TAT_TITLE, _("Sig."));
  
 -  for (i=0; i < n_pairs; ++i)
 +  for (i = 0; i < proc->n_pairs; i++)
      {
 -      double p,q;
 -
 -      double df = pairs[i].n -2;
 +      struct pair *pair = &proc->pairs[i];
++      double df = pair->n - 2;
 +      double p, q;
-       double df = pair->n -2;
-       double correlation_t = (pair->correlation * sqrt (df) /
-                               sqrt (1 - pow2 (pair->correlation)));
+       /* corr2 will mathematically always be in the range [0, 1.0].  Inaccurate
+          calculations sometimes cause it to be slightly greater than 1.0, so
+          force it into the correct range to avoid NaN from sqrt(). */
 -      double corr2 = MIN (1.0, pow2 (pairs[i].correlation));
 -      double correlation_t =
 -      pairs[i].correlation * sqrt (df) /
 -      sqrt (1 - corr2);
 -
++      double corr2 = MIN (1.0, pow2 (pair->correlation));
++      double correlation_t = pair->correlation * sqrt (df) / sqrt (1 - corr2);
  
        /* row headings */
 -      tab_text (table, 0,i+1, TAB_LEFT | TAT_TITLE | TAT_PRINTF,
 -             _ ("Pair %d"), i);
 -
 -      tab_text (table, 1,i+1, TAB_LEFT | TAT_TITLE | TAT_PRINTF,
 -             _ ("%s & %s"),
 -               var_get_name (pairs[i].v[0]),
 -               var_get_name (pairs[i].v[1]));
 -
 +      tab_text_format (table, 0, i + 1, TAB_LEFT | TAT_TITLE,
 +                       _("Pair %d"), i);
 +      tab_text_format (table, 1, i + 1, TAB_LEFT | TAT_TITLE,
 +                       _("%s & %s"),
 +                       var_get_name (pair->v[0]),
 +                       var_get_name (pair->v[1]));
  
        /* row data */
 -      tab_double (table, 2, i+1, TAB_RIGHT, pairs[i].n, wfmt);
 -      tab_double (table, 3, i+1, TAB_RIGHT, pairs[i].correlation, NULL);
 +      tab_double (table, 2, i + 1, TAB_RIGHT, pair->n, &proc->weight_format);
 +      tab_double (table, 3, i + 1, TAB_RIGHT, pair->correlation, NULL);
  
        p = gsl_cdf_tdist_P (correlation_t, df);
        q = gsl_cdf_tdist_Q (correlation_t, df);
 -
 -      tab_double (table, 4, i+1, TAB_RIGHT, 2.0* (correlation_t>0?q:p), NULL);
 +      tab_double (table, 4, i + 1, TAB_RIGHT,
 +                 2.0 * (correlation_t > 0 ? q : p), NULL);
      }
  
    tab_submit (table);
  }
 -
 -
 -
 -
 +\f
  /* Calculation Implementation */
  
 -/* Per case calculations common to all variants of the T test */
 -static int
 +/* Calculations common to all variants of the T test. */
 +static void
  common_calc (const struct dictionary *dict,
 -           const struct ccase *c,
 -           void *_cmd,
 -           enum mv_class exclude)
 +             struct t_test_proc *proc,
 +             struct casereader *reader)
  {
 +  struct ccase *c;
    int i;
 -  struct cmd_t_test *cmd = (struct cmd_t_test *)_cmd;
 -
 -  double weight = dict_get_case_weight (dict, c, NULL);
 -
 -
 -  /* Listwise has to be implicit if the independent variable is missing ?? */
 -  if ( cmd->sbc_groups )
 -    {
 -      if (var_is_value_missing (indep_var, case_data (c, indep_var), exclude))
 -      return 0;
 -    }
  
 -  for (i = 0; i < cmd->n_variables ; ++i)
 +  for (i = 0; i < proc->n_vars; i++)
      {
 -      const struct variable *v = cmd->v_variables[i];
 -      const union value *val = case_data (c, v);
 -
 -      if (!var_is_value_missing (v, val, exclude))
 -      {
 -        struct group_statistics *gs;
 -        gs = &group_proc_get (v)->ugs;
 -
 -        gs->n += weight;
 -        gs->sum += weight * val->f;
 -        gs->ssq += weight * val->f * val->f;
 -      }
 +      struct group_statistics *gs = &group_proc_get (proc->vars[i])->ugs;
 +      gs->sum = 0;
 +      gs->n = 0;
 +      gs->ssq = 0;
 +      gs->sum_diff = 0;
      }
 -  return 0;
 -}
 -
 -/* Pre calculations common to all variants of the T test */
 -static void
 -common_precalc ( struct cmd_t_test *cmd )
 -{
 -  int i=0;
  
 -  for (i=0; i< cmd->n_variables ; ++i)
 +  for (; (c = casereader_read (reader)) != NULL; case_unref (c))
      {
 -      struct group_statistics *gs;
 -      gs= &group_proc_get (cmd->v_variables[i])->ugs;
 -
 -      gs->sum=0;
 -      gs->n=0;
 -      gs->ssq=0;
 -      gs->sum_diff=0;
 +      double weight = dict_get_case_weight (dict, c, NULL);
 +
 +      /* Listwise has to be implicit if the independent variable
 +         is missing ?? */
 +      if (proc->mode == T_IND_SAMPLES)
 +        {
 +          if (var_is_value_missing (proc->indep_var,
 +                                    case_data (c, proc->indep_var),
 +                                    proc->exclude))
 +            continue;
 +        }
 +
 +      for (i = 0; i < proc->n_vars; i++)
 +        {
 +          const struct variable *v = proc->vars[i];
 +          const union value *val = case_data (c, v);
 +
 +          if (!var_is_value_missing (v, val, proc->exclude))
 +            {
 +              struct group_statistics *gs;
 +              gs = &group_proc_get (v)->ugs;
 +
 +              gs->n += weight;
 +              gs->sum += weight * val->f;
 +              gs->ssq += weight * pow2 (val->f);
 +            }
 +        }
      }
 -}
 +  casereader_destroy (reader);
  
 -/* Post calculations common to all variants of the T test */
 -void
 -common_postcalc (struct cmd_t_test *cmd)
 -{
 -  int i=0;
 -
 -  for (i=0; i< cmd->n_variables ; ++i)
 +  for (i = 0; i < proc->n_vars; i++)
      {
 -      struct group_statistics *gs;
 -      gs= &group_proc_get (cmd->v_variables[i])->ugs;
 -
 -      gs->mean=gs->sum / gs->n;
 -      gs->s_std_dev= sqrt (
 -                       ( (gs->ssq / gs->n ) - gs->mean * gs->mean )
 -                       ) ;
 -
 -      gs->std_dev= sqrt (
 -                       gs->n/ (gs->n-1) *
 -                       ( (gs->ssq / gs->n ) - gs->mean * gs->mean )
 -                       ) ;
 +      struct group_statistics *gs = &group_proc_get (proc->vars[i])->ugs;
  
 +      gs->mean = gs->sum / gs->n;
 +      gs->s_std_dev = sqrt (((gs->ssq / gs->n) - pow2 (gs->mean)));
 +      gs->std_dev = sqrt (gs->n / (gs->n- 1)
 +                          * ((gs->ssq / gs->n) - pow2 (gs->mean)));
        gs->se_mean = gs->std_dev / sqrt (gs->n);
 -      gs->mean_diff= gs->sum_diff / gs->n;
 +      gs->mean_diff = gs->sum_diff / gs->n;
      }
  }
  
 -/* Per case calculations for one sample t test  */
 +/* Calculations for one sample T test. */
  static int
 -one_sample_calc (const struct dictionary *dict,
 -               const struct ccase *c, void *cmd_,
 -               enum mv_class exclude)
 +one_sample_calc (const struct dictionary *dict, struct t_test_proc *proc,
 +                 struct casereader *reader)
  {
 +  struct ccase *c;
    int i;
  
 -  struct cmd_t_test *cmd = (struct cmd_t_test *)cmd_;
 -
 -  double weight = dict_get_case_weight (dict, c, NULL);
 -
 -
 -  for (i=0; i< cmd->n_variables ; ++i)
 +  for (i = 0; i < proc->n_vars; i++)
      {
 -      struct group_statistics *gs;
 -      const struct variable *v = cmd->v_variables[i];
 -      const union value *val = case_data (c, v);
 -
 -      gs= &group_proc_get (cmd->v_variables[i])->ugs;
 -
 -      if (!var_is_value_missing (v, val, exclude))
 -      gs->sum_diff += weight * (val->f - cmd->n_testval[0]);
 +      struct group_statistics *gs = &group_proc_get (proc->vars[i])->ugs;
 +      gs->sum_diff = 0;
      }
  
 -  return 0;
 -}
 -
 -/* Pre calculations for one sample t test */
 -static void
 -one_sample_precalc ( struct cmd_t_test *cmd )
 -{
 -  int i=0;
 -
 -  for (i=0; i< cmd->n_variables ; ++i)
 +  for (; (c = casereader_read (reader)) != NULL; case_unref (c))
      {
 -      struct group_statistics *gs;
 -      gs= &group_proc_get (cmd->v_variables[i])->ugs;
 -
 -      gs->sum_diff=0;
 +      double weight = dict_get_case_weight (dict, c, NULL);
 +      for (i = 0; i < proc->n_vars; i++)
 +        {
 +          const struct variable *v = proc->vars[i];
 +          struct group_statistics *gs = &group_proc_get (v)->ugs;
 +          const union value *val = case_data (c, v);
 +          if (!var_is_value_missing (v, val, proc->exclude))
 +            gs->sum_diff += weight * (val->f - proc->testval);
 +        }
      }
 -}
  
 -/* Post calculations for one sample t test */
 -static void
 -one_sample_postcalc (struct cmd_t_test *cmd)
 -{
 -  int i=0;
 -
 -  for (i=0; i< cmd->n_variables ; ++i)
 +  for (i = 0; i < proc->n_vars; i++)
      {
 -      struct group_statistics *gs;
 -      gs= &group_proc_get (cmd->v_variables[i])->ugs;
 -
 -      gs->mean_diff = gs->sum_diff / gs->n ;
 +      struct group_statistics *gs = &group_proc_get (proc->vars[i])->ugs;
 +      gs->mean_diff = gs->sum_diff / gs->n;
      }
 -}
 -
  
 +  casereader_destroy (reader);
  
 -static void
 -paired_precalc (struct cmd_t_test *cmd UNUSED)
 -{
 -  int i;
 -
 -  for (i=0; i < n_pairs ; ++i )
 -    {
 -      pairs[i].n = 0;
 -      pairs[i].sum[0] = 0;      pairs[i].sum[1] = 0;
 -      pairs[i].ssq[0] = 0;      pairs[i].ssq[1] = 0;
 -      pairs[i].sum_of_prod = 0;
 -      pairs[i].correlation = 0;
 -      pairs[i].sum_of_diffs = 0;
 -      pairs[i].ssq_diffs = 0;
 -    }
 -
 +  return 0;
  }
  
 -
  static int
 -paired_calc (const struct dictionary *dict, const struct ccase *c,
 -           struct cmd_t_test *cmd UNUSED, enum mv_class exclude)
 +paired_calc (const struct dictionary *dict, struct t_test_proc *proc,
 +             struct casereader *reader)
  {
 +  struct ccase *c;
    int i;
  
 -  double weight = dict_get_case_weight (dict, c, NULL);
 -
 -  for (i=0; i < n_pairs ; ++i )
 +  for (i = 0; i < proc->n_pairs; i++)
      {
 -      const struct variable *v0 = pairs[i].v[0];
 -      const struct variable *v1 = pairs[i].v[1];
 -
 -      const union value *val0 = case_data (c, v0);
 -      const union value *val1 = case_data (c, v1);
 -
 -      if (!var_is_value_missing (v0, val0, exclude) &&
 -          !var_is_value_missing (v1, val1, exclude))
 -      {
 -        pairs[i].n += weight;
 -        pairs[i].sum[0] += weight * val0->f;
 -        pairs[i].sum[1] += weight * val1->f;
 -
 -        pairs[i].ssq[0] += weight * pow2 (val0->f);
 -        pairs[i].ssq[1] += weight * pow2 (val1->f);
 -
 -        pairs[i].sum_of_prod += weight * val0->f * val1->f ;
 -
 -        pairs[i].sum_of_diffs += weight * ( val0->f - val1->f ) ;
 -        pairs[i].ssq_diffs += weight * pow2 (val0->f - val1->f);
 -      }
 +      struct pair *pair = &proc->pairs[i];
 +      pair->n = 0;
 +      pair->sum[0] = pair->sum[1] = 0;
 +      pair->ssq[0] = pair->ssq[1] = 0;
 +      pair->sum_of_prod = 0;
 +      pair->correlation = 0;
 +      pair->sum_of_diffs = 0;
 +      pair->ssq_diffs = 0;
      }
  
 -  return 0;
 -}
 -
 -static void
 -paired_postcalc (struct cmd_t_test *cmd UNUSED)
 -{
 -  int i;
 +  for (; (c = casereader_read (reader)) != NULL; case_unref (c))
 +    {
 +      double weight = dict_get_case_weight (dict, c, NULL);
 +      for (i = 0; i < proc->n_pairs; i++)
 +        {
 +          struct pair *pair = &proc->pairs[i];
 +          const struct variable *v0 = pair->v[0];
 +          const struct variable *v1 = pair->v[1];
 +
 +          const union value *val0 = case_data (c, v0);
 +          const union value *val1 = case_data (c, v1);
 +
 +          if (!var_is_value_missing (v0, val0, proc->exclude)
 +              && !var_is_value_missing (v1, val1, proc->exclude))
 +            {
 +              pair->n += weight;
 +              pair->sum[0] += weight * val0->f;
 +              pair->sum[1] += weight * val1->f;
 +              pair->ssq[0] += weight * pow2 (val0->f);
 +              pair->ssq[1] += weight * pow2 (val1->f);
 +              pair->sum_of_prod += weight * val0->f * val1->f;
 +              pair->sum_of_diffs += weight * (val0->f - val1->f);
 +              pair->ssq_diffs += weight * pow2 (val0->f - val1->f);
 +            }
 +        }
 +    }
  
 -  for (i=0; i < n_pairs ; ++i )
 +  for (i = 0; i < proc->n_pairs; i++)
      {
 +      struct pair *pair = &proc->pairs[i];
 +      const double n = pair->n;
        int j;
 -      const double n = pairs[i].n;
  
 -      for (j=0; j < 2 ; ++j)
 +      for (j=0; j < 2; j++)
        {
 -        pairs[i].mean[j] = pairs[i].sum[j] / n ;
 -        pairs[i].s_std_dev[j] = sqrt ((pairs[i].ssq[j] / n -
 -                                            pow2 (pairs[i].mean[j]))
 -                                   );
 -
 -        pairs[i].std_dev[j] = sqrt (n/ (n-1)* (pairs[i].ssq[j] / n -
 -                                            pow2 (pairs[i].mean[j]))
 -                                   );
 +        pair->mean[j] = pair->sum[j] / n;
 +        pair->s_std_dev[j] = sqrt ((pair->ssq[j] / n
 +                                      - pow2 (pair->mean[j])));
 +        pair->std_dev[j] = sqrt (n / (n- 1) * (pair->ssq[j] / n
 +                                                - pow2 (pair->mean[j])));
        }
  
 -      pairs[i].correlation = pairs[i].sum_of_prod / pairs[i].n -
 -      pairs[i].mean[0] * pairs[i].mean[1] ;
 +      pair->correlation = (pair->sum_of_prod / pair->n
 +                           - pair->mean[0] * pair->mean[1]);
        /* correlation now actually contains the covariance */
 +      pair->correlation /= pair->std_dev[0] * pair->std_dev[1];
 +      pair->correlation *= pair->n / (pair->n - 1);
  
 -      pairs[i].correlation /= pairs[i].std_dev[0] * pairs[i].std_dev[1];
 -      pairs[i].correlation *= pairs[i].n / ( pairs[i].n - 1 );
 -
 -      pairs[i].mean_diff = pairs[i].sum_of_diffs / n ;
 -
 -      pairs[i].std_dev_diff = sqrt (  n / (n - 1) * (
 -                                  ( pairs[i].ssq_diffs / n )
 -                                  -
 -                                  pow2 (pairs[i].mean_diff )
 -                                  ) );
 +      pair->mean_diff = pair->sum_of_diffs / n;
 +      pair->std_dev_diff = sqrt (n / (n - 1) * ((pair->ssq_diffs / n)
 +                                                - pow2 (pair->mean_diff)));
      }
 +
 +  casereader_destroy (reader);
 +  return 0;
  }
  
 -static void
 -group_precalc (struct cmd_t_test *cmd )
 +static int
 +group_calc (const struct dictionary *dict, struct t_test_proc *proc,
 +            struct casereader *reader)
  {
 +  struct ccase *c;
    int i;
 -  int j;
  
 -  for (i=0; i< cmd->n_variables ; ++i)
 +  for (i = 0; i < proc->n_vars; i++)
      {
 -      struct group_proc *ttpr = group_proc_get (cmd->v_variables[i]);
 +      struct group_proc *ttpr = group_proc_get (proc->vars[i]);
 +      int j;
  
        /* There's always 2 groups for a T - TEST */
        ttpr->n_groups = 2;
 -
 -      gp.indep_width = var_get_width (indep_var);
 -
        ttpr->group_hash = hsh_create (2,
 -                                  (hsh_compare_func *) compare_group_binary,
 -                                  (hsh_hash_func *) hash_group_binary,
 -                                  (hsh_free_func *) free_group,
 -                                  (void *) &gp );
 +                                     (hsh_compare_func *) compare_group_binary,
 +                                     (hsh_hash_func *) hash_group_binary,
 +                                     (hsh_free_func *) free_group,
 +                                     proc);
  
 -      for (j=0 ; j < 2 ; ++j)
 +      for (j = 0; j < 2; j++)
        {
          struct group_statistics *gs = xmalloc (sizeof *gs);
 -
          gs->sum = 0;
          gs->n = 0;
          gs->ssq = 0;
 -
 -        if ( gp.criterion == CMP_EQ )
 -          {
 -            gs->id = gp.v.g_value[j];
 -          }
 +        if (proc->criterion == CMP_EQ)
 +            gs->id = proc->g_value[j];
          else
            {
 -            if ( j == 0 )
 -              gs->id.f = gp.v.critical_value - 1.0 ;
 +            if (j == 0)
 +              gs->id.f = proc->critical_value - 1.0;
              else
 -              gs->id.f = gp.v.critical_value + 1.0 ;
 +              gs->id.f = proc->critical_value + 1.0;
            }
  
 -        hsh_insert ( ttpr->group_hash, (void *) gs );
 +        hsh_insert (ttpr->group_hash, gs);
        }
      }
  
 -}
 -
 -static int
 -group_calc (const struct dictionary *dict,
 -          const struct ccase *c, struct cmd_t_test *cmd,
 -          enum mv_class exclude)
 -{
 -  int i;
 -
 -  const double weight = dict_get_case_weight (dict, c, NULL);
 -
 -  const union value *gv;
 -
 -  if (var_is_value_missing (indep_var, case_data (c, indep_var), exclude))
 -    return 0;
 -
 -  gv = case_data (c, indep_var);
 -
 -  for (i=0; i< cmd->n_variables ; ++i)
 +  for (; (c = casereader_read (reader)) != NULL; case_unref (c))
      {
 -      const struct variable *var = cmd->v_variables[i];
 -      const union value *val = case_data (c, var);
 -      struct hsh_table *grp_hash = group_proc_get (var)->group_hash;
 -      struct group_statistics *gs;
 -
 -      gs = hsh_find (grp_hash, (void *) gv);
 -
 -      /* If the independent variable doesn't match either of the values
 -         for this case then move on to the next case */
 -      if ( ! gs )
 -              return 0;
 -
 -      if (!var_is_value_missing (var, val, exclude))
 -      {
 -        gs->n += weight;
 -        gs->sum += weight * val->f;
 -        gs->ssq += weight * pow2 (val->f);
 -      }
 +      const double weight = dict_get_case_weight (dict, c, NULL);
 +      const union value *gv;
 +
 +      if (var_is_value_missing (proc->indep_var,
 +                                case_data (c, proc->indep_var), proc->exclude))
 +        continue;
 +
 +      gv = case_data (c, proc->indep_var);
 +      for (i = 0; i < proc->n_vars; i++)
 +        {
 +          const struct variable *var = proc->vars[i];
 +          const union value *val = case_data (c, var);
 +          struct hsh_table *grp_hash = group_proc_get (var)->group_hash;
 +          struct group_statistics *gs = hsh_find (grp_hash, gv);
 +
 +          /* If the independent variable doesn't match either of the values
 +             for this case then move on to the next case. */
 +          if (gs == NULL)
 +            break;
 +
 +          if (!var_is_value_missing (var, val, proc->exclude))
 +            {
 +              gs->n += weight;
 +              gs->sum += weight * val->f;
 +              gs->ssq += weight * pow2 (val->f);
 +            }
 +        }
      }
  
 -  return 0;
 -}
 -
 -
 -static void
 -group_postcalc ( struct cmd_t_test *cmd )
 -{
 -  int i;
 -
 -  for (i = 0; i < cmd->n_variables ; ++i)
 +  for (i = 0; i < proc->n_vars; i++)
      {
 -      const struct variable *var = cmd->v_variables[i];
 +      const struct variable *var = proc->vars[i];
        struct hsh_table *grp_hash = group_proc_get (var)->group_hash;
        struct hsh_iterator g;
        struct group_statistics *gs;
 -      int count=0;
 +      int count = 0;
  
 -      for (gs =  hsh_first (grp_hash,&g);
 -         gs != 0;
 -         gs = hsh_next (grp_hash,&g))
 +      for (gs = hsh_first (grp_hash, &g); gs != NULL;
 +         gs = hsh_next (grp_hash, &g))
        {
          gs->mean = gs->sum / gs->n;
 -
 -        gs->s_std_dev= sqrt (
 -                            ( (gs->ssq / gs->n ) - gs->mean * gs->mean )
 -                            ) ;
 -
 -        gs->std_dev= sqrt (
 -                          gs->n/ (gs->n-1) *
 -                          ( (gs->ssq / gs->n ) - gs->mean * gs->mean )
 -                          ) ;
 -
 +        gs->s_std_dev = sqrt (((gs->ssq / gs->n) - pow2 (gs->mean)));
 +        gs->std_dev = sqrt (gs->n / (gs->n- 1)
 +                              * ((gs->ssq / gs->n) - pow2 (gs->mean)));
          gs->se_mean = gs->std_dev / sqrt (gs->n);
 -        count ++;
 +        count++;
        }
        assert (count == 2);
      }
 -}
  
 +  casereader_destroy (reader);
  
 +  return 0;
 +}
  
  static void
 -calculate (struct cmd_t_test *cmd,
 -          struct casereader *input, const struct dataset *ds)
 +calculate (struct t_test_proc *proc,
 +           struct casereader *input, const struct dataset *ds)
  {
    const struct dictionary *dict = dataset_dict (ds);
    struct ssbox stat_summary_box;
    struct trbox test_results_box;
 -
 -  struct casereader *pass1, *pass2, *pass3;
    struct taint *taint;
 -  struct ccase c;
 -
 -  enum mv_class exclude = cmd->miss != TTS_INCLUDE ? MV_ANY : MV_SYSTEM;
 +  struct ccase *c;
  
 -  if (!casereader_peek (input, 0, &c))
 +  c = casereader_peek (input, 0);
 +  if (c == NULL)
      {
        casereader_destroy (input);
        return;
      }
 -  output_split_file_values (ds, &c);
 -  case_destroy (&c);
 +  output_split_file_values (ds, c);
 +  case_unref (c);
  
 -  if ( cmd->miss == TTS_LISTWISE )
 +  if (proc->listwise_missing)
      input = casereader_create_filter_missing (input,
 -                                              cmd->v_variables,
 -                                              cmd->n_variables,
 -                                              exclude, NULL);
 -
 +                                              proc->vars,
 +                                              proc->n_vars,
 +                                              proc->exclude, NULL, NULL);
    input = casereader_create_filter_weight (input, dict, NULL, NULL);
 -
    taint = taint_clone (casereader_get_taint (input));
 -  casereader_split (input, &pass1, &pass2);
 -
 -  common_precalc (cmd);
 -  for (; casereader_read (pass1, &c); case_destroy (&c))
 -    common_calc (dict, &c, cmd, exclude);
 -  casereader_destroy (pass1);
 -  common_postcalc (cmd);
  
 -  switch (mode)
 +  common_calc (dict, proc, casereader_clone (input));
 +  switch (proc->mode)
      {
      case T_1_SAMPLE:
 -      one_sample_precalc (cmd);
 -      for (; casereader_read (pass2, &c); case_destroy (&c))
 -        one_sample_calc (dict, &c, cmd, exclude);
 -      one_sample_postcalc (cmd);
 +      one_sample_calc (dict, proc, input);
        break;
      case T_PAIRED:
 -      paired_precalc (cmd);
 -      for (; casereader_read (pass2, &c); case_destroy (&c))
 -        paired_calc (dict, &c, cmd, exclude);
 -      paired_postcalc (cmd);
 +      paired_calc (dict, proc, input);
        break;
      case T_IND_SAMPLES:
 -      pass3 = casereader_clone (pass2);
 -
 -      group_precalc (cmd);
 -      for (; casereader_read (pass2, &c); case_destroy (&c))
 -        group_calc (dict, &c, cmd, exclude);
 -      group_postcalc (cmd);
 -
 -      levene (dict, pass3, indep_var, cmd->n_variables, cmd->v_variables,
 -              exclude);
 +      group_calc (dict, proc, casereader_clone (input));
 +      levene (dict, input, proc->indep_var, proc->n_vars, proc->vars,
 +              proc->exclude);
        break;
 +    default:
 +      NOT_REACHED ();
      }
 -  casereader_destroy (pass2);
  
    if (!taint_has_tainted_successor (taint))
      {
 -      ssbox_create (&stat_summary_box,cmd,mode);
 -      ssbox_populate (&stat_summary_box, dict, cmd);
 +      ssbox_create (&stat_summary_box, proc);
 +      ssbox_populate (&stat_summary_box, proc);
        ssbox_finalize (&stat_summary_box);
  
 -      if ( mode == T_PAIRED )
 -        pscbox (dict);
 +      if (proc->mode == T_PAIRED)
 +        pscbox (proc);
  
 -      trbox_create (&test_results_box, cmd, mode);
 -      trbox_populate (&test_results_box, dict, cmd);
 +      trbox_create (&test_results_box, proc);
 +      trbox_populate (&test_results_box, proc);
        trbox_finalize (&test_results_box);
      }
  
    taint_destroy (taint);
  }
  
 -short which_group (const struct group_statistics *g,
 -                const struct group_properties *p);
 +/* return 0 if G belongs to group 0,
 +          1 if it belongs to group 1,
 +          2 if it belongs to neither group */
 +static int
 +which_group (const struct group_statistics *g,
 +             const struct t_test_proc *proc)
 +{
 +  int width = var_get_width (proc->indep_var);
 +
 +  if (0 == value_compare_3way (&g->id, &proc->g_value[0], width))
 +    return 0;
 +
 +  if (0 == value_compare_3way (&g->id, &proc->g_value[1], width))
 +    return 1;
 +
 +  return 2;
 +}
  
  /* Return -1 if the id of a is less than b; +1 if greater than and
     0 if equal */
  static int
  compare_group_binary (const struct group_statistics *a,
 -                   const struct group_statistics *b,
 -                   const struct group_properties *p)
 +                      const struct group_statistics *b,
 +                      const struct t_test_proc *proc)
  {
 -  short flag_a;
 -  short flag_b;
 +  int flag_a;
 +  int flag_b;
  
 -  if ( p->criterion == CMP_LE )
 +  if (proc->criterion == CMP_LE)
      {
 -      /* less-than comparision is not meaningfull for
 -       alpha variables, so we shouldn't ever arrive here */
 -      assert (p->indep_width == 0 ) ;
 -
 -      flag_a = ( a->id.f < p->v.critical_value ) ;
 -      flag_b = ( b->id.f < p->v.critical_value ) ;
 +      flag_a = (a->id.f < proc->critical_value);
 +      flag_b = (b->id.f < proc->critical_value);
      }
    else
      {
 -      flag_a = which_group (a, p);
 -      flag_b = which_group (b, p);
 +      flag_a = which_group (a, proc);
 +      flag_b = which_group (b, proc);
      }
  
 -  if (flag_a < flag_b )
 -    return -1;
 +  if (flag_a < flag_b)
 +    return - 1;
  
    return (flag_a > flag_b);
  }
  
  static unsigned
  hash_group_binary (const struct group_statistics *g,
 -                const struct group_properties *p)
 -{
 -  short flag = -1;
 -
 -  if ( p->criterion == CMP_LE )
 -    {
 -      /* Not meaningfull to do a less than compare for alpha values ? */
 -      assert (p->indep_width == 0 ) ;
 -      flag = ( g->id.f < p->v.critical_value ) ;
 -    }
 -  else if ( p->criterion == CMP_EQ)
 -    {
 -      flag = which_group (g,p);
 -    }
 -  else
 -    NOT_REACHED ();
 -
 -  return flag;
 -}
 -
 -/* return 0 if G belongs to group 0,
 -          1 if it belongs to group 1,
 -        2 if it belongs to neither group */
 -short
 -which_group (const struct group_statistics *g,
 -          const struct group_properties *p)
 +                   const struct t_test_proc *proc)
  {
 -  if ( 0 == compare_values (&g->id, &p->v.g_value[0], p->indep_width))
 -    return 0;
 -
 -  if ( 0 == compare_values (&g->id, &p->v.g_value[1], p->indep_width))
 -    return 1;
 -
 -  return 2;
 +  return (proc->criterion == CMP_LE
 +          ? g->id.f < proc->critical_value
 +          : which_group (g, proc));
  }
  
  /*
index c3641580e0511d66a0a293819c8b6f065a53f96d,8bcad4947eee31d63e09264daf960e0d0f8a1008..33c445b09a61b0ef36714272bd221bfd2cd9f741
@@@ -1,5 -1,5 +1,5 @@@
  /* PSPP - a program for statistical analysis.
-    Copyright (C) 2004, 2008 Free Software Foundation, Inc.
 -   Copyright (C) 2004, 2009 Free Software Foundation, Inc.
++   Copyright (C) 2004, 2008, 2009 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 <output/chart.h>
  #include <math/chart-geometry.h>
 -#include <math/factor-stats.h>
 -
 -
 -
 +#include <math/box-whisker.h>
  
  /* Draw a box-and-whiskers plot
  */
  
 -/* Draw an outlier on the plot CH
 +/* Draw an OUTLIER on the plot CH
   * at CENTRELINE
 - * The outlier is in (*wvp)[idx]
 - * If EXTREME is non zero, then consider it to be an extreme
 - * value
   */
 -void
 -draw_outlier(struct chart *ch, double centreline,
 -           struct weighted_value **wvp,
 -           int idx,
 -           short extreme);
 -
 -
 -void
 -draw_outlier(struct chart *ch, double centreline,
 -           struct weighted_value **wvp,
 -           int idx,
 -           short extreme
 -           )
 +static void
 +draw_case (struct chart *ch, double centreline,
 +         const struct outlier *outlier)
  {
 -  char label[10];
  
  #define MARKER_CIRCLE 4
  #define MARKER_STAR 3
  
    pl_fmarker_r(ch->lp,
               centreline,
 -             ch->data_bottom +
 -             (wvp[idx]->v.f - ch->y_min ) * ch->ordinate_scale,
 -             extreme?MARKER_STAR:MARKER_CIRCLE,
 +             ch->data_bottom + (outlier->value - ch->y_min) * ch->ordinate_scale,
 +             outlier->extreme ? MARKER_STAR : MARKER_CIRCLE,
               20);
  
    pl_moverel_r(ch->lp, 10,0);
  
 -  snprintf(label, 10, "%d", wvp[idx]->case_nos->num);
 -
 -  pl_alabel_r(ch->lp, 'l', 'c', label);
 -
 +  pl_alabel_r(ch->lp, 'l', 'c', ds_cstr (&outlier->label));
  }
  
  
  void
 -boxplot_draw_boxplot(struct chart *ch,
 -                   double box_centre,
 -                   double box_width,
 -                   struct metrics *m,
 -                   const char *name)
 +boxplot_draw_boxplot (struct chart *ch,
 +                    double box_centre,
 +                    double box_width,
 +                    const struct box_whisker *bw,
 +                    const char *name)
  {
    double whisker[2];
 -  int i;
 -
 -  const double *hinge = m->hinge;
 -  struct weighted_value **wvp = m->wvp;
 -  const int n_data = m->n_data;
 -
 -  const double step = (hinge[2] - hinge[0]) * 1.5;
 +  double hinge[3];
 +  struct ll *ll;
  
 +  const struct ll_list *outliers;
  
    const double box_left = box_centre - box_width / 2.0;
  
    const double box_right = box_centre + box_width / 2.0;
  
 +  double box_bottom ;
 +  double box_top ;
 +  double bottom_whisker ;
 +  double top_whisker ;
  
 -  const double box_bottom =
 -    ch->data_bottom + ( hinge[0] - ch->y_min ) * ch->ordinate_scale;
 -
 -
 -  const double box_top =
 -    ch->data_bottom + ( hinge[2] - ch->y_min ) * ch->ordinate_scale;
 -
 -  assert(m);
 +  box_whisker_whiskers (bw, whisker);
 +  box_whisker_hinges (bw, hinge);
  
 -  /* Can't really draw a boxplot if there's no data */
 -  if ( n_data == 0 )
 -        return ;
 +  box_bottom = ch->data_bottom + (hinge[0] - ch->y_min ) * ch->ordinate_scale;
  
 -  whisker[1] = hinge[2];
 -  whisker[0] = wvp[0]->v.f;
 +  box_top = ch->data_bottom + (hinge[2] - ch->y_min ) * ch->ordinate_scale;
  
 -  for ( i = 0 ; i < n_data ; ++i )
 -    {
 -      if ( hinge[2] + step >  wvp[i]->v.f)
 -      whisker[1] = wvp[i]->v.f;
 -
 -      if ( hinge[0] - step >  wvp[i]->v.f)
 -      whisker[0] = wvp[i]->v.f;
 -
 -    }
 -
 -  {
 -  const double bottom_whisker =
 -    ch->data_bottom + ( whisker[0] - ch->y_min ) * ch->ordinate_scale;
 -
 -  const double top_whisker =
 -    ch->data_bottom + ( whisker[1] - ch->y_min ) * ch->ordinate_scale;
 +  bottom_whisker = ch->data_bottom + (whisker[0] - ch->y_min) *
 +    ch->ordinate_scale;
  
 +  top_whisker = ch->data_bottom + (whisker[1] - ch->y_min) * ch->ordinate_scale;
  
    pl_savestate_r(ch->lp);
  
 -
    /* Draw the box */
 -  pl_savestate_r(ch->lp);
 -  pl_fillcolorname_r(ch->lp,ch->fill_colour);
 -  pl_filltype_r(ch->lp,1);
 -  pl_fbox_r(ch->lp,
 +  pl_savestate_r (ch->lp);
 +  pl_fillcolorname_r (ch->lp, ch->fill_colour);
 +  pl_filltype_r (ch->lp,1);
 +  pl_fbox_r (ch->lp,
            box_left,
            box_bottom,
            box_right,
            box_top);
  
 -  pl_restorestate_r(ch->lp);
 -
 -
 +  pl_restorestate_r (ch->lp);
  
    /* Draw the median */
 -  pl_savestate_r(ch->lp);
 -  pl_linewidth_r(ch->lp,5);
 -  pl_fline_r(ch->lp,
 +  pl_savestate_r (ch->lp);
 +  pl_linewidth_r (ch->lp, 5);
 +  pl_fline_r (ch->lp,
             box_left,
 -           ch->data_bottom + ( hinge[1] - ch->y_min ) * ch->ordinate_scale,
 +           ch->data_bottom + (hinge[1] - ch->y_min) * ch->ordinate_scale,
             box_right,
 -           ch->data_bottom + ( hinge[1] - ch->y_min ) * ch->ordinate_scale);
 -  pl_restorestate_r(ch->lp);
 -
 +           ch->data_bottom + (hinge[1] - ch->y_min) * ch->ordinate_scale);
 +  pl_restorestate_r (ch->lp);
  
    /* Draw the bottom whisker */
 -  pl_fline_r(ch->lp,
 +  pl_fline_r (ch->lp,
             box_left,
             bottom_whisker,
             box_right,
             bottom_whisker);
  
    /* Draw top whisker */
 -  pl_fline_r(ch->lp,
 +  pl_fline_r (ch->lp,
             box_left,
             top_whisker,
             box_right,
             top_whisker);
  
  
 -
    /* Draw centre line.
       (bottom half) */
 -  pl_fline_r(ch->lp,
 +  pl_fline_r (ch->lp,
             box_centre, bottom_whisker,
             box_centre, box_bottom);
  
    /* (top half) */
 -  pl_fline_r(ch->lp,
 +  pl_fline_r (ch->lp,
             box_centre, top_whisker,
             box_centre, box_top);
 -  }
  
 -  /* Draw outliers */
 -  for ( i = 0 ; i < n_data ; ++i )
 +  outliers = box_whisker_outliers (bw);
 +  for (ll = ll_head (outliers);
 +       ll != ll_null (outliers); ll = ll_next (ll))
      {
 -      if ( wvp[i]->v.f >= hinge[2] + step )
 -      draw_outlier(ch, box_centre, wvp, i,
 -                   ( wvp[i]->v.f > hinge[2] + 2 * step )
 -                   );
 -
 -      if ( wvp[i]->v.f <= hinge[0] - step )
 -      draw_outlier(ch, box_centre, wvp, i,
 -                   ( wvp[i]->v.f < hinge[0] - 2 * step )
 -                   );
 +      const struct outlier *outlier = ll_data (ll, struct outlier, ll);
 +      draw_case (ch, box_centre, outlier);
      }
  
 -
    /* Draw  tick  mark on x axis */
-   draw_tick(ch, TICK_ABSCISSA, box_centre - ch->data_left, name);
+   draw_tick(ch, TICK_ABSCISSA, box_centre - ch->data_left, "%s", name);
  
    pl_restorestate_r(ch->lp);
 -
  }
  
 -
 -
  void
 -boxplot_draw_yscale(struct chart *ch , double y_max, double y_min)
 +boxplot_draw_yscale (struct chart *ch, double y_max, double y_min)
  {
    double y_tick;
    double d;
    ch->y_max  = y_max;
    ch->y_min  = y_min;
  
 -  y_tick = chart_rounded_tick(fabs(ch->y_max - ch->y_min) / 5.0);
 +  y_tick = chart_rounded_tick (fabs(ch->y_max - ch->y_min) / 5.0);
  
    ch->y_min = (ceil( ch->y_min  / y_tick ) - 1.0  ) * y_tick;
  
    ch->ordinate_scale = fabs(ch->data_top - ch->data_bottom)
      / fabs(ch->y_max - ch->y_min) ;
  
 -
    /* Move to data bottom-left */
    pl_move_r(ch->lp,
            ch->data_left, ch->data_bottom);
      {
        draw_tick (ch, TICK_ORDINATE, (d - ch->y_min ) * ch->ordinate_scale, "%g", d);
      }
 -
  }
index f4cc5bbf06c3f91650c4988b15e76e36f8efc74a,9a4df52052392cf1769dc1d2bade682a95af1e18..cfbaa4df8ddc396ee4d2338dee87acfd349bf0a5
@@@ -1,5 -1,5 +1,5 @@@
  /* PSPP - a program for statistical analysis.
-    Copyright (C) 2004 Free Software Foundation, Inc.
+    Copyright (C) 2004, 2009 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
@@@ -26,6 -26,7 +26,7 @@@
  #include <math/chart-geometry.h>
  #include <output/chart.h>
  
+ #include <libpspp/compiler.h>
  #include <libpspp/str.h>
  #include <output/manager.h>
  #include <output/output.h>
@@@ -51,11 -52,13 +52,13 @@@ enum tick_orientatio
  void draw_tick(struct chart *chart,
          enum tick_orientation orientation,
          double position,
-              const char *label, ...);
+              const char *label, ...)
+   PRINTF_FORMAT (4, 5);
  
  
  /* Write the title on a chart*/
- void   chart_write_title(struct chart *chart, const char *title, ...);
+ void   chart_write_title(struct chart *chart, const char *title, ...)
+   PRINTF_FORMAT (2, 3);
  
  
  /* Set the scale for the abscissa */
@@@ -70,6 -73,4 +73,6 @@@ void chart_write_xlabel(struct chart *c
  /* Write the ordinate label */
  void  chart_write_ylabel(struct chart *ch, const char *label);
  
 +void chart_write_legend (struct chart *ch);
 +
  #endif
index 4b11618a2f1a409fa3d92a9071a456131ce8485a,88ad0899199d939dd5411cfb8fb2656368e395a4..fbf1925e635e165c3c096d119bace8123fa0d5bc
@@@ -1,10 -1,10 +1,10 @@@
  /* PSPP - a program for statistical analysis.
-    Copyright  (C) 2004 Free Software Foundation, Inc.
+    Copyright (C) 2004, 2009 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.
 +    (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
  #include <data/variable.h>
  #include <libpspp/hash.h>
  #include <output/chart.h>
 +#include <math/histogram.h>
 +#include <math/moments.h>
  
  #include "gettext.h"
  #define _(msgid) gettext (msgid)
  
  /* Write the legend of the chart */
 -void
 -histogram_write_legend(struct chart *ch, const struct normal_curve *norm)
 +static void
 +histogram_write_legend (struct chart *ch, double n, double mean, double stddev)
  {
    char buf[100];
 -  if ( !ch )
 +
 +  if (!ch)
      return ;
  
 -  pl_savestate_r(ch->lp);
 +  pl_savestate_r (ch->lp);
  
 -  sprintf(buf,"N = %.2f",norm->N);
 -  pl_move_r(ch->lp, ch->legend_left, ch->data_bottom);
 -  pl_alabel_r(ch->lp,0,'b',buf);
 +  sprintf (buf, "N = %.2f", n);
 +  pl_move_r (ch->lp, ch->legend_left, ch->data_bottom);
 +  pl_alabel_r (ch->lp, 0, 'b', buf);
  
 -  sprintf(buf,"Mean = %.1f",norm->mean);
 -  pl_fmove_r(ch->lp,ch->legend_left,ch->data_bottom + ch->font_size * 1.5);
 -  pl_alabel_r(ch->lp,0,'b',buf);
 +  sprintf (buf, "Mean = %.1f", mean);
 +  pl_fmove_r (ch->lp,ch->legend_left,ch->data_bottom + ch->font_size * 1.5);
 +  pl_alabel_r (ch->lp, 0, 'b', buf);
  
 -  sprintf(buf,"Std. Dev = %.2f",norm->stddev);
 -  pl_fmove_r(ch->lp,ch->legend_left,ch->data_bottom + ch->font_size * 1.5 * 2);
 -  pl_alabel_r(ch->lp,0,'b',buf);
 +  sprintf (buf, "Std. Dev = %.2f", stddev);
 +  pl_fmove_r (ch->lp, ch->legend_left, ch->data_bottom + ch->font_size * 1.5 * 2);
 +  pl_alabel_r (ch->lp, 0, 'b', buf);
  
 -  pl_restorestate_r(ch->lp);
 +  pl_restorestate_r (ch->lp);
  }
  
 -static void hist_draw_bar(struct chart *ch, const gsl_histogram *hist, int bar);
 +static void hist_draw_bar (struct chart *ch, const struct histogram *hist, int bar);
  
  
  static void
 -hist_draw_bar(struct chart *ch, const gsl_histogram *hist, int bar)
 +hist_draw_bar (struct chart *ch, const struct histogram *hist, int bar)
  {
 -  if ( !ch )
 +  if (!ch)
      return ;
  
 -
    {
      double upper;
      double lower;
      double height;
  
 -    const size_t bins = gsl_histogram_bins(hist);
 +    const size_t bins = gsl_histogram_bins (hist->gsl_hist);
      const double x_pos = (ch->data_right - ch->data_left) * bar / (double) bins ;
      const double width = (ch->data_right - ch->data_left) / (double) bins ;
  
 +    assert ( 0 == gsl_histogram_get_range (hist->gsl_hist, bar, &lower, &upper));
  
 -    assert ( 0 == gsl_histogram_get_range(hist, bar, &lower, &upper));
 -
 -    assert( upper >= lower);
 +    assert ( upper >= lower);
  
 -    height = gsl_histogram_get(hist, bar) *
 -      (ch->data_top - ch->data_bottom) / gsl_histogram_max_val(hist);
 +    height = gsl_histogram_get (hist->gsl_hist, bar) *
 +     (ch->data_top - ch->data_bottom) / gsl_histogram_max_val (hist->gsl_hist);
  
 -    pl_savestate_r(ch->lp);
 -    pl_move_r(ch->lp,ch->data_left, ch->data_bottom);
 -    pl_fillcolorname_r(ch->lp, ch->fill_colour);
 -    pl_filltype_r(ch->lp,1);
 +    pl_savestate_r (ch->lp);
 +    pl_move_r (ch->lp,ch->data_left, ch->data_bottom);
 +    pl_fillcolorname_r (ch->lp, ch->fill_colour);
 +    pl_filltype_r (ch->lp,1);
  
  
 -    pl_fboxrel_r(ch->lp,
 +    pl_fboxrel_r (ch->lp,
                 x_pos, 0,
                 x_pos + width, height);
  
 -    pl_restorestate_r(ch->lp);
 +    pl_restorestate_r (ch->lp);
  
-     {
-       char buf[5];
-       snprintf (buf,5,"%g", (upper + lower) / 2.0);
-       draw_tick (ch, TICK_ABSCISSA,
-               x_pos + width / 2.0, buf);
-     }
+     draw_tick (ch, TICK_ABSCISSA,
+                x_pos + width / 2.0, "%g", (upper + lower) / 2.0);
    }
  }
  
  
  
 +void
 +histogram_plot (const struct histogram *hist,
 +              const char *label,
 +              const struct moments1 *m)
 +{
 +  double mean, var, n;
 +
 +  moments1_calculate (m, &n, &mean, &var, NULL,  NULL);
 +
 +  histogram_plot_n (hist, label, n, mean, sqrt(var), m);
 +}
 +
  
 +/* This function is deprecated.  Don't use it in new code */
  void
 -histogram_plot(const gsl_histogram *hist,
 -             const char *factorname,
 -             const struct normal_curve *norm, short show_normal)
 +histogram_plot_n (const struct histogram *hist,
 +                const char *label,
 +                double n, double mean, double stddev,
 +                bool show_normal)
  {
    int i;
    int bins;
  
 -  struct chart *ch;
 -
 -  ch = chart_create();
 -  if (ch == NULL)
 -    return;
 +  struct chart *ch = chart_create ();
  
 -  chart_write_title(ch, _("HISTOGRAM"));
 +  chart_write_title (ch, _("HISTOGRAM"));
  
 -  chart_write_ylabel(ch, _("Frequency"));
 -  chart_write_xlabel(ch, factorname);
 +  chart_write_ylabel (ch, _("Frequency"));
 +  chart_write_xlabel (ch, label);
  
    if ( ! hist ) /* If this happens, probably all values are SYSMIS */
      {
 -      chart_submit(ch);
 -      return ;
 +      chart_submit (ch);
 +      return;
      }
    else
      {
 -      bins = gsl_histogram_bins(hist);
 +      bins = gsl_histogram_bins (hist->gsl_hist);
      }
  
 -  chart_write_yscale(ch, 0, gsl_histogram_max_val(hist), 5);
 +  chart_write_yscale (ch, 0, gsl_histogram_max_val (hist->gsl_hist), 5);
  
    for ( i = 0 ; i < bins ; ++i )
 -      hist_draw_bar(ch, hist, i);
 +    hist_draw_bar (ch, hist, i);
  
 -  histogram_write_legend(ch, norm);
 +  histogram_write_legend (ch, n, mean, stddev);
  
 -  if ( show_normal  )
 -  {
 -    /* Draw the normal curve */
 -
 -    double d ;
 -    double x_min, x_max, not_used ;
 -    double abscissa_scale ;
 -    double ordinate_scale ;
 -    double range ;
 -
 -    gsl_histogram_get_range(hist, 0, &x_min, &not_used);
 -    range = not_used - x_min;
 -    gsl_histogram_get_range(hist, bins - 1, &not_used, &x_max);
 -
 -    abscissa_scale = (ch->data_right - ch->data_left) / (x_max - x_min);
 -    ordinate_scale = (ch->data_top - ch->data_bottom) /
 -      gsl_histogram_max_val(hist) ;
 -
 -    pl_move_r(ch->lp, ch->data_left, ch->data_bottom);
 -    for( d = ch->data_left;
 -       d <= ch->data_right ;
 -       d += (ch->data_right - ch->data_left) / 100.0)
 -      {
 -      const double x = (d - ch->data_left) / abscissa_scale + x_min ;
 -      const double y = norm->N * range *
 -        gsl_ran_gaussian_pdf(x - norm->mean, norm->stddev);
 -
 -      pl_fcont_r(ch->lp,  d,  ch->data_bottom  + y * ordinate_scale);
 -
 -      }
 -    pl_endpath_r(ch->lp);
 +  if (show_normal)
 +    {
 +      /* Draw the normal curve */
 +
 +      double d ;
 +      double x_min, x_max, not_used ;
 +      double abscissa_scale ;
 +      double ordinate_scale ;
 +      double range ;
 +
 +      gsl_histogram_get_range (hist->gsl_hist, 0, &x_min, &not_used);
 +      range = not_used - x_min;
 +      gsl_histogram_get_range (hist->gsl_hist, bins - 1, &not_used, &x_max);
 +
 +      abscissa_scale = (ch->data_right - ch->data_left) / (x_max - x_min);
 +      ordinate_scale = (ch->data_top - ch->data_bottom) /
 +      gsl_histogram_max_val (hist->gsl_hist) ;
 +
 +      pl_move_r (ch->lp, ch->data_left, ch->data_bottom);
 +      for ( d = ch->data_left;
 +          d <= ch->data_right ;
 +          d += (ch->data_right - ch->data_left) / 100.0)
 +      {
 +        const double x = (d - ch->data_left) / abscissa_scale + x_min ;
 +        const double y = n * range *
 +          gsl_ran_gaussian_pdf (x - mean, stddev);
 +
 +        pl_fcont_r (ch->lp,  d,  ch->data_bottom  + y * ordinate_scale);
 +
 +      }
 +      pl_endpath_r (ch->lp);
 +    }
  
 -  }
 -  chart_submit(ch);
 +  chart_submit (ch);
  }
  
 +
diff --combined src/ui/gui/psppire.c
index 3555463f88e4d353c84bcd366b223955d4217604,a7b22037b868e7d523726c8f80faab41ebe1acc8..1cd7ee803c736a740b373a53b035c7dcef6bd90a
  
  #include <config.h>
  
 -#include <locale.h>
 +#include <libpspp/i18n.h>
  #include <assert.h>
  #include <libintl.h>
  #include <gsl/gsl_errno.h>
  
 +#include <xalloc.h>
 +#include <argp.h>
 +#include <ui/command-line.h>
  #include "relocatable.h"
  
 -#include "data-editor.h"
 +#include "psppire-data-window.h"
  #include "psppire.h"
 +#include "widgets.h"
  
 +#include <libpspp/getl.h>
  #include <unistd.h>
  #include <data/casereader.h>
  #include <data/datasheet.h>
  #include <libpspp/version.h>
  #include <output/output.h>
  #include <output/journal.h>
 +#include <language/syntax-string-source.h>
  
  #include <gtk/gtk.h>
 -#include <glade/glade.h>
  #include "psppire-dict.h"
  #include "psppire-var-store.h"
  #include "psppire-data-store.h"
 -#include "helper.h"
 +#include "executor.h"
  #include "message-dialog.h"
 +#include <ui/syntax-gen.h>
 +
 +#include "psppire-window-register.h"
 +#include "psppire-output-window.h"
 +
 +#include <data/sys-file-reader.h>
 +#include <data/por-file-reader.h>
  
 -#include "output-viewer.h"
 +#include <ui/source-init-opts.h>
  
 +GtkRecentManager *the_recent_mgr = 0;
  PsppireDataStore *the_data_store = 0;
  PsppireVarStore *the_var_store = 0;
  
@@@ -70,29 -57,32 +70,28 @@@ static void create_icon_factory (void)
  struct source_stream *the_source_stream ;
  struct dataset * the_dataset = NULL;
  
 +static GtkWidget *the_data_window;
  
  static void
  replace_casereader (struct casereader *s)
  {
 -  PsppireCaseFile *pcf = psppire_case_file_new (s);
 -
 -  psppire_data_store_set_case_file (the_data_store, pcf);
 +  psppire_data_store_set_reader (the_data_store, s);
  }
  
  #define _(msgid) gettext (msgid)
  #define N_(msgid) msgid
  
  
 +
 +
  void
 -initialize (void)
 +initialize (struct command_line_processor *clp, int argc, char **argv)
  {
    PsppireDict *dictionary = 0;
  
 -  /* gtk_init messes with the locale.
 -     So unset the bits we want to control ourselves */
 -  setlocale (LC_NUMERIC, "C");
 -
 -  bindtextdomain (PACKAGE, relocate (locale_dir));
--
 +  i18n_init ();
  
 -  glade_init ();
 +  preregister_widgets ();
  
    gsl_set_error_handler_off ();
    fn_init ();
    journal_enable ();
    textdomain (PACKAGE);
  
 -  new_data_window (NULL, NULL);
 +
 +  the_recent_mgr = gtk_recent_manager_get_default ();
 +
 +  the_data_window = psppire_data_window_new ();
 +
 +  command_line_processor_replace_aux (clp, &post_init_argp, the_source_stream);
 +  command_line_processor_replace_aux (clp, &non_option_argp, the_source_stream);
 +
 +  command_line_processor_parse (clp, argc, argv);
 +
 +  execute_syntax (create_syntax_string_source (""));
 +
 +  gtk_widget_show (the_data_window);
  }
  
  
@@@ -167,28 -145,9 +166,28 @@@ de_initialize (void
    message_dialog_done ();
    settings_done ();
    outp_done ();
 +  i18n_done ();
  }
  
  
 +static void
 +func (gpointer key, gpointer value, gpointer data)
 +{
 +  gboolean rv;
 +  PsppireWindow *window = PSPPIRE_WINDOW (value);
 +
 +  g_signal_emit_by_name (window, "delete-event", 0, &rv);
 +}
 +
 +void
 +psppire_quit (void)
 +{
 +  PsppireWindowRegister *reg = psppire_window_register_new ();
 +  psppire_window_register_foreach (reg, func, NULL);
 +
 +  gtk_main_quit ();
 +}
 +
  
  struct icon_info
  {
@@@ -250,11 -209,11 +249,11 @@@ create_icon_factory (void
  
  
      gtk_stock_add (items, 2);
 -    gtk_icon_factory_add (factory, "pspp-stock-reset", 
 +    gtk_icon_factory_add (factory, "pspp-stock-reset",
                          gtk_icon_factory_lookup_default (GTK_STOCK_REFRESH)
                          );
  
 -    gtk_icon_factory_add (factory, "pspp-stock-select", 
 +    gtk_icon_factory_add (factory, "pspp-stock-select",
                          gtk_icon_factory_lookup_default (GTK_STOCK_INDEX)
                          );
    }
    gtk_icon_factory_add_default (factory);
  }
  
 +\f
 +
 +static error_t
 +parse_non_options (int key, char *arg, struct argp_state *state)
 +{
 +  struct source_stream *ss = state->input;
 +
 +  if ( NULL == ss )
 +    return 0;
 +
 +  switch (key)
 +    {
 +    case ARGP_KEY_ARG:
 +      {
 +      gchar *filename = NULL;
 +      gchar *utf8 = NULL;
 +      const gchar *local_encoding = NULL;
 +      gsize written = -1;
 +      const gboolean local_is_utf8 = g_get_charset (&local_encoding);
 +
 +      /* There seems to be no Glib function to convert from local encoding
 +         to filename encoding.  Therefore it has to be done in two steps:
 +         the intermediate encoding is UTF8.
 +
 +         Either step could fail.  However, in many cases the file can still
 +         be loaded even if the conversion fails. So in those cases, after showing
 +         a warning, we simply copy the locally encoded filename to the destination
 +         and hope for the best.
 +      */
 +
 +      if ( local_is_utf8)
 +        {
 +          utf8 = xstrdup (arg);
 +        }
 +      else
 +        {
 +          GError *err = NULL;
 +          utf8 = g_locale_to_utf8 (arg, -1, NULL, &written, &err);
 +          if ( NULL == utf8)
 +            {
 +              g_warning ("Cannot convert filename from local encoding \"%s\" to UTF-8: %s",
 +                         local_encoding,
 +                         err->message);
 +              g_clear_error (&err);
 +            }
 +        }
 +
 +      if ( NULL != utf8)
 +        {
 +          GError *err = NULL;
 +          filename = g_filename_from_utf8 (utf8, written, NULL, NULL, &err);
 +          if ( NULL == filename)
 +            {
 +              g_warning ("Cannot convert filename from UTF8 to filename encoding: %s",
 +                         err->message);
 +              g_clear_error (&err);
 +            }
 +        }
 +
 +      g_free (utf8);
 +
 +      if ( filename == NULL)
 +        filename = xstrdup (arg);
 +
 +      psppire_window_load (PSPPIRE_WINDOW (the_data_window), filename);
 +
 +      g_free (filename);
 +      break;
 +      }
 +    default:
 +      return ARGP_ERR_UNKNOWN;
 +    }
 +  return 0;
 +}
 +
 +
 +const struct argp non_option_argp = {NULL, parse_non_options, 0, 0, 0, 0, 0};
 +
 +
 +const char *
 +output_file_name (void)
 +{
 +  const char *dir = default_output_path ();
 +  static char *filename = NULL;
 +
 +  if ( NULL == filename )
 +    filename = xasprintf ("%s%s", dir, OUTPUT_FILE_NAME);
 +
 +  return filename;
 +}
diff --combined src/ui/gui/psppire.h
index cfe49e9182aaa0f374eaec352e50e7c652d80cac,815de4a07eda818a77d77499b1900355148b2c95..27a633f2ecf35c57cd30dc01ac85aa4a61db28a9
@@@ -1,5 -1,5 +1,5 @@@
  /* PSPPIRE - a graphical user interface for PSPP.
-    Copyright (C) 2004, 2005, 2006  Free Software Foundation
+    Copyright (C) 2004, 2005, 2006, 2009  Free Software Foundation
  
     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
  #ifndef PSPPIRE_H
  #define PSPPIRE_H
  
 +#include <argp.h>
  
 -void initialize (void);
 +struct command_line_processor ;
 +extern const struct argp non_option_argp ;
 +
 +void initialize (struct command_line_processor *, int argc, char **argv);
  void de_initialize (void);
++
 +void psppire_quit (void);
 +
  const char * output_file_name (void);
  
  #endif /* PSPPIRE_H */
diff --combined src/ui/terminal/main.c
index af8f7f2dd4df9828361a1dccfdb54d09473875b9,8acbdc2dde79d7bd1a8a97be24da76523bf0f928..7ad162fca6d40b746a0155d891d8e08784e2e4df
@@@ -16,6 -16,7 +16,6 @@@
  
  #include <config.h>
  
 -#include <locale.h>
  #include <signal.h>
  #include <stdio.h>
  #include <stdlib.h>
@@@ -29,8 -30,6 +29,8 @@@
  #include <ieeefp.h>
  #endif
  
 +
 +#include <libpspp/i18n.h>
  #include <data/dictionary.h>
  #include <data/file-handle-def.h>
  #include <libpspp/getl.h>
  #include <math/random.h>
  #include <output/output.h>
  #include <ui/debugger.h>
 -#include <ui/terminal/command-line.h>
  #include <ui/terminal/msg-ui.h>
  #include <ui/terminal/read-line.h>
  #include <ui/terminal/terminal.h>
 +#include <ui/terminal/terminal-opts.h>
 +#include <ui/command-line.h>
 +#include <ui/source-init-opts.h>
  
  #include "fatal-signal.h"
  #include "progname.h"
+ #include "relocatable.h"
  
  #include "gettext.h"
  #define _(msgid) gettext (msgid)
  
  
 -static void i18n_init (void);
  static void fpu_init (void);
  static void clean_up (void);
  
@@@ -75,15 -74,12 +76,15 @@@ static struct dataset * the_dataset = N
  static struct lexer *the_lexer;
  static struct source_stream *the_source_stream ;
  
 +const char *argp_program_version = version;
 +const char *argp_program_bug_address = PACKAGE_BUGREPORT;
 +
  /* Program entry point. */
  int
  main (int argc, char **argv)
  {
    int *view_width_p, *view_length_p;
 -
 +  struct command_line_processor *clp;
    set_program_name (argv[0]);
  
    signal (SIGABRT, bug_handler);
  
    the_dataset = create_dataset ();
  
 -  if (parse_command_line (argc, argv, the_source_stream))
 +
 +
 +  clp = command_line_processor_create (_("PSPP --- A program for statistical analysis"),
 +                                     _("FILE1, FILE2 ... FILEn"), NULL);
 +
 +  command_line_processor_add_options (clp, &io_argp,
 +                                    _("Options affecting input and output locations:"), the_source_stream);
 +
 +  command_line_processor_add_options (clp, &test_argp,
 +                                    _("Diagnostic options:"), the_source_stream);
 +
 +  command_line_processor_add_options (clp, &post_init_argp,
 +                                    _("Options affecting syntax and behavior:"), the_source_stream);
 +
 +  command_line_processor_parse (clp, argc, argv);
 +
 +  msg_ui_init (the_source_stream);
 +
 +  if (!settings_get_testing_mode ())
      {
 -      msg_ui_init (the_source_stream);
 -      if (!settings_get_testing_mode ())
 -        outp_read_devices ();
 +      outp_read_devices ();
 +    }
 +  else
 +    {
 +      outp_configure_driver_line
 +      (
 +       ss_cstr ("raw-ascii:ascii:listing:width=9999 length=9999 "
 +                "output-file=\"pspp.list\" emphasis=none "
 +                "headers=off paginate=off squeeze=on "
 +                "top-margin=0 bottom-margin=0"));
 +    }
 +
 +  the_lexer = lex_create (the_source_stream);
 +
 +  for (;;)
 +    {
 +      int result = cmd_parse (the_lexer, the_dataset);
 +
 +      if (result == CMD_EOF || result == CMD_FINISH)
 +      break;
 +      if (result == CMD_CASCADING_FAILURE &&
 +        !getl_is_interactive (the_source_stream))
 +      {
 +        msg (SE, _("Stopping syntax file processing here to avoid "
 +                   "a cascade of dependent command failures."));
 +        getl_abort_noninteractive (the_source_stream);
 +      }
        else
 -        outp_configure_driver_line (
 -          ss_cstr ("raw-ascii:ascii:listing:width=9999 length=9999 "
 -                   "output-file=\"pspp.list\" emphasis=none "
 -                   "headers=off paginate=off squeeze=on "
 -                   "top-margin=0 bottom-margin=0"));
 -      the_lexer = lex_create (the_source_stream);
 -
 -      for (;;)
 -        {
 -          int result = cmd_parse (the_lexer, the_dataset);
 -
 -          if (result == CMD_EOF || result == CMD_FINISH)
 -            break;
 -          if (result == CMD_CASCADING_FAILURE &&
 -            !getl_is_interactive (the_source_stream))
 -            {
 -              msg (SE, _("Stopping syntax file processing here to avoid "
 -                         "a cascade of dependent command failures."));
 -              getl_abort_noninteractive (the_source_stream);
 -            }
 -          else
 -            check_msg_count (the_source_stream);
 -        }
 +      check_msg_count (the_source_stream);
      }
  
 +
    clean_up ();
    return any_errors ();
  }
  \f
 -static void
 -i18n_init (void)
 -{
 -#if ENABLE_NLS
 -#if HAVE_LC_MESSAGES
 -  setlocale (LC_MESSAGES, "");
 -#endif
 -#if HAVE_LC_PAPER
 -  setlocale (LC_PAPER, "");
 -#endif
 -  bindtextdomain (PACKAGE, relocate (locale_dir));
 -  textdomain (PACKAGE);
 -#endif /* ENABLE_NLS */
 -}
  static void
  fpu_init (void)
  {
@@@ -219,6 -209,5 +221,6 @@@ clean_up (void
        readln_uninitialize ();
        outp_done ();
        msg_ui_done ();
 +      i18n_done ();
      }
  }