? asymp.SPS
? asymp.html
? binomial-example.sps
? binomial.c.gcov
? binomial.sps
? case.h.gcov
? diffs
? exact-asymp.SPS
? exact-asymp.html
? exact.SPS
? exact.html
? npar-example.sps
? npar-parse.sps
? npar.c.gcov
? npar.patch
? npar.q.gcov
? npar.sps
? npar2.patch
? npar3.patch
? nparxx.sps
? r.sps
? x.tar
? xsq.patch
? doc/.dirstamp
? gl/.deps
? gl/Makefile
? gl/Makefile.am
? gl/Makefile.in
? gl/alloca.c
? gl/alloca.h
? gl/alloca_.h
? gl/asnprintf.c
? gl/asprintf.c
? gl/c-ctype.c
? gl/c-ctype.h
? gl/c-strtod.c
? gl/c-strtod.h
? gl/charset.alias
? gl/config.charset
? gl/configmake.h
? gl/error.c
? gl/error.h
? gl/exit.h
? gl/exitfail.c
? gl/exitfail.h
? gl/full-read.c
? gl/full-read.h
? gl/full-write.c
? gl/full-write.h
? gl/getdelim.c
? gl/getdelim.h
? gl/gethostname.c
? gl/getline.c
? gl/getline.h
? gl/getlogin_r.c
? gl/getlogin_r.h
? gl/getopt.c
? gl/getopt1.c
? gl/getopt_.h
? gl/getopt_int.h
? gl/gettext.h
? gl/intprops.h
? gl/lbrkprop.h
? gl/linebreak.c
? gl/linebreak.h
? gl/localcharset.c
? gl/localcharset.h
? gl/m4
? gl/mbchar.c
? gl/mbchar.h
? gl/mbuiter.h
? gl/memcasecmp.c
? gl/memcasecmp.h
? gl/memchr.c
? gl/memcmp.c
? gl/memmem.c
? gl/memmem.h
? gl/memmove.c
? gl/memset.c
? gl/minmax.h
? gl/printf-args.c
? gl/printf-args.h
? gl/printf-parse.c
? gl/printf-parse.h
? gl/progname.c
? gl/progname.h
? gl/readlink.c
? gl/ref-add.sed
? gl/ref-add.sin
? gl/ref-del.sed
? gl/ref-del.sin
? gl/safe-read.c
? gl/safe-read.h
? gl/safe-write.c
? gl/safe-write.h
? gl/size_max.h
? gl/snprintf.c
? gl/snprintf.h
? gl/stat-macros.h
? gl/stdbool_.h
? gl/stdint.h
? gl/stdint_.h
? gl/stpcpy.c
? gl/stpcpy.h
? gl/strcase.h
? gl/strcasecmp.c
? gl/strcspn.c
? gl/strerror.c
? gl/strftime.c
? gl/strftime.h
? gl/strncasecmp.c
? gl/strndup.c
? gl/strndup.h
? gl/strnlen.c
? gl/strnlen.h
? gl/strnlen1.c
? gl/strnlen1.h
? gl/strpbrk.c
? gl/strpbrk.h
? gl/strsep.c
? gl/strsep.h
? gl/strstr.c
? gl/strstr.h
? gl/strtod.c
? gl/strtok_r.c
? gl/strtok_r.h
? gl/strtol.c
? gl/strtoul.c
? gl/time_r.c
? gl/time_r.h
? gl/utf16-ucs4.h
? gl/utf8-ucs4.h
? gl/vasnprintf.c
? gl/vasnprintf.h
? gl/vasprintf.c
? gl/vasprintf.h
? gl/vsnprintf.c
? gl/vsnprintf.h
? gl/wcwidth.h
? gl/xalloc-die.c
? gl/xalloc.h
? gl/xasprintf.c
? gl/xmalloc.c
? gl/xreadlink.c
? gl/xreadlink.h
? gl/xsize.h
? gl/xstrndup.c
? gl/xstrndup.h
? gl/xvasprintf.c
? gl/xvasprintf.h
? intl/ChangeLog
? intl/Makefile
? intl/Makefile.in
? intl/VERSION
? intl/bindtextdom.c
? intl/config.charset
? intl/dcgettext.c
? intl/dcigettext.c
? intl/dcngettext.c
? intl/dgettext.c
? intl/dngettext.c
? intl/eval-plural.h
? intl/explodename.c
? intl/finddomain.c
? intl/gettext.c
? intl/gettextP.h
? intl/gmo.h
? intl/hash-string.h
? intl/intl-compat.c
? intl/l10nflist.c
? intl/langprefs.c
? intl/libgnuintl.h.in
? intl/loadinfo.h
? intl/loadmsgcat.c
? intl/localcharset.c
? intl/localcharset.h
? intl/locale.alias
? intl/localealias.c
? intl/localename.c
? intl/log.c
? intl/ngettext.c
? intl/os2compat.c
? intl/os2compat.h
? intl/osdep.c
? intl/plural-exp.c
? intl/plural-exp.h
? intl/plural.c
? intl/plural.y
? intl/printf-args.c
? intl/printf-args.h
? intl/printf-parse.c
? intl/printf-parse.h
? intl/printf.c
? intl/ref-add.sin
? intl/ref-del.sin
? intl/relocatable.c
? intl/relocatable.h
? intl/textdomain.c
? intl/vasnprintf.c
? intl/vasnprintf.h
? intl/vasnwprintf.h
? intl/wprintf-parse.h
? intl/xsize.h
? lib/gsl-extras/.deps
? lib/gsl-extras/.dirstamp
? lib/gtksheet/.deps
? lib/gtksheet/.dirstamp
? lib/linreg/.deps
? lib/linreg/.dirstamp
? m4/Makefile.am
? m4/codeset.m4
? m4/gettext.m4
? m4/glibc2.m4
? m4/glibc21.m4
? m4/iconv.m4
? m4/intdiv0.m4
? m4/intmax.m4
? m4/inttypes-pri.m4
? m4/inttypes.m4
? m4/inttypes_h.m4
? m4/isc-posix.m4
? m4/lcmessage.m4
? m4/lib-ld.m4
? m4/lib-link.m4
? m4/lib-prefix.m4
? m4/longdouble.m4
? m4/longlong.m4
? m4/nls.m4
? m4/po.m4
? m4/printf-posix.m4
? m4/progtest.m4
? m4/signed.m4
? m4/size_max.m4
? m4/stdint_h.m4
? m4/uintmax_t.m4
? m4/ulonglong.m4
? m4/wchar_t.m4
? m4/wint_t.m4
? m4/xsize.m4
? src/data/.deps
? src/data/.dirstamp
? src/language/.deps
? src/language/.dirstamp
? src/language/control/.deps
? src/language/control/.dirstamp
? src/language/data-io/.deps
? src/language/data-io/.dirstamp
? src/language/data-io/file-handle.c
? src/language/data-io/list.c
? src/language/dictionary/.deps
? src/language/dictionary/.dirstamp
? src/language/expressions/.deps
? src/language/expressions/.dirstamp
? src/language/lexer/.deps
? src/language/lexer/.dirstamp
? src/language/lexer/q2c
? src/language/stats/.deps
? src/language/stats/.dirstamp
? src/language/stats/correlations.c
? src/language/stats/crosstabs.c
? src/language/stats/examine.c
? src/language/stats/frequencies.c
? src/language/stats/means.c
? src/language/stats/npar.c
? src/language/stats/oneway.c
? src/language/stats/rank.c
? src/language/stats/regression.c
? src/language/stats/t-test.c
? src/language/tests/.deps
? src/language/tests/.dirstamp
? src/language/utilities/.deps
? src/language/utilities/.dirstamp
? src/language/utilities/set.c
? src/language/xforms/.deps
? src/language/xforms/.dirstamp
? src/libpspp/.deps
? src/libpspp/.dirstamp
? src/libpspp/version.c
? src/math/.deps
? src/math/.dirstamp
? src/math/linreg/.deps
? src/math/linreg/.dirstamp
? src/math/ts/.deps
? src/math/ts/.dirstamp
? src/output/.deps
? src/output/.dirstamp
? src/output/charts/.deps
? src/output/charts/.dirstamp
? src/ui/.deps
? src/ui/.dirstamp
? src/ui/gui/.deps
? src/ui/gui/.dirstamp
? src/ui/gui/psppire
? src/ui/terminal/.deps
? src/ui/terminal/.dirstamp
? src/ui/terminal/pspp
? tests/command/npar-binomial.sh
? tests/formats/.deps
? tests/formats/.dirstamp
? tests/formats/inexactify
? tests/libpspp/.deps
? tests/libpspp/.dirstamp
? tests/libpspp/ll-test
? tests/libpspp/llx-test
Index: src/data/casefilter.c
===================================================================
RCS file: /sources/pspp/pspp/src/data/casefilter.c,v
retrieving revision 1.3
diff -b -w -U3 -r1.3 casefilter.c
--- src/data/casefilter.c	10 Dec 2006 02:33:43 -0000	1.3
+++ src/data/casefilter.c	10 Dec 2006 02:41:26 -0000
@@ -98,7 +98,7 @@
    filter considers. N_VARS is the size of VARS */
 void
 casefilter_add_variables (struct casefilter *filter,
-			   struct variable **vars, int n_vars)
+			  struct variable *const *vars, int n_vars)
 {
   int i;
 
@@ -118,5 +118,3 @@
   free (filter->vars);
   free (filter);
 }
-
-
Index: src/data/casefilter.h
===================================================================
RCS file: /sources/pspp/pspp/src/data/casefilter.h,v
retrieving revision 1.1
diff -b -w -U3 -r1.1 casefilter.h
--- src/data/casefilter.h	5 Nov 2006 00:35:43 -0000	1.1
+++ src/data/casefilter.h	10 Dec 2006 02:41:26 -0000
@@ -36,7 +36,8 @@
 
 /* Add the variables in VARS to the list of variables for which the
    filter considers. N_VARS is the size of VARS */
-void casefilter_add_variables (struct casefilter *, struct variable **, int);
+void casefilter_add_variables (struct casefilter *, 
+			       struct variable *const*, int);
 
 /* Destroy the filter FILTER */
 void casefilter_destroy (struct casefilter *); 
@@ -44,7 +45,10 @@
 /* Returns true iff the entire case should be skipped */
 bool casefilter_skip_case (const struct casefilter *, const struct ccase *);
 
-/* Returns true iff the variable V in case C is missing */
+/* Returns true iff the variable V in case C is missing.
+   Note that this function's behaviour is independent of the set of 
+   variables  contained by the filter.
+ */
 bool casefilter_variable_missing (const struct casefilter *f, 
 				   const struct ccase *c, 
 				   const struct variable *v);
Index: src/data/variable.c
===================================================================
RCS file: /sources/pspp/pspp/src/data/variable.c,v
retrieving revision 1.15
diff -b -w -U3 -r1.15 variable.c
--- src/data/variable.c	7 Dec 2006 06:31:04 -0000	1.15
+++ src/data/variable.c	10 Dec 2006 02:41:27 -0000
@@ -30,6 +30,7 @@
 #include <libpspp/misc.h>
 #include <libpspp/str.h>
 #include "value-labels.h"
+#include <minmax.h>
 
 #include "minmax.h"
 
@@ -123,21 +124,6 @@
   free (v->aux);
 }
 
-/* Duplicate a value.
-   The caller is responsible for freeing the returned value
-*/
-union value *
-value_dup (const union value *val, int width)
-{
-  size_t bytes = MAX(width, sizeof *val);
-
-  union value *v = xmalloc (bytes);
-  memcpy (v, val, bytes);
-  return v;
-}
-
-
-
 /* Compares A and B, which both have the given WIDTH, and returns
    a strcmp()-type result. */
 int
@@ -149,6 +135,18 @@
     return memcmp (a->s, b->s, MIN(MAX_SHORT_STRING, width));
 }
 
+/* Duplicate a value.
+   The caller is responsible for freeing the returned value
+*/
+union value *
+value_dup (const union value *val, int width)
+{
+  size_t bytes = MAX (width, sizeof (*val));
+  union value *v = xmalloc (bytes);
+  memcpy (v, val, bytes);
+  return v;
+}
+
 /* Create a hash of v */
 unsigned 
 hash_value(const union value  *v, int width)
@@ -287,6 +285,31 @@
   return hsh_hash_case_string (var_get_name (v));
 }
 
+
+/* A hsh_compare_func that orders variables A and B by their
+   indices. */
+int
+compare_var_index (const void *a_, const void *b_, const void *aux UNUSED) 
+{
+  const struct variable *a = a_;
+  const struct variable *b = b_;
+
+  if ( a->index < b->index ) 
+	return -1;
+
+  return (a->index > b->index);
+}
+
+/* A hsh_hash_func that hashes variable V based on its index. */
+unsigned
+hash_var_index (const void *v_, const void *aux UNUSED) 
+{
+  const struct variable *v = v_;
+
+  return v->index;
+}
+
+
 /* A hsh_compare_func that orders pointers to variables A and B
    by their names. */
 int
Index: src/data/variable.h
===================================================================
RCS file: /sources/pspp/pspp/src/data/variable.h,v
retrieving revision 1.15
diff -b -w -U3 -r1.15 variable.h
--- src/data/variable.h	7 Dec 2006 06:31:04 -0000	1.15
+++ src/data/variable.h	10 Dec 2006 02:41:27 -0000
@@ -113,6 +113,8 @@
 void var_set_name (struct variable *, const char *);
 bool var_is_valid_name (const char *, bool issue_error);
 bool var_is_plausible_name (const char *name, bool issue_error);
+int compare_var_index (const void *, const void *, const void *);
+unsigned hash_var_index (const void *, const void *);
 int  compare_var_names (const void *, const void *, const void *);
 unsigned hash_var_name (const void *, const void *);
 
@@ -218,10 +220,16 @@
 */
 const char * var_to_string(const struct variable *var);
 
+/* Duplicate a value.
+   The caller is responsible for freeing the returned value
+*/
+union value * value_dup (const union value *val, int width);
+
 
 int width_to_bytes(int width);
 
 union value * value_dup (const union value *val, int width);
 
 
+
 #endif /* !variable.h */
Index: src/language/command.def
===================================================================
RCS file: /sources/pspp/pspp/src/language/command.def,v
retrieving revision 1.10
diff -b -w -U3 -r1.10 command.def
--- src/language/command.def	22 Nov 2006 14:27:16 -0000	1.10
+++ src/language/command.def	10 Dec 2006 02:41:28 -0000
@@ -107,6 +107,7 @@
 DEF_CMD (S_DATA, 0, "MATCH FILES", cmd_match_files)
 DEF_CMD (S_DATA, 0, "MEANS", cmd_means)
 DEF_CMD (S_DATA, 0, "MODIFY VARS", cmd_modify_vars)
+DEF_CMD (S_DATA, 0, "NPAR TESTS", cmd_npar_tests)
 DEF_CMD (S_DATA, 0, "ONEWAY", cmd_oneway)
 DEF_CMD (S_DATA, 0, "PEARSON CORRELATIONS", cmd_correlations)
 DEF_CMD (S_DATA, 0, "RANK", cmd_rank)
@@ -203,7 +204,6 @@
 UNIMPL_CMD ("NLR", "Non Linear Regression")
 UNIMPL_CMD ("NOMREG", "Multinomial logistic regression")
 UNIMPL_CMD ("NONPAR CORR", "Nonparametric correlation")
-UNIMPL_CMD ("NPAR TESTS", "Nonparametric tests")
 UNIMPL_CMD ("NUMBERED", "")
 UNIMPL_CMD ("OLAP CUBES", "On-line analytical processing")
 UNIMPL_CMD ("OMS", "Output management")
Index: src/language/stats/automake.mk
===================================================================
RCS file: /sources/pspp/pspp/src/language/stats/automake.mk,v
retrieving revision 1.4
diff -b -w -U3 -r1.4 automake.mk
--- src/language/stats/automake.mk	15 Jul 2006 04:38:57 -0000	1.4
+++ src/language/stats/automake.mk	10 Dec 2006 02:41:28 -0000
@@ -8,6 +8,7 @@
 	src/language/stats/examine.c \
 	src/language/stats/frequencies.c \
 	src/language/stats/means.c \
+	src/language/stats/npar.c \
 	src/language/stats/oneway.c \
 	src/language/stats/rank.c \
 	src/language/stats/regression.c \
@@ -29,4 +30,13 @@
 	src/language/stats/sort-criteria.c \
 	src/language/stats/sort-criteria.h \
 	src/language/stats/flip.c \
-	src/language/stats/regression-export.h 
+	src/language/stats/regression-export.h \
+	src/language/stats/binomial.c \
+	src/language/stats/binomial.h \
+	src/language/stats/chisquare.c \
+	src/language/stats/chisquare.h \
+	src/language/stats/npar-summary.c \
+	src/language/stats/npar-summary.h \
+	src/language/stats/freq.h \
+	src/language/stats/freq.c
+
Index: src/language/stats/binomial.c
===================================================================
RCS file: src/language/stats/binomial.c
diff -N src/language/stats/binomial.c
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ src/language/stats/binomial.c	10 Dec 2006 02:41:28 -0000
@@ -0,0 +1,329 @@
+/* PSPP - computes sample statistics.
+   Copyright (C) 2006 Free Software Foundation, Inc.
+   Written by John Darrington <john@darrington.wattle.id.au>
+
+   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 2 of the
+   License, or (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful, but
+   WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301, USA. */
+
+#include <config.h>
+#include <libpspp/compiler.h>
+#include <output/table.h>
+#include <libpspp/alloc.h>
+
+#include <data/case.h>
+#include <data/casefile.h>
+#include <data/dictionary.h>
+#include <data/procedure.h>
+#include <data/variable.h>
+#include <data/value-labels.h>
+#include <data/casefilter.h>
+
+#include <libpspp/message.h>
+#include <libpspp/assertion.h>
+
+#include "binomial.h"
+#include "freq.h"
+
+#include "gettext.h"
+#define _(msgid) gettext (msgid)
+
+#include <libpspp/misc.h>
+
+#include <gsl/gsl_cdf.h>
+#include <gsl/gsl_randist.h>
+#include <gsl-extras/gsl-extras.h>
+
+#include <minmax.h>
+
+#define ASYMPTOTIC_THRESHOLD 25
+
+#include <libpspp/hash.h>
+
+static  bool asymp_result_occured = false;
+static  bool exact_result_occured = false; 
+
+static double 
+calculate_binomial(double n1, double n2, double p, bool *asymp)
+{
+  double n = n1 + n2;
+  const bool test_reversed = n1 / n > p ;
+  double m = test_reversed ? n2 : n1;
+  double sig2tailed;
+
+  *asymp = ( n > ASYMPTOTIC_THRESHOLD ) ;
+
+  /* SPSS Statistical Algorithms, Siegal and Castellan, SPSS v12
+     and intuition all seem to have totally different methods for
+     calculating the binomial significance. Each method gives an
+     approximate agreement in most cases, but some cases the results are
+     totally different.
+  */
+
+  if (*asymp == false)
+    {
+      const double pstar = test_reversed ? 1 - p : p;
+
+      double sum = 0.0;
+      int i;
+
+      for ( i = 0 ; i <= m ; ++i )
+	sum += gsl_ran_binomial_pdf (i, pstar, n);
+
+      sig2tailed = 2 * sum;
+
+      /* SPSS Statistical Algorithms says to subtract
+	 gsl_ran_binomial_pdf (m, pstar, n)
+	 But SPSS doesn't seem to
+	 actually do that.
+	 Neither does it seem sensible.
+      */
+    }
+  else
+    {
+      double z1, z2;
+      double pz1, pz2;
+      z1 = (n1  + 0.5 - n * p) / sqrt (n * p * (1 - p));
+      z2 = (n1  - 0.5 - n * p) / sqrt (n * p * (1 - p));
+
+      pz1 = gsl_cdf_ugaussian_P(z1);
+      pz2 = gsl_cdf_ugaussian_P(z2);
+ 
+      sig2tailed =  2 * pz1; /* IS THIS RIGHT ??? */
+    }
+
+  if ( p != 0.5 ) 
+    return sig2tailed / 2.0;
+
+
+  return sig2tailed;
+}
+
+static void
+do_binomial (const struct dictionary *dict, 
+	     const struct casefile *cf, 
+	     const struct binomial_test *bst, 
+	     struct freq *cat1, 
+	     struct freq *cat2,
+	     const struct casefilter *filter
+	     )
+{
+  bool warn = true;
+
+  const struct one_sample_test *ost = (const struct one_sample_test *) bst;
+  struct ccase c;
+  struct casereader *r = casefile_get_reader (cf, NULL);
+
+  while (casereader_read(r, &c))
+    {
+      int v;
+      double w = 
+	dict_get_case_weight (dict, &c, &warn);
+      
+      for (v = 0 ; v < ost->n_vars ; ++v ) 
+	{
+	  const struct variable *var = ost->vars[v];
+	  const union value *value = case_data (&c, var->fv);
+
+	  if ( casefilter_variable_missing (filter, &c, var))
+	    break;
+
+	  if ( NULL == cat1[v].value ) 
+	    {
+	      cat1[v].value = value_dup (value, var->width);
+	      cat1[v].count = w;
+	    }
+	  else if ( 0 == compare_values (cat1[v].value, value, var->width))
+	    cat1[v].count += w;
+	  else if ( NULL == cat2[v].value ) 
+	    {
+	      cat2[v].value = value_dup (value, var->width);
+	      cat2[v].count = w;
+	    }
+	  else if ( 0 == compare_values (cat2[v].value, value, var->width))
+	    cat2[v].count += w;
+	  else if ( bst->category1 == SYSMIS)
+	    msg (ME, _("Variable %s is not dichotomous"), var->name);
+	}
+
+      case_destroy (&c);
+    }
+  casereader_destroy (r);
+}
+
+
+static void
+tab_custom_dimensions (struct tab_table *t, struct outp_driver *d)
+{
+  int i;
+
+  assert (t != NULL);
+  
+  for (i = 0; i < t->nc; i++)
+    {
+      if (( i == 6 && ! asymp_result_occured) || 
+	  ( i == 7 && ! exact_result_occured) )
+	t->w[i] = -1;
+      else
+	t->w[i] = tab_natural_width (t, d, i);
+    }
+  
+  for (i = 0; i < t->nr; i++)
+    t->h[i] = tab_natural_height (t, d, i);
+}
+
+
+void 
+binomial_execute (const struct dataset *ds, 
+		  const struct casefile *cf, 
+		  struct casefilter *filter, 
+		  const struct npar_test *test)
+{
+  int v;
+  const struct binomial_test *bst = (const struct binomial_test *) test;
+  const struct one_sample_test *ost = (const struct one_sample_test*) test;
+
+  struct freq *cat1 = xzalloc (sizeof (*cat1) * ost->n_vars);
+  struct freq *cat2 = xzalloc (sizeof (*cat1) * ost->n_vars);
+  struct tab_table *table ;
+
+  assert ((bst->category1 == SYSMIS) == (bst->category2 == SYSMIS) );
+
+  if ( bst->category1 != SYSMIS ) 
+    {
+      union value v;
+      v.f = bst->category1; 
+      cat1->value = value_dup (&v, 0);
+    }
+
+  if ( bst->category2 != SYSMIS ) 
+    {
+      union value v;
+      v.f = bst->category2; 
+      cat2->value = value_dup (&v, 0);
+    }
+
+  asymp_result_occured = false;
+  exact_result_occured = false; 
+
+  do_binomial (dataset_dict(ds), cf, bst, cat1, cat2, filter);
+
+  table = tab_create (8, ost->n_vars * 3 + 1, 0);
+
+  tab_dim (table, tab_custom_dimensions);
+
+  tab_title (table, _("Binomial Test"));
+	
+  tab_headers (table, 2, 0, 1, 0);
+
+  tab_box (table, TAL_1, TAL_1, -1, TAL_1, 
+	   0, 0, table->nc - 1, tab_nr(table) - 1 );
+
+  for (v = 0 ; v < ost->n_vars; ++v) 
+    {
+      bool asymp_result;
+      int col;
+      double n_total, sig;
+      const struct variable *var = ost->vars[v];
+      tab_hline (table, TAL_1, 0, tab_nc (table) -1, 1 + v * 3);
+
+      /* Titles */
+      tab_text (table, 0, 1 + v * 3, TAB_LEFT, 
+		var_to_string (var));
+      
+      tab_text (table, 1, 1 + v * 3, TAB_LEFT, 
+		_("Group1"));
+
+      tab_text (table, 1, 2 + v * 3, TAB_LEFT, 
+		_("Group2"));
+
+      tab_text (table, 1, 3 + v * 3, TAB_LEFT, 
+		_("Total"));
+
+      /* Test Prop */
+      tab_float (table, 5, 1 + v * 3, TAB_NONE, bst->p, 8, 3);
+
+      /* Category labels */
+      tab_text (table, 2, 1 + v * 3, TAB_NONE, 
+		value_to_string (cat1[v].value, var));
+      tab_text (table, 2, 2 + v * 3, TAB_NONE, 
+		value_to_string (cat2[v].value, var));
+
+      /* Observed N */
+      tab_float (table, 3, 1 + v * 3, TAB_NONE,
+		 cat1[v].count, 8, 0);
+
+      tab_float (table, 3, 2 + v * 3, TAB_NONE,
+		 cat2[v].count, 8, 0);
+
+      n_total = cat1[v].count + cat2[v].count;
+
+
+      tab_float (table, 3, 3 + v * 3, TAB_NONE,
+		 n_total, 8, 0);
+
+      /* Observed Proportions */
+
+      tab_float (table, 4, 1 + v * 3, TAB_NONE,
+		 cat1[v].count / n_total, 8, 3);
+
+      tab_float (table, 4, 2 + v * 3, TAB_NONE,
+		 cat2[v].count / n_total, 8, 3);
+
+      tab_float (table, 4, 3 + v * 3, TAB_NONE,
+		 (cat1[v].count + cat2[v].count) / n_total, 8, 2);
+
+
+      /* Significance */
+      sig = calculate_binomial (cat1[v].count, cat2[v].count, 
+				       bst->p, &asymp_result);
+
+      if ( asymp_result ) 
+	{
+	  asymp_result_occured = true;
+	  col = 6;
+	}
+      else
+	{
+	  exact_result_occured = true;
+	  col = 7;
+	}
+
+      tab_float (table, col, 1 + v * 3, TAB_NONE,
+		 sig, 8, 3);
+    }
+
+  tab_text (table,  2, 0,  TAB_CENTER, _("Category"));
+  tab_text (table,  3, 0,  TAB_CENTER, _("N"));
+  tab_text (table,  4, 0,  TAB_CENTER, _("Observed Prop."));
+  tab_text (table,  5, 0,  TAB_CENTER, _("Test Prop."));
+
+  if ( asymp_result_occured ) 
+    tab_text (table,  6, 0,  TAB_CENTER | TAT_PRINTF, 
+	      _("Asymp. Sig. (%d-tailed)"),
+	      bst->p == 0.5 ? 2: 1);
+
+  if ( exact_result_occured ) 
+    tab_text (table,  7, 0,  TAB_CENTER | TAT_PRINTF, 
+	      _("Exact Sig. (%d-tailed)"),
+	      bst->p == 0.5 ? 2: 1);
+
+  tab_vline (table, TAL_2, 2, 0, tab_nr (table) -1);
+
+  free (cat1);
+  free (cat2);
+
+  tab_submit (table);
+
+}
Index: src/language/stats/binomial.h
===================================================================
RCS file: src/language/stats/binomial.h
diff -N src/language/stats/binomial.h
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ src/language/stats/binomial.h	10 Dec 2006 02:41:28 -0000
@@ -0,0 +1,49 @@
+/* PSPP - computes sample statistics.
+   Copyright (C) 2006 Free Software Foundation, Inc.
+   Written by John Darrington <john@darrington.wattle.id.au>
+
+   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 2 of the
+   License, or (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful, but
+   WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301, USA. */
+
+#if !binomial_h
+#define binomial_h 1
+
+#include <config.h>
+#include <stddef.h>
+#include <stdbool.h>
+
+#include "npar.h"
+
+
+struct binomial_test
+{
+  struct one_sample_test parent;
+  double p;
+  double category1;
+  double category2;
+  double cutpoint;
+};
+
+
+struct casefile;
+struct dataset;
+
+
+void binomial_execute (const struct dataset *, 
+		       const struct casefile *, 
+		       struct casefilter *, 
+		       const struct npar_test *);
+
+#endif
Index: src/language/stats/chisquare.c
===================================================================
RCS file: src/language/stats/chisquare.c
diff -N src/language/stats/chisquare.c
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ src/language/stats/chisquare.c	10 Dec 2006 02:41:29 -0000
@@ -0,0 +1,497 @@
+/* PSPP - computes sample statistics.
+   Copyright (C) 2006 Free Software Foundation, Inc.
+   Written by John Darrington <john@darrington.wattle.id.au>
+
+   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 2 of the
+   License, or (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful, but
+   WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301, USA. */
+
+#include <config.h>
+#include <libpspp/compiler.h>
+#include <libpspp/assertion.h>
+
+#include <stdlib.h>
+
+#include <data/case.h>
+#include <data/casefile.h>
+#include <data/casefilter.h>
+#include <data/variable.h>
+#include <data/dictionary.h>
+#include <data/procedure.h>
+
+#include <libpspp/message.h>
+#include <libpspp/hash.h>
+#include <libpspp/alloc.h>
+
+#include <gsl/gsl_cdf.h>
+
+#include <output/table.h>
+#include <data/value-labels.h>
+
+#include "npar.h"
+#include "chisquare.h"
+#include "freq.h"
+
+#include <math.h>
+
+#include "gettext.h"
+#define _(msgid) gettext (msgid)
+
+
+
+
+/* Return a hash table containing the frequency counts of each 
+   value of VAR in CF .
+   It is the caller's responsibility to free the hash table when 
+   no longer required.
+*/
+static struct hsh_table *
+create_freq_hash_with_range (const struct dictionary *dict, 
+			     const struct casefile *cf, 
+			     struct casefilter *filter,
+			     const struct variable *var, 
+			     double lo, 
+			     double hi)
+{
+  bool warn = true;
+  float i_d;
+  struct ccase c;
+  struct casereader *r = casefile_get_reader (cf, filter);
+
+  struct hsh_table *freq_hash = 
+    hsh_create (4, compare_freq, hash_freq, 
+		free_freq_mutable_hash,
+		(void *) var);
+
+  /* Populate the hash with zero entries */
+  for (i_d = trunc (lo); i_d <= trunc (hi); i_d += 1.0 ) 
+    {
+      union value the_value;
+      struct freq_mutable *fr = xmalloc (sizeof (*fr));
+
+      the_value.f = i_d;
+
+      fr->value = value_dup (&the_value, 0);
+      fr->count = 0;
+
+      hsh_insert (freq_hash, fr);
+    }
+
+  while (casereader_read(r, &c))
+    {
+      union value obs_value;
+      struct freq **existing_fr;
+      struct freq *fr = xmalloc(sizeof  (*fr));
+      fr->value = case_data(&c, var->fv );
+
+      if ( casefilter_variable_missing (filter, &c, var))
+	{
+	  free (fr);
+	  continue;
+	}
+
+      fr->count = dict_get_case_weight (dict, &c, &warn);
+
+      obs_value.f = trunc (fr->value->f);
+
+      if ( obs_value.f < lo || obs_value.f > hi) 
+	{
+	  free (fr);
+	  case_destroy (&c);
+	  continue;
+	}
+
+      fr->value = &obs_value;
+
+      existing_fr = (struct freq **) hsh_probe (freq_hash, fr);
+
+      /* This must exist in the hash, because we previously populated it 
+	 with zero counts */
+      assert (*existing_fr);
+
+      (*existing_fr)->count += fr->count;
+      free (fr);
+
+      case_destroy (&c);
+    }
+  casereader_destroy (r);
+
+  return freq_hash;
+}
+
+
+/* Return a hash table containing the frequency counts of each 
+   value of VAR in CF .
+   It is the caller's responsibility to free the hash table when 
+   no longer required.
+*/
+static struct hsh_table *
+create_freq_hash (const struct dictionary *dict, 
+		  const struct casefile *cf, 
+		  struct casefilter *filter, 
+		  const struct variable *var)
+{
+  bool warn = true;
+  struct ccase c;
+  struct casereader *r = casefile_get_reader (cf, filter);
+
+  struct hsh_table *freq_hash = 
+    hsh_create (4, compare_freq, hash_freq, 
+		free_freq_hash,
+		(void *) var);
+
+  while (casereader_read(r, &c))
+    {
+      struct freq **existing_fr;
+      struct freq *fr = xmalloc(sizeof  (*fr));
+      fr->value = case_data(&c, var->fv );
+
+      if ( casefilter_variable_missing (filter, &c, var))
+	{
+	  free (fr);
+	  continue;
+	}
+
+      fr->count = dict_get_case_weight (dict, &c, &warn);
+
+      existing_fr = (struct freq **) hsh_probe (freq_hash, fr);
+      if ( *existing_fr) 
+	{
+	  (*existing_fr)->count += fr->count;
+	  free (fr);
+	}
+      else
+	{
+	  *existing_fr = fr;
+	}
+
+      case_destroy (&c);
+    }
+  casereader_destroy (r);
+
+  return freq_hash;
+}
+
+
+
+static struct tab_table *
+create_variable_frequency_table (const struct dictionary *dict, 
+				 const struct casefile *cf, 
+				 struct casefilter *filter,
+				 const struct chisquare_test *test, 
+				 int v, 
+				 struct hsh_table **freq_hash)
+
+{
+  int i;
+  const struct one_sample_test *ost = (const struct one_sample_test*)test;
+  int n_cells;
+  struct tab_table *table ;
+  const struct variable *var =  ost->vars[v];
+
+  *freq_hash = create_freq_hash (dict, cf, filter, var);
+      
+  n_cells = hsh_count (*freq_hash);
+
+  if ( test->n_expected > 0 && n_cells != test->n_expected ) 
+    {
+      msg(ME, _("CHISQUARE test specified %d expected values, but"
+		" %d distinct values were encountered in variable %s."), 
+	  test->n_expected, n_cells, 
+	  var->name
+	  );
+      return NULL;
+    }
+
+  table = tab_create(4, n_cells + 2, 0);
+  tab_dim (table, tab_natural_dimensions);
+
+  tab_title (table, var_to_string(var));
+  tab_text (table, 1, 0, TAB_LEFT, _("Observed N"));
+  tab_text (table, 2, 0, TAB_LEFT, _("Expected N"));
+  tab_text (table, 3, 0, TAB_LEFT, _("Residual"));
+	
+  tab_headers (table, 1, 0, 1, 0);
+
+  tab_box (table, TAL_1, TAL_1, -1, -1, 
+	   0, 0, table->nc - 1, tab_nr(table) - 1 );
+
+  tab_hline (table, TAL_1, 0, tab_nc(table) - 1, 1);
+
+  tab_vline (table, TAL_2, 1, 0, tab_nr(table) - 1);
+  for ( i = 2 ; i < 4 ; ++i ) 
+    tab_vline (table, TAL_1, i, 0, tab_nr(table) - 1);
+
+
+  tab_text (table, 0, table->nr - 1, TAB_LEFT, _("Total"));
+
+  return table;
+}
+
+
+static struct tab_table *
+create_combo_frequency_table (const struct chisquare_test *test)
+{
+  int i;
+  const struct one_sample_test *ost = (const struct one_sample_test*)test;
+
+  struct tab_table *table ;
+
+  int n_cells = test->hi - test->lo + 1;
+
+  table = tab_create(1 + ost->n_vars * 4, n_cells + 3, 0);
+  tab_dim (table, tab_natural_dimensions);
+
+  tab_title (table, _("Frequencies"));
+  for ( i = 0 ; i < ost->n_vars ; ++i ) 
+    {
+      const struct variable *var = ost->vars[i];
+      tab_text (table, i * 4 + 1, 1, TAB_LEFT, _("Category"));
+      tab_text (table, i * 4 + 2, 1, TAB_LEFT, _("Observed N"));
+      tab_text (table, i * 4 + 3, 1, TAB_LEFT, _("Expected N"));
+      tab_text (table, i * 4 + 4, 1, TAB_LEFT, _("Residual"));
+
+      tab_vline (table, TAL_2, i * 4 + 1, 
+		 0, tab_nr (table) - 1);
+
+      tab_vline (table, TAL_1, i * 4 + 2, 
+		 0, tab_nr (table) - 1);
+
+      tab_vline (table, TAL_1, i * 4 + 3, 
+		 1, tab_nr (table) - 1);
+
+      tab_vline (table, TAL_1, i * 4 + 4, 
+		 1, tab_nr (table) - 1);
+
+
+      tab_joint_text (table, 
+		      i * 4 + 1, 0,
+		      i * 4 + 4, 0,
+		      TAB_CENTER, 
+		      var_to_string (var));
+    }
+
+  for ( i = test->lo ; i <= test->hi ; ++i ) 
+    tab_float (table, 0, 2 + i - test->lo, 
+	       TAB_LEFT, 1 + i - test->lo, 8, 0);
+	
+  tab_headers (table, 1, 0, 2, 0);
+
+  tab_box (table, TAL_1, TAL_1, -1, -1, 
+	   0, 0, table->nc - 1, tab_nr(table) - 1 );
+
+  tab_hline (table, TAL_1, 1, tab_nc(table) - 1, 1);
+  tab_hline (table, TAL_1, 0, tab_nc(table) - 1, 2);
+
+  tab_text (table, 0, table->nr - 1, TAB_LEFT, _("Total"));
+
+  return table;
+}
+
+
+static struct tab_table *
+create_stats_table (const struct chisquare_test *test)
+{
+  const struct one_sample_test *ost = (const struct one_sample_test*) test;
+  
+  struct tab_table *table = tab_create (1 + ost->n_vars, 4, 0);
+  tab_dim (table, tab_natural_dimensions);
+  tab_title (table, _("Test Statistics"));
+  tab_headers (table, 1, 0, 1, 0);
+
+  tab_box (table, TAL_1, TAL_1, -1, -1,
+	   0, 0, tab_nc(table) - 1, tab_nr(table) - 1 );
+
+  tab_box (table, -1, -1, -1, TAL_1,
+	   1, 0, tab_nc(table) - 1, tab_nr(table) - 1 );
+
+
+  tab_vline (table, TAL_2, 1, 0, tab_nr (table) - 1);
+  tab_hline (table, TAL_1, 0, tab_nc (table) - 1, 1);
+  
+
+  tab_text (table, 0, 1, TAB_LEFT, _("Chi-Square"));
+  tab_text (table, 0, 2, TAB_LEFT, _("df"));
+  tab_text (table, 0, 3, TAB_LEFT, _("Asymp. Sig."));
+
+  return table;
+}
+
+
+void 
+chisquare_execute (const struct dataset *ds,
+		   const struct casefile *cf, 
+		   struct casefilter *filter,
+		   const struct npar_test *test)
+{
+  const struct dictionary *dict = dataset_dict (ds);
+  int v, i;
+  struct one_sample_test *ost = (struct one_sample_test *) test;
+  struct chisquare_test *cst = (struct chisquare_test *) test;
+  struct tab_table *stats_table = create_stats_table (cst);
+  int n_cells = 0;
+  double total_expected = 0.0;
+
+  double *df = xzalloc (sizeof (*df) * ost->n_vars);
+  double *xsq = xzalloc (sizeof (*df) * ost->n_vars);
+  
+  for ( i = 0 ; i < cst->n_expected ; ++i ) 
+    total_expected += cst->expected[i];
+
+  if ( cst->ranged == false ) 
+    {
+      for ( v = 0 ; v < ost->n_vars ; ++v ) 
+	{
+	  double total_obs = 0.0;
+	  struct hsh_table *freq_hash = NULL;
+	  struct tab_table *freq_table = 
+	    create_variable_frequency_table(dict, cf, filter, cst, 
+					    v, &freq_hash);
+
+	  struct freq **ff = (struct freq **) hsh_sort (freq_hash);
+
+	  if ( NULL == freq_table ) 
+	    {
+	      hsh_destroy (freq_hash);
+	      continue;
+	    }
+
+	  n_cells = hsh_count (freq_hash);
+
+	  for ( i = 0 ; i < n_cells ; ++i ) 
+	    total_obs += ff[i]->count;
+
+	  xsq[v] = 0.0;
+	  for ( i = 0 ; i < n_cells ; ++i ) 
+	    {
+	      double exp;
+	      const union value *observed_value = ff[i]->value;
+
+	      /* The key */
+	      tab_text (freq_table, 0, i + 1, TAB_LEFT, 
+			value_to_string(observed_value, ost->vars[v]));
+
+	      /* The observed N */
+	      tab_float (freq_table, 1, i + 1, TAB_NONE,
+			 ff[i]->count, 8, 0);
+
+	      if ( cst->n_expected > 0 )
+		exp = cst->expected[i] * total_obs / total_expected ; 
+	      else
+		exp = total_obs / (double) n_cells; 
+
+	      tab_float (freq_table, 2, i + 1, TAB_NONE,
+			 exp, 8, 2);
+
+	      /* The residual */
+	      tab_float (freq_table, 3, i + 1, TAB_NONE,
+			 ff[i]->count - exp, 8, 2);
+
+	      xsq[v] += (ff[i]->count - exp) * (ff[i]->count - exp) / exp;
+	    }
+
+	  df[v] = n_cells - 1.0;
+
+	  tab_float (freq_table, 1, i + 1, TAB_NONE,
+		     total_obs, 8, 0);
+
+	  tab_submit (freq_table);
+
+	  hsh_destroy (freq_hash);
+	}
+    }
+  else  /* ranged == true */
+    {
+      struct tab_table *freq_table = create_combo_frequency_table (cst);
+      
+      n_cells = cst->hi - cst->lo + 1;
+
+      for ( v = 0 ; v < ost->n_vars ; ++v ) 
+	{
+	  double total_obs = 0.0;
+	  struct hsh_table *freq_hash = 
+	    create_freq_hash_with_range (dict, cf, filter, ost->vars[v], 
+					 cst->lo, cst->hi);
+
+	  struct freq **ff = (struct freq **) hsh_sort (freq_hash);
+
+	  assert ( n_cells == hsh_count (freq_hash));
+
+	  for ( i = 0 ; i < hsh_count (freq_hash) ; ++i ) 
+	    total_obs += ff[i]->count;
+
+	  xsq[v] = 0.0;
+	  for ( i = 0 ; i < hsh_count (freq_hash) ; ++i ) 
+	    {
+	      double exp;
+
+	      const union value *observed_value = ff[i]->value;
+
+	      /* The key */
+	      tab_text  (freq_table, v * 4 + 1, i + 2 , TAB_LEFT, 
+			 value_to_string(observed_value, ost->vars[v]));
+
+	      /* The observed N */
+	      tab_float (freq_table, v * 4 + 2, i + 2 , TAB_NONE,
+			 ff[i]->count, 8, 0);
+
+	      if ( cst->n_expected > 0 )
+		exp = cst->expected[i] * total_obs / total_expected ; 
+	      else
+		exp = total_obs / (double) hsh_count (freq_hash); 
+
+	      /* The expected N */
+	      tab_float (freq_table, v * 4 + 3, i + 2 , TAB_NONE,
+			 exp, 8, 2);
+
+	      /* The residual */
+	      tab_float (freq_table, v * 4 + 4, i + 2 , TAB_NONE,
+			 ff[i]->count - exp, 8, 2);
+
+	      xsq[v] += (ff[i]->count - exp) * (ff[i]->count - exp) / exp;
+	    }
+
+	  
+	  tab_float (freq_table, v * 4 + 2, tab_nr (freq_table) - 1, TAB_NONE,
+		     total_obs, 8, 0);
+	  
+	  df[v] = n_cells - 1.0;
+	  
+	  hsh_destroy (freq_hash);
+	}
+
+      tab_submit (freq_table);
+    }
+
+
+  /* Populate the summary statistics table */
+  for ( v = 0 ; v < ost->n_vars ; ++v ) 
+    {
+      const struct variable *var = ost->vars[v];
+
+      tab_text (stats_table, 1 + v, 0, TAB_CENTER, var->name);
+
+      tab_float (stats_table, 1 + v, 1, TAB_NONE, xsq[v], 8,3);
+      tab_float (stats_table, 1 + v, 2, TAB_NONE, df[v], 8,0);
+
+      tab_float (stats_table, 1 + v, 3, TAB_NONE, 
+		 gsl_cdf_chisq_Q (xsq[v], df[v]), 8,3);
+    }
+
+  free (xsq);
+  free (df);
+
+  tab_submit (stats_table);
+}
+
Index: src/language/stats/chisquare.h
===================================================================
RCS file: src/language/stats/chisquare.h
diff -N src/language/stats/chisquare.h
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ src/language/stats/chisquare.h	10 Dec 2006 02:41:29 -0000
@@ -0,0 +1,56 @@
+/* PSPP - computes sample statistics.
+   Copyright (C) 2006 Free Software Foundation, Inc.
+   Written by John Darrington <john@darrington.wattle.id.au>
+
+   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 2 of the
+   License, or (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful, but
+   WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301, USA. */
+
+#if !chisquare_h
+#define chisquare_h 1
+
+#include <config.h>
+#include <stddef.h>
+#include <stdbool.h>
+
+#include "npar.h"
+struct chisquare_test
+{
+  struct one_sample_test parent;  
+
+  bool ranged ;     /* True if this test has a range specified */
+
+  int lo;           /* Lower bound of range (undefined if RANGED is false) */
+  int hi;           /* Upper bound of range (undefined if RANGED is false) */
+
+  double *expected; 
+  int n_expected;
+};
+
+struct casefile;
+struct dictionary ;
+struct hsh_table;
+
+void chisquare_insert_variables (const struct npar_test *test,
+				 struct hsh_table *variables);
+
+
+void chisquare_execute (const struct dataset *ds, 
+			const struct casefile *cf, 
+			struct casefilter *filter,
+			const struct npar_test *test);
+
+
+
+#endif
Index: src/language/stats/freq.c
===================================================================
RCS file: src/language/stats/freq.c
diff -N src/language/stats/freq.c
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ src/language/stats/freq.c	10 Dec 2006 02:41:29 -0000
@@ -0,0 +1,58 @@
+/* PSPP - computes sample statistics.
+   Copyright (C) 2006 Free Software Foundation, Inc.
+   Written by John Darrington <john@darrington.wattle.id.au>
+
+   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 2 of the
+   License, or (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful, but
+   WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301, USA. */
+
+#include <config.h>
+#include <data/variable.h>
+#include <stdlib.h>
+
+#include "freq.h"
+
+int 
+compare_freq ( const void *_f1, const void *_f2, const void *_var)
+{
+  const struct freq *f1 = _f1;
+  const struct freq *f2 = _f2;
+  const struct variable *var = _var;
+
+  return  compare_values (f1->value, f2->value, var->width);
+}
+
+unsigned int
+hash_freq (const void *_f, const void *_var)
+{
+  const struct freq *f = _f;
+  const struct variable *var  = _var;
+
+  return hash_value (f->value, var->width);
+}
+
+/* Free function to be used on FR whose value parameter has been copied */
+void 
+free_freq_mutable_hash (void *fr, const void *var UNUSED)
+{
+  struct freq_mutable *freq = fr;
+  free (freq->value);
+  free (freq);
+}
+
+void 
+free_freq_hash (void *fr, const void *var UNUSED)
+{
+  free (fr);
+}
Index: src/language/stats/freq.h
===================================================================
RCS file: src/language/stats/freq.h
diff -N src/language/stats/freq.h
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ src/language/stats/freq.h	10 Dec 2006 02:41:29 -0000
@@ -0,0 +1,51 @@
+/* PSPP - computes sample statistics.
+   Copyright (C) 2006 Free Software Foundation, Inc.
+   Written by John Darrington <john@darrington.wattle.id.au>
+
+   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 2 of the
+   License, or (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful, but
+   WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301, USA. */
+
+#ifndef freq_h
+#define freq_h
+
+union value ;
+/* Frequency table entry. */
+struct freq
+  {
+    const union value *value;	/* The value. */
+    double count;		/* The number of occurrences of the value. */
+  };
+
+/* Non const version of frequency table entry. */
+struct freq_mutable
+  {
+    union value *value;	        /* The value. */
+    double count;		/* The number of occurrences of the value. */
+  };
+
+
+int compare_freq ( const void *_f1, const void *_f2, const void *_var);
+
+unsigned int hash_freq (const void *_f, const void *_var);
+
+/* Free function for struct freq */
+void free_freq_hash (void *fr, const void *aux);
+
+/* Free function for struct freq_mutable */
+void free_freq_mutable_hash (void *fr, const void *var UNUSED);
+
+
+
+#endif
Index: src/language/stats/frequencies.q
===================================================================
RCS file: /sources/pspp/pspp/src/language/stats/frequencies.q,v
retrieving revision 1.24
diff -b -w -U3 -r1.24 frequencies.q
--- src/language/stats/frequencies.q	5 Dec 2006 15:10:21 -0000	1.24
+++ src/language/stats/frequencies.q	10 Dec 2006 02:41:29 -0000
@@ -58,6 +58,8 @@
 #include <output/output.h>
 #include <output/table.h>
 
+#include "freq.h"
+
 #include "minmax.h"
 
 #include "gettext.h"
@@ -203,13 +205,6 @@
 
 /* Frequency tables. */
 
-/* Frequency table entry. */
-struct freq
-  {
-    union value *v;		/* The value. */
-    double c;			/* The number of occurrences of the value. */
-  };
-
 /* Types of frequency tables. */
 enum
   {
@@ -288,7 +283,6 @@
 static void dump_statistics (struct variable *, int show_varname);
 static void cleanup_freq_tab (struct variable *);
 
-static hsh_hash_func hash_value_numeric, hash_value_alpha;
 static hsh_compare_func compare_value_numeric_a, compare_value_alpha_a;
 static hsh_compare_func compare_value_numeric_d, compare_value_alpha_d;
 static hsh_compare_func compare_freq_numeric_a, compare_freq_alpha_a;
@@ -528,16 +522,16 @@
               struct freq target;
 	      struct freq **fpp;
 
-              target.v = (union value *) val;
+              target.value = (union value *) val;
               fpp = (struct freq **) hsh_probe (ft->data, &target);
 
 	      if (*fpp != NULL)
-		(*fpp)->c += weight;
+		(*fpp)->count += weight;
 	      else
 		{
 		  struct freq *fp = pool_alloc (gen_pool, sizeof *fp);
-                  fp->c = weight;
-                  fp->v = pool_clone (gen_pool,
+                  fp->count = weight;
+                  fp->value = pool_clone (gen_pool,
                                       val, MAX (MAX_SHORT_STRING, vf->width));
                   *fpp = fp;
 		}
@@ -582,20 +576,7 @@
 
       if (ft->mode == FRQM_GENERAL)
 	{
-          hsh_hash_func *hash;
-	  hsh_compare_func *compare;
-
-	  if (var_is_numeric (v))
-            {
-              hash = hash_value_numeric;
-              compare = compare_value_numeric_a; 
-            }
-	  else 
-            {
-              hash = hash_value_alpha;
-              compare = compare_value_alpha_a;
-            }
-	  ft->data = hsh_create (16, compare, hash, NULL, v);
+	  ft->data = hsh_create (16, compare_freq, hash_freq, NULL, v);
 	}
       else
 	{
@@ -722,7 +703,7 @@
   const struct freq *f = f_;
   const struct variable *v = v_;
 
-  return !var_is_value_missing (v, f->v);
+  return !var_is_value_missing (v, f->value);
 }
 
 /* Summarizes the frequency table data for variable V. */
@@ -767,7 +748,7 @@
   for(i = 0 ;  i < ft->n_valid ; ++i ) 
     {
       f = &ft->valid[i];
-      ft->valid_cases += f->c;
+      ft->valid_cases += f->count;
 
     }
 
@@ -775,7 +756,7 @@
   for(i = 0 ;  i < ft->n_missing ; ++i ) 
     {
       f = &ft->missing[i];
-      ft->total_cases += f->c;
+      ft->total_cases += f->count;
     }
 
 }
@@ -996,25 +977,6 @@
 
 /* Comparison functions. */
 
-/* Hash of numeric values. */
-static unsigned
-hash_value_numeric (const void *value_, const void *aux UNUSED)
-{
-  const struct freq *value = value_;
-  return hsh_hash_double (value->v[0].f);
-}
-
-/* Hash of string values. */
-static unsigned
-hash_value_alpha (const void *value_, const void *v_)
-{
-  const struct freq *value = value_;
-  const struct variable *v = v_;
-  struct var_freqs *vf = get_var_freqs (v);
-
-  return hsh_hash_bytes (value->v[0].s, vf->width);
-}
-
 /* Ascending numeric compare of values. */
 static int
 compare_value_numeric_a (const void *a_, const void *b_, const void *aux UNUSED)
@@ -1022,9 +984,9 @@
   const struct freq *a = a_;
   const struct freq *b = b_;
 
-  if (a->v[0].f > b->v[0].f)
+  if (a->value[0].f > b->value[0].f)
     return 1;
-  else if (a->v[0].f < b->v[0].f)
+  else if (a->value[0].f < b->value[0].f)
     return -1;
   else
     return 0;
@@ -1039,7 +1001,7 @@
   const struct variable *v = v_;
   struct var_freqs *vf = get_var_freqs (v);
 
-  return memcmp (a->v[0].s, b->v[0].s, vf->width);
+  return memcmp (a->value[0].s, b->value[0].s, vf->width);
 }
 
 /* Descending numeric compare of values. */
@@ -1064,14 +1026,14 @@
   const struct freq *a = a_;
   const struct freq *b = b_;
 
-  if (a->c > b->c)
+  if (a->count > b->count)
     return 1;
-  else if (a->c < b->c)
+  else if (a->count < b->count)
     return -1;
 
-  if (a->v[0].f > b->v[0].f)
+  if (a->value[0].f > b->value[0].f)
     return 1;
-  else if (a->v[0].f < b->v[0].f)
+  else if (a->value[0].f < b->value[0].f)
     return -1;
   else
     return 0;
@@ -1087,12 +1049,12 @@
   const struct variable *v = v_;
   struct var_freqs *vf = get_var_freqs (v);
 
-  if (a->c > b->c)
+  if (a->count > b->count)
     return 1;
-  else if (a->c < b->c)
+  else if (a->count < b->count)
     return -1;
   else
-    return memcmp (a->v[0].s, b->v[0].s, vf->width);
+    return memcmp (a->value[0].s, b->value[0].s, vf->width);
 }
 
 /* Descending numeric compare of frequency;
@@ -1103,14 +1065,14 @@
   const struct freq *a = a_;
   const struct freq *b = b_;
 
-  if (a->c > b->c)
+  if (a->count > b->count)
     return -1;
-  else if (a->c < b->c)
+  else if (a->count < b->count)
     return 1;
 
-  if (a->v[0].f > b->v[0].f)
+  if (a->value[0].f > b->value[0].f)
     return 1;
-  else if (a->v[0].f < b->v[0].f)
+  else if (a->value[0].f < b->value[0].f)
     return -1;
   else
     return 0;
@@ -1126,12 +1088,12 @@
   const struct variable *v = v_;
   struct var_freqs *vf = get_var_freqs (v);
 
-  if (a->c > b->c)
+  if (a->count > b->count)
     return -1;
-  else if (a->c < b->c)
+  else if (a->count < b->count)
     return 1;
   else
-    return memcmp (a->v[0].s, b->v[0].s, vf->width);
+    return memcmp (a->value[0].s, b->value[0].s, vf->width);
 }
 
 /* Frequency table display. */
@@ -1209,21 +1171,21 @@
     {
       double percent, valid_percent;
 
-      cum_freq += f->c;
+      cum_freq += f->count;
 
-      percent = f->c / ft->total_cases * 100.0;
-      valid_percent = f->c / ft->valid_cases * 100.0;
+      percent = f->count / ft->total_cases * 100.0;
+      valid_percent = f->count / ft->valid_cases * 100.0;
       cum_total += valid_percent;
 
       if (lab)
 	{
-	  const char *label = val_labs_find (v->val_labs, f->v[0]);
+	  const char *label = val_labs_find (v->val_labs, f->value[0]);
 	  if (label != NULL)
 	    tab_text (t, 0, r, TAB_LEFT, label);
 	}
 
-      tab_value (t, 0 + lab, r, TAB_NONE, f->v, &vf->print);
-      tab_float (t, 1 + lab, r, TAB_NONE, f->c, 8, 0);
+      tab_value (t, 0 + lab, r, TAB_NONE, f->value, &vf->print);
+      tab_float (t, 1 + lab, r, TAB_NONE, f->count, 8, 0);
       tab_float (t, 2 + lab, r, TAB_NONE, percent, 5, 1);
       tab_float (t, 3 + lab, r, TAB_NONE, valid_percent, 5, 1);
       tab_float (t, 4 + lab, r, TAB_NONE, cum_total, 5, 1);
@@ -1231,19 +1193,19 @@
     }
   for (; f < &ft->valid[n_categories]; f++)
     {
-      cum_freq += f->c;
+      cum_freq += f->count;
 
       if (lab)
 	{
-	  const char *label = val_labs_find (v->val_labs, f->v[0]);
+	  const char *label = val_labs_find (v->val_labs, f->value[0]);
 	  if (label != NULL)
 	    tab_text (t, 0, r, TAB_LEFT, label);
 	}
 
-      tab_value (t, 0 + lab, r, TAB_NONE, f->v, &vf->print);
-      tab_float (t, 1 + lab, r, TAB_NONE, f->c, 8, 0);
+      tab_value (t, 0 + lab, r, TAB_NONE, f->value, &vf->print);
+      tab_float (t, 1 + lab, r, TAB_NONE, f->count, 8, 0);
       tab_float (t, 2 + lab, r, TAB_NONE,
-		     f->c / ft->total_cases * 100.0, 5, 1);
+		     f->count / ft->total_cases * 100.0, 5, 1);
       tab_text (t, 3 + lab, r, TAB_NONE, _("Missing"));
       r++;
     }
@@ -1312,21 +1274,21 @@
     {
       double percent;
 
-      percent = f->c / ft->total_cases * 100.0;
-      cum_total += f->c / ft->valid_cases * 100.0;
+      percent = f->count / ft->total_cases * 100.0;
+      cum_total += f->count / ft->valid_cases * 100.0;
 
-      tab_value (t, 0, r, TAB_NONE, f->v, &vf->print);
-      tab_float (t, 1, r, TAB_NONE, f->c, 8, 0);
+      tab_value (t, 0, r, TAB_NONE, f->value, &vf->print);
+      tab_float (t, 1, r, TAB_NONE, f->count, 8, 0);
       tab_float (t, 2, r, TAB_NONE, percent, 3, 0);
       tab_float (t, 3, r, TAB_NONE, cum_total, 3, 0);
       r++;
     }
   for (; f < &ft->valid[n_categories]; f++)
     {
-      tab_value (t, 0, r, TAB_NONE, f->v, &vf->print);
-      tab_float (t, 1, r, TAB_NONE, f->c, 8, 0);
+      tab_value (t, 0, r, TAB_NONE, f->value, &vf->print);
+      tab_float (t, 1, r, TAB_NONE, f->count, 8, 0);
       tab_float (t, 2, r, TAB_NONE,
-		 f->c / ft->total_cases * 100.0, 3, 0);
+		 f->count / ft->total_cases * 100.0, 3, 0);
       r++;
     }
 
@@ -1389,7 +1351,7 @@
     {
       static double prev_value = SYSMIS;
       f = &ft->valid[idx]; 
-      rank += f->c ;
+      rank += f->count ;
       for (i = 0; i < n_percentiles; i++) 
         {
 	  double tp;
@@ -1404,7 +1366,7 @@
 
 	  if ( percentiles[i].flag ) 
 	    {
-	      percentiles[i].x2 = f->v[0].f;
+	      percentiles[i].x2 = f->value[0].f;
 	      percentiles[i].x1 = prev_value;
 	      percentiles[i].flag2 = 1;
 	      continue;
@@ -1412,9 +1374,9 @@
 
           if (rank >  tp ) 
 	  {
-	    if ( f->c > 1 && rank - (f->c - 1) > tp ) 
+	    if ( f->count > 1 && rank - (f->count - 1) > tp ) 
 	      {
-		percentiles[i].x2 = percentiles[i].x1 = f->v[0].f;
+		percentiles[i].x2 = percentiles[i].x1 = f->value[0].f;
 		percentiles[i].flag2 = 1;
 	      }
 	    else
@@ -1425,14 +1387,14 @@
 	    continue;
 	  }
         }
-      prev_value = f->v[0].f;
+      prev_value = f->value[0].f;
     }
 
   for (i = 0; i < n_percentiles; i++) 
     {
       /* Catches the case when p == 100% */
       if ( ! percentiles[i].flag2 ) 
-	percentiles[i].x1 = percentiles[i].x2 = f->v[0].f;
+	percentiles[i].x1 = percentiles[i].x2 = f->value[0].f;
 
       /*
       printf("percentile %d (p==%.2f); X1 = %g; X2 = %g\n",
@@ -1468,12 +1430,12 @@
   X_mode = SYSMIS;
   for (f = ft->valid; f < ft->missing; f++)
     {
-      if (most_often < f->c) 
+      if (most_often < f->count) 
         {
-          most_often = f->c;
-          X_mode = f->v[0].f;
+          most_often = f->count;
+          X_mode = f->value[0].f;
         }
-      else if (most_often == f->c) 
+      else if (most_often == f->count) 
         {
           /* A duplicate mode is undefined.
              FIXME: keep track of *all* the modes. */
@@ -1484,16 +1446,16 @@
   /* Calculate moments. */
   m = moments_create (MOMENT_KURTOSIS);
   for (f = ft->valid; f < ft->missing; f++)
-    moments_pass_one (m, f->v[0].f, f->c);
+    moments_pass_one (m, f->value[0].f, f->count);
   for (f = ft->valid; f < ft->missing; f++)
-    moments_pass_two (m, f->v[0].f, f->c);
+    moments_pass_two (m, f->value[0].f, f->count);
   moments_calculate (m, NULL, &d[frq_mean], &d[frq_variance],
                      &d[frq_skew], &d[frq_kurt]);
   moments_destroy (m);
                      
   /* Formulas below are taken from _SPSS Statistical Algorithms_. */
-  d[frq_min] = ft->valid[0].v[0].f;
-  d[frq_max] = ft->valid[ft->n_valid - 1].v[0].f;
+  d[frq_min] = ft->valid[0].value[0].f;
+  d[frq_max] = ft->valid[ft->n_valid - 1].value[0].f;
   d[frq_mode] = X_mode;
   d[frq_range] = d[frq_max] - d[frq_min];
   d[frq_median] = *median_value;
@@ -1598,11 +1560,11 @@
   /* Find out the extremes of the x value */
   for ( frq = hsh_first(fh, &hi); frq != 0; frq = hsh_next(fh, &hi) ) 
     {
-      if ( var_is_value_missing(var, frq->v))
+      if ( var_is_value_missing(var, frq->value))
 	continue;
 
-      if ( frq->v[0].f < x_min ) x_min = frq->v[0].f ;
-      if ( frq->v[0].f > x_max ) x_max = frq->v[0].f ;
+      if ( frq->value[0].f < x_min ) x_min = frq->value[0].f ;
+      if ( frq->value[0].f > x_max ) x_max = frq->value[0].f ;
     }
 
   hist = histogram_create(bins, x_min, x_max);
@@ -1610,7 +1572,7 @@
   for( i = 0 ; i < ft->n_valid ; ++i ) 
     {
       frq = &ft->valid[i];
-      gsl_histogram_accumulate(hist, frq->v[0].f, frq->c);
+      gsl_histogram_accumulate(hist, frq->value[0].f, frq->count);
     }
 
   return hist;
@@ -1643,9 +1605,9 @@
     {
       const struct freq *frq = &frq_tab->valid[i];
 
-      slices[i].label = value_to_string(frq->v, var);
+      slices[i].label = value_to_string(frq->value, var);
 
-      slices[i].magnetude = frq->c;
+      slices[i].magnetude = frq->count;
     }
 
   return slices;
Index: src/language/stats/npar-summary.c
===================================================================
RCS file: src/language/stats/npar-summary.c
diff -N src/language/stats/npar-summary.c
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ src/language/stats/npar-summary.c	10 Dec 2006 02:41:30 -0000
@@ -0,0 +1,171 @@
+/* PSPP - computes sample statistics.
+   Copyright (C) 2006 Free Software Foundation, Inc.
+   Written by John Darrington <john@darrington.wattle.id.au>
+
+   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 2 of the
+   License, or (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful, but
+   WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301, USA. */
+
+#include <config.h>
+#include <output/table.h>
+#include <libpspp/hash.h>
+#include <data/variable.h>
+#include "npar-summary.h"
+#include <math/moments.h>
+#include <data/casefile.h>
+#include <data/casefilter.h>
+#include <data/case.h>
+#include <data/dictionary.h>
+#include <math.h>
+#include <minmax.h>
+
+#include "gettext.h"
+#define _(msgid) gettext (msgid)
+
+
+void
+npar_summary_calc_descriptives (struct descriptives *desc,
+				const struct casefile *cf,
+				struct casefilter *filter, 
+				const struct dictionary *dict,
+				const struct variable *const *vv, 
+				int n_vars UNUSED)
+{
+  int i = 0;
+  while (*vv)
+    {
+      bool warn = true;
+      double minimum = DBL_MAX;
+      double maximum = -DBL_MAX;
+      double var;
+      struct moments1 *moments = moments1_create (MOMENT_VARIANCE);
+      struct casereader *r = casefile_get_reader (cf, filter);
+      struct ccase c;
+      const struct variable *v = *vv++;
+
+      while (casereader_read(r, &c))
+	{
+	  const union value *val = case_data(&c, v->fv );
+	  double w = dict_get_case_weight (dict, &c, &warn);
+
+	  if ( ! casefilter_variable_missing (filter, &c, v ))
+	    {
+	      minimum = MIN (minimum, val->f);
+	      maximum = MAX (maximum, val->f);
+	      moments1_add (moments, val->f, w); 
+	    }
+	  case_destroy (&c);
+	}
+      casereader_destroy (r);
+
+      moments1_calculate (moments, 
+			  &desc[i].n, 
+			  &desc[i].mean, 
+			  &var,
+			  NULL, NULL);
+
+      desc[i].std_dev = sqrt (var);
+
+      moments1_destroy (moments);
+      
+      desc[i].min = minimum;
+      desc[i].max = maximum;
+      
+      i++;
+    }
+}
+
+
+
+void
+do_summary_box (const struct descriptives *desc, 
+		const struct variable *const *vv,
+		int n_vars)
+{
+  int v;
+  bool quartiles = false;
+
+  int col;
+  int columns = 1 ;
+  struct tab_table *table ;
+
+
+  if ( desc ) columns += 5;
+  if ( quartiles ) columns += 3;
+
+  table = tab_create (columns, 2 + n_vars, 0);
+
+  tab_dim (table, tab_natural_dimensions);
+
+  tab_title (table, _("Descriptive Statistics"));
+	
+  tab_headers (table, 1, 0, 1, 0);
+
+  tab_box (table, TAL_1, TAL_1, -1, TAL_1, 
+	   0, 0, table->nc - 1, tab_nr(table) - 1 );
+
+  tab_hline (table, TAL_2, 0, tab_nc (table) -1, 2);
+  tab_vline (table, TAL_2, 1, 0, tab_nr (table) - 1);
+
+  col = 1;
+  if ( desc ) 
+    {
+      tab_joint_text (table, col, 0, col, 1, TAT_TITLE | TAB_CENTER, 
+		      _("N"));
+      col++;
+      tab_joint_text (table, col, 0, col, 1, TAT_TITLE | TAB_CENTER, 
+		      _("Mean"));
+      col++;
+      tab_joint_text (table, col, 0, col, 1, TAT_TITLE | TAB_CENTER, 
+		      _("Std. Deviation"));
+      col++;
+      tab_joint_text (table, col, 0, col, 1, TAT_TITLE | TAB_CENTER, 
+		      _("Minimum"));
+      col++;
+      tab_joint_text (table, col, 0, col, 1, TAT_TITLE | TAB_CENTER, 
+		      _("Maximum"));
+      col++;
+    }
+
+  if ( quartiles ) 
+    {
+      tab_joint_text (table, col, 0, col + 2, 0, TAT_TITLE | TAB_CENTER,
+		      _("Percentiles"));
+      tab_hline (table, TAL_1, col, col + 2, 1);
+
+      tab_text (table, col, 1, TAT_TITLE | TAB_CENTER, 
+		_("25th"));
+      col++;
+      tab_text (table, col, 1, TAT_TITLE | TAB_CENTER, 
+		_("50th (Median)"));
+      col++;
+      tab_text (table, col, 1, TAT_TITLE | TAB_CENTER, 
+		_("75th"));
+      col++;
+    }
+
+  for ( v = 0 ; v < n_vars ; ++v ) 
+    {
+      tab_text (table, 0, 2 + v, TAT_NONE, var_to_string (vv[v]));
+
+      tab_float (table, 1, 2 + v, TAT_NONE, desc[v].n, 8, 0);
+      tab_float (table, 2, 2 + v, TAT_NONE, desc[v].mean, 8, 3);
+      tab_float (table, 3, 2 + v, TAT_NONE, desc[v].std_dev, 8, 3);
+      tab_float (table, 4, 2 + v, TAT_NONE, desc[v].min, 8, 3);
+      tab_float (table, 5, 2 + v, TAT_NONE, desc[v].max, 8, 3);
+    }
+
+
+  tab_submit (table);
+}
Index: src/language/stats/npar-summary.h
===================================================================
RCS file: src/language/stats/npar-summary.h
diff -N src/language/stats/npar-summary.h
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ src/language/stats/npar-summary.h	10 Dec 2006 02:41:30 -0000
@@ -0,0 +1,53 @@
+/* PSPP - computes sample statistics.
+   Copyright (C) 2006 Free Software Foundation, Inc.
+   Written by John Darrington <john@darrington.wattle.id.au>
+
+   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 2 of the
+   License, or (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful, but
+   WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301, USA. */
+
+#if !n_par_summary_h
+#define n_par_summary_h 1
+
+#include <config.h>
+
+struct variable ;
+struct casefile ;
+struct dictionary;
+struct casefilter;
+
+struct descriptives
+{
+  double n;
+  double mean;
+  double std_dev;
+  double min;
+  double max;
+};
+
+void npar_summary_calc_descriptives (struct descriptives *desc,
+				     const struct casefile *cf,
+				     struct casefilter *filter,
+				     const struct dictionary *dict,
+				     const struct variable *const *vv, 
+				     int n_vars);
+
+
+void do_summary_box (const struct descriptives *desc, 
+		     const struct variable *const *vv,
+		     int n_vars);
+
+
+
+#endif
Index: src/language/stats/npar.h
===================================================================
RCS file: src/language/stats/npar.h
diff -N src/language/stats/npar.h
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ src/language/stats/npar.h	10 Dec 2006 02:41:30 -0000
@@ -0,0 +1,56 @@
+/* PSPP - computes sample statistics.
+   Copyright (C) 2006 Free Software Foundation, Inc.
+   Written by John Darrington <john@darrington.wattle.id.au>
+
+   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 2 of the
+   License, or (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful, but
+   WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301, USA. */
+
+#if !npar_h
+#define npar_h 1
+
+typedef struct variable *var_ptr;
+typedef var_ptr variable_pair[2];
+
+struct hsh_table;
+struct casefilter ;
+
+struct npar_test
+{
+  void (*execute) (const struct dataset *, 
+		   const struct casefile *, 
+		   struct casefilter *,
+		   const struct npar_test *
+		   );
+
+  void (*insert_variables) (const struct npar_test *, 
+			    struct hsh_table *);
+};
+
+struct one_sample_test
+{
+  struct npar_test parent;
+  struct variable **vars;
+  size_t n_vars;
+};
+
+struct two_sample_test
+{
+  struct npar_test parent;
+  variable_pair *pairs;
+  size_t n_pairs;
+};
+
+
+#endif
Index: src/language/stats/npar.q
===================================================================
RCS file: src/language/stats/npar.q
diff -N src/language/stats/npar.q
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ src/language/stats/npar.q	10 Dec 2006 02:41:30 -0000
@@ -0,0 +1,568 @@
+/* PSPP - NPAR TESTS. -*-c-*-
+
+Copyright (C) 2006 Free Software Foundation, Inc.
+Author: John Darrington <john@darrington.wattle.id.au>, 
+
+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 2 of the
+License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301, USA. */
+
+#include <config.h>
+
+#include <language/lexer/lexer.h>
+#include <language/lexer/variable-parser.h>
+#include <language/command.h>
+#include <data/procedure.h>
+#include <libpspp/pool.h>
+#include <libpspp/hash.h>
+
+#include <data/casefilter.h>
+#include <data/case.h>
+#include <data/casefile.h>
+#include <math/moments.h>
+#include <data/dictionary.h>
+#include <language/stats/chisquare.h>
+#include <language/stats/binomial.h>
+#include <math.h>
+
+#include "npar.h"
+#include "npar-summary.h"
+
+#include "gettext.h"
+#define _(msgid) gettext (msgid)
+
+/* (headers) */
+
+/* (specification)
+   "NPAR TESTS" (npar_):
+   +chisquare=custom;
+   +binomial=custom;
+   +wilcoxon=custom;
+   +mcnemar=custom;
+   +sign=custom;
+   +cochran=varlist;
+   +friedman=varlist;
+   +kendall=varlist;
+   missing=miss:!analysis/listwise,
+           incl:include/!exclude;
+   +statistics[st_]=descriptives,quartiles,all.
+*/
+/* (declarations) */
+/* (functions) */
+
+
+static struct cmd_npar_tests cmd;
+
+
+struct npar_specs
+{
+  struct pool *pool;
+  struct npar_test **test;
+  size_t n_tests;
+
+  const struct variable *const* vv; /* Compendium of all variables 
+				       (those mentioned on ANY subcommand */
+  int n_vars; /* Number of variables in vv */
+
+  struct casefilter *filter; /* The missing value filter */
+
+  bool descriptives;       /* Descriptive statistics should be calculated */
+  bool quartiles;          /* Quartiles should be calculated */
+};
+
+void one_sample_insert_variables (const struct npar_test *test,
+				  struct hsh_table *variables);
+
+static bool 
+npar_execute(const struct ccase *first UNUSED,
+	     const struct casefile *cf, void *aux, 
+	     const struct dataset *ds)
+{
+  int t;
+  const struct npar_specs *specs = aux;
+  struct descriptives *summary_descriptives = NULL;
+
+  for ( t = 0 ; t < specs->n_tests; ++t ) 
+    {
+      const struct npar_test *test = specs->test[t];
+      if ( NULL == test->execute )
+	{
+	  msg (SW, _("NPAR subcommand not currently implemented."));
+	  continue;
+	}
+      test->execute (ds, cf, specs->filter, test);
+    }
+
+  if ( specs->descriptives )
+    {
+      summary_descriptives = xnmalloc (sizeof (*summary_descriptives), 
+				       specs->n_vars);
+
+      npar_summary_calc_descriptives (summary_descriptives, cf, 
+				      specs->filter,
+				      dataset_dict (ds),
+				      specs->vv, specs->n_vars);
+    }
+
+  if ( specs->descriptives || specs->quartiles ) 
+    do_summary_box (summary_descriptives, specs->vv, specs->n_vars );
+
+  free (summary_descriptives);
+  
+  return true;
+}
+
+
+int
+cmd_npar_tests (struct lexer *lexer, struct dataset *ds)
+{
+  bool ok;
+  int i;
+  struct npar_specs npar_specs = {0, 0, 0, 0, 0, 0, 0, 0};
+  struct hsh_table *var_hash;
+  npar_specs.pool = pool_create ();
+
+  var_hash = hsh_create_pool (npar_specs.pool, 0, 
+			      compare_var_index, hash_var_index, NULL, NULL);
+
+  if ( ! parse_npar_tests (lexer, ds, &cmd, &npar_specs) ) 
+    {
+      pool_destroy (npar_specs.pool);
+      return CMD_FAILURE;
+    }
+
+  for (i = 0; i < npar_specs.n_tests; ++i ) 
+    {
+      const struct npar_test *test = npar_specs.test[i];
+      test->insert_variables (test, var_hash);
+    }
+
+  npar_specs.vv =  (const struct variable *const *) hsh_data (var_hash);
+  npar_specs.n_vars = hsh_count (var_hash);
+  
+  if ( cmd.sbc_statistics ) 
+    {
+      int i;
+
+      for ( i = 0 ; i < NPAR_ST_count; ++i ) 
+	{
+	  if ( cmd.a_statistics[i] ) 
+	    {
+	      switch ( i ) 
+		{
+		case NPAR_ST_DESCRIPTIVES:
+		  npar_specs.descriptives = true;
+		  break;
+		case NPAR_ST_QUARTILES:
+		  npar_specs.quartiles = true;
+		  break;
+		case NPAR_ST_ALL:
+		  npar_specs.quartiles = true;
+		  npar_specs.descriptives = true;
+		  break;
+		default:
+		  NOT_REACHED();
+		};
+	    }
+	}
+    }
+
+  npar_specs.filter = 
+    casefilter_create (cmd.incl == NPAR_EXCLUDE, 0, 0);
+
+  if ( cmd.miss == NPAR_LISTWISE ) 
+    casefilter_add_variables (npar_specs.filter, 
+			      npar_specs.vv, 
+			      npar_specs.n_vars);
+
+  ok = multipass_procedure_with_splits (ds, npar_execute, &npar_specs);
+
+  casefilter_destroy (npar_specs.filter);
+
+  hsh_destroy (var_hash);
+
+  pool_destroy (npar_specs.pool);
+
+  return ok ? CMD_SUCCESS : CMD_CASCADING_FAILURE;
+}
+
+int
+npar_custom_chisquare(struct lexer *lexer, struct dataset *ds, struct cmd_npar_tests *cmd UNUSED, void *aux )
+{
+  struct npar_specs *specs = aux;
+
+  struct chisquare_test *cstp = pool_alloc(specs->pool, sizeof(*cstp));
+  struct one_sample_test *tp = (struct one_sample_test *) cstp;
+
+  ((struct npar_test *)tp)->execute = chisquare_execute;
+  ((struct npar_test *)tp)->insert_variables = one_sample_insert_variables;
+
+  if (!parse_variables_pool (lexer, specs->pool, dataset_dict (ds), 
+			     &tp->vars, &tp->n_vars,
+			     PV_NO_SCRATCH | PV_NO_DUPLICATE))
+    {
+      return 2;
+    }
+
+  cstp->ranged = false;
+
+  if ( lex_match (lexer, '('))
+    {
+      cstp->ranged = true;
+      if ( ! lex_force_num (lexer)) return 0;
+      cstp->lo = lex_integer (lexer);
+      lex_get (lexer);
+      lex_force_match (lexer, ',');
+      if (! lex_force_num (lexer) ) return 0;
+      cstp->hi = lex_integer (lexer);
+      if ( cstp->lo >= cstp->hi ) 
+	{
+	  msg(ME, 
+	      _("The specified value of HI (%d) is "
+		"lower than the specified value of LO (%d)"), 
+	      cstp->hi, cstp->lo);
+	  return 0;
+	}
+      lex_get (lexer);
+      if (! lex_force_match (lexer, ')')) return 0;
+    }
+
+  cstp->n_expected = 0;
+  cstp->expected = NULL;
+  if ( lex_match (lexer, '/') ) 
+    {
+      if ( lex_match_id (lexer, "EXPECTED") ) 
+	{
+	  lex_force_match (lexer, '=');
+	  if ( ! lex_match_id (lexer, "EQUAL") ) 
+	    {
+	      double f;
+	      int n;
+	      while ( lex_is_number(lexer) ) 
+		{
+		  int i;
+		  n = 1;
+		  f = lex_number (lexer);
+		  lex_get (lexer);
+		  if ( lex_match (lexer, '*'))
+		    {
+		      n = f;
+		      f = lex_number (lexer);
+		      lex_get (lexer);
+		    }
+		  lex_match (lexer, ',');
+
+		  cstp->n_expected += n;
+		  cstp->expected = pool_realloc (specs->pool,
+						 cstp->expected, 
+						 sizeof(double) * 
+						 cstp->n_expected);
+		  for ( i = cstp->n_expected - n ;
+			i < cstp->n_expected; 
+			++i ) 
+		    cstp->expected[i] = f;
+		  
+		}
+	    }
+	}     
+      else
+	lex_put_back (lexer, '/');
+    }
+
+  if ( cstp->ranged && cstp->n_expected > 0 && 
+       cstp->n_expected != cstp->hi - cstp->lo + 1 ) 
+    {
+      msg(ME, 
+	  _("%d expected values were given, but the specified "
+	    "range (%d-%d) requires exactly %d values."), 
+	  cstp->n_expected, cstp->lo, cstp->hi, 
+	  cstp->hi - cstp->lo +1);
+      return 0;
+    }
+
+  specs->n_tests++;
+  specs->test = pool_realloc (specs->pool, 
+			      specs->test, 
+			      sizeof(*specs->test) * specs->n_tests);
+
+  specs->test[specs->n_tests - 1] = (struct npar_test *) tp;
+
+  return 1;
+}
+
+
+int
+npar_custom_binomial(struct lexer *lexer, struct dataset *ds, struct cmd_npar_tests *cmd UNUSED, void *aux)
+{
+  struct npar_specs *specs = aux;
+  struct binomial_test *btp = pool_alloc(specs->pool, sizeof(*btp));
+  struct one_sample_test *tp = (struct one_sample_test *) btp;
+
+  ((struct npar_test *)tp)->execute = binomial_execute;
+  ((struct npar_test *)tp)->insert_variables = one_sample_insert_variables;
+
+  btp->category1 = btp->category2 = btp->cutpoint = SYSMIS;
+
+  if ( lex_match(lexer, '(') ) 
+    {
+      if ( lex_force_num (lexer) ) 
+	{
+	  btp->p = lex_number (lexer);
+	  lex_get (lexer);
+	  lex_force_match (lexer, ')');
+	}
+      else
+	return 0;
+    }
+
+  if ( lex_match (lexer, '=') ) 
+    {
+      if (parse_variables_pool (lexer, specs->pool, dataset_dict (ds), 
+				&tp->vars, &tp->n_vars,
+				PV_NUMERIC | PV_NO_SCRATCH | PV_NO_DUPLICATE) )
+	{
+	  if ( lex_match (lexer, '('))
+	    {
+	      lex_force_num (lexer);
+	      btp->category1 = lex_number (lexer);
+	      lex_get (lexer);
+	      if ( ! lex_force_match (lexer, ',')) return 2;
+	      if ( ! lex_force_num (lexer) ) return 2;
+	      btp->category2 = lex_number (lexer);
+	      lex_get (lexer);
+	      lex_force_match (lexer, ')');
+	    }
+	}
+      else
+	return 2;
+    }
+  else
+    {
+      if ( lex_match (lexer, '(') ) 
+	{
+	  lex_force_num (lexer);
+	  btp->cutpoint = lex_number (lexer);
+	  lex_get (lexer);
+	  lex_force_match (lexer, ')');
+	}
+    }
+
+  specs->n_tests++;
+  specs->test = pool_realloc (specs->pool, 
+			      specs->test, 
+			      sizeof(*specs->test) * specs->n_tests);
+
+  specs->test[specs->n_tests - 1] = (struct npar_test *) tp;
+
+  return 1;
+}
+
+
+bool parse_two_sample_related_test (struct lexer *lexer, 
+				    const struct dictionary *dict, 
+				    struct cmd_npar_tests *cmd, 
+				    struct two_sample_test *test_parameters,
+				    struct pool *pool
+				    );
+
+
+bool
+parse_two_sample_related_test (struct lexer *lexer, 
+			       const struct dictionary *dict, 
+			       struct cmd_npar_tests *cmd UNUSED, 
+			       struct two_sample_test *test_parameters,
+			       struct pool *pool
+			       )
+{
+  int n = 0;
+  bool paired = false;
+  bool with = false;
+  struct variable **vlist1;
+  size_t n_vlist1;
+
+  struct variable **vlist2;
+  size_t n_vlist2;
+
+  if (!parse_variables_pool (lexer, pool, 
+			     dict, 
+			     &vlist1, &n_vlist1,
+			     PV_NUMERIC | PV_NO_SCRATCH | PV_NO_DUPLICATE) )
+    return false;
+
+  if ( lex_match(lexer, T_WITH))
+    {
+      with = true;
+      if ( !parse_variables_pool (lexer, pool, dict,
+				  &vlist2, &n_vlist2,
+				  PV_NUMERIC | PV_NO_SCRATCH | PV_NO_DUPLICATE) )
+	return false;
+      
+      paired = (lex_match (lexer, '(') && 
+		lex_match_id (lexer, "PAIRED") && lex_match (lexer, ')'));
+    }
+  
+
+  if ( with ) 
+    {
+      if (paired) 
+	{
+	  if ( n_vlist1 != n_vlist2) 
+	    msg (SE, _("PAIRED was specified but the number of variables "
+		       "preceding WITH (%d) did not match the number "
+		       "following (%d)."), n_vlist1, n_vlist2);
+
+	  test_parameters->n_pairs = n_vlist1 ;
+	}
+      else
+	{
+	  test_parameters->n_pairs = n_vlist1 * n_vlist2;
+	}
+    }
+  else
+    {
+      test_parameters->n_pairs = (n_vlist1 * (n_vlist1 - 1)) / 2 ;
+    }
+
+  test_parameters->pairs = 
+    pool_alloc (pool, sizeof ( variable_pair) * test_parameters->n_pairs);
+
+  if ( with ) 
+    {
+      if (paired)
+	{
+	  int i;
+	  assert (n_vlist1 == n_vlist2);
+	  for ( i = 0 ; i < n_vlist1; ++i )
+	    {
+	      test_parameters->pairs[n][0] = vlist1[i];
+	      test_parameters->pairs[n][1] = vlist2[i];
+	      n++;
+	    }
+	}
+      else
+	{
+	  int i,j;
+	  for ( i = 0 ; i < n_vlist1; ++i )
+	    {
+	      for ( j = 0 ; j < n_vlist2; ++j )
+		{
+		  test_parameters->pairs[n][0] = vlist1[i];
+		  test_parameters->pairs[n][1] = vlist2[j];
+		  n++;
+		}
+	    }
+	}
+    }
+  else
+    {
+      int i,j;
+      for ( i = 0 ; i < n_vlist1 - 1; ++i )
+	{
+	  for ( j = i + 1 ; j < n_vlist1; ++j ) 
+	    {
+	      assert ( n < test_parameters->n_pairs);
+	      test_parameters->pairs[n][0] = vlist1[i];
+	      test_parameters->pairs[n][1] = vlist1[j];
+	      n++;
+	    }
+	}
+    }
+
+  assert ( n == test_parameters->n_pairs);
+
+  return true;
+}
+
+int
+npar_custom_wilcoxon (struct lexer *lexer, 
+		      struct dataset *ds, 
+		      struct cmd_npar_tests *cmd, void *aux )
+{
+  struct npar_specs *specs = aux;
+
+  struct two_sample_test *tp = pool_alloc(specs->pool, sizeof(*tp));
+  ((struct npar_test *)tp)->execute = NULL;
+
+  if (!parse_two_sample_related_test (lexer, dataset_dict (ds), cmd, 
+				      tp, specs->pool) ) 
+    return 0;
+  
+  specs->n_tests++;
+  specs->test = pool_realloc (specs->pool, 
+			      specs->test, 
+			      sizeof(*specs->test) * specs->n_tests);
+  specs->test[specs->n_tests - 1] = (struct npar_test *) tp;
+
+  return 1;
+}
+
+int
+npar_custom_mcnemar (struct lexer *lexer, 
+		     struct dataset *ds, 
+		     struct cmd_npar_tests *cmd, void *aux )
+{
+  struct npar_specs *specs = aux;
+
+  struct two_sample_test *tp = pool_alloc(specs->pool, sizeof(*tp));
+  ((struct npar_test *)tp)->execute = NULL;
+
+
+  if (!parse_two_sample_related_test (lexer, dataset_dict (ds), 
+				      cmd, tp, specs->pool) ) 
+    return 0;
+  
+  specs->n_tests++;
+  specs->test = pool_realloc (specs->pool, 
+			      specs->test, 
+			      sizeof(*specs->test) * specs->n_tests);
+  specs->test[specs->n_tests - 1] = (struct npar_test *) tp;
+  
+  return 1;
+}
+
+int
+npar_custom_sign (struct lexer *lexer, struct dataset *ds, 
+		  struct cmd_npar_tests *cmd, void *aux )
+{
+  struct npar_specs *specs = aux;
+
+  struct two_sample_test *tp = pool_alloc(specs->pool, sizeof(*tp));
+  ((struct npar_test *)tp)->execute = NULL;
+
+
+  if (!parse_two_sample_related_test (lexer, dataset_dict (ds), cmd, 
+				      tp, specs->pool) ) 
+    return 0;
+  
+  specs->n_tests++;
+  specs->test = pool_realloc (specs->pool, 
+			      specs->test, 
+			      sizeof(*specs->test) * specs->n_tests);
+  specs->test[specs->n_tests - 1] = (struct npar_test *) tp;
+
+  return 1;
+}
+
+/* Insert the variables for TEST into VAR_HASH */
+void
+one_sample_insert_variables (const struct npar_test *test,
+			    struct hsh_table *var_hash)
+{
+  int i;
+  struct one_sample_test *ost = (struct one_sample_test *) test;
+
+  for ( i = 0 ; i < ost->n_vars ; ++i ) 
+    hsh_insert (var_hash, ost->vars[i]);
+}
+
Index: src/language/stats/t-test.q
===================================================================
RCS file: /sources/pspp/pspp/src/language/stats/t-test.q,v
retrieving revision 1.16
diff -b -w -U3 -r1.16 t-test.q
--- src/language/stats/t-test.q	5 Dec 2006 15:10:21 -0000	1.16
+++ src/language/stats/t-test.q	10 Dec 2006 02:41:31 -0000
@@ -1789,6 +1789,7 @@
   struct casefilter *filter = casefilter_create (cmd->miss != TTS_INCLUDE, 
 						 NULL, 0);
 
+
   if ( cmd->miss == TTS_LISTWISE ) 
     casefilter_add_variables (filter,
 			      cmd->v_variables, cmd->n_variables);
Index: tests/automake.mk
===================================================================
RCS file: /sources/pspp/pspp/tests/automake.mk,v
retrieving revision 1.20
diff -b -w -U3 -r1.20 automake.mk
--- tests/automake.mk	1 Dec 2006 06:51:08 -0000	1.20
+++ tests/automake.mk	10 Dec 2006 02:41:31 -0000
@@ -27,6 +27,8 @@
 	tests/command/missing-values.sh \
 	tests/command/no_case_size.sh \
 	tests/command/n_of_cases.sh \
+	tests/command/npar-binomial.sh \
+	tests/command/npar-chisquare.sh \
 	tests/command/oneway.sh \
 	tests/command/oneway-missing.sh \
 	tests/command/oneway-with-splits.sh \
Index: tests/command/npar-chisquare.sh
===================================================================
RCS file: tests/command/npar-chisquare.sh
diff -N tests/command/npar-chisquare.sh
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ tests/command/npar-chisquare.sh	10 Dec 2006 02:41:31 -0000
@@ -0,0 +1,357 @@
+#!/bin/sh
+
+# This program tests the chisquare subcommand of the NPAR command.
+
+TEMPDIR=/tmp/pspp-tst-$$
+TESTFILE=$TEMPDIR/`basename $0`.sps
+
+# ensure that top_srcdir and top_builddir  are absolute
+if [ -z "$top_srcdir" ] ; then top_srcdir=. ; fi
+if [ -z "$top_builddir" ] ; then top_builddir=. ; fi
+top_srcdir=`cd $top_srcdir; pwd`
+top_builddir=`cd $top_builddir; pwd`
+
+PSPP=$top_builddir/src/ui/terminal/pspp
+
+STAT_CONFIG_PATH=$top_srcdir/config
+export STAT_CONFIG_PATH
+
+LANG=C
+export LANG
+
+
+cleanup()
+{
+     if [ x"$PSPP_TEST_NO_CLEANUP" != x ] ; then 
+	echo "NOT cleaning $TEMPDIR"
+     	return ; 
+     fi
+     rm -rf $TEMPDIR
+}
+
+
+fail()
+{
+    echo $activity
+    echo FAILED
+    cleanup;
+    exit 1;
+}
+
+
+no_result()
+{
+    echo $activity
+    echo NO RESULT;
+    cleanup;
+    exit 2;
+}
+
+pass()
+{
+    cleanup;
+    exit 0;
+}
+
+mkdir -p $TEMPDIR
+
+cd $TEMPDIR
+
+activity="create file 1"
+cat <<EOF > $TESTFILE
+DATA LIST NOTABLE LIST /x * y * w *.
+BEGIN DATA.
+1   2  1
+2   1  3
+3.1 1  4
+3.2 2  1
+4   2  2
+5   3  1
+1   4  2
+END DATA.
+
+WEIGHT BY w.
+
+NPAR TESTS
+  CHISQUARE=x y
+  .
+
+NPAR TESTS
+  CHISQUARE=y
+  /EXPECTED=3 4 5 4
+  .
+
+NPAR TESTS
+  CHISQUARE=x y(2, 4)
+  /EXPECTED = 6 10 3
+  .
+
+EOF
+if [ $? -ne 0 ] ; then no_result ; fi
+
+
+activity="run program 1"
+$SUPERVISOR $PSPP --testing-mode -o raw-ascii $TESTFILE
+if [ $? -ne 0 ] ; then no_result ; fi
+
+activity="compare output 1"
+diff $TEMPDIR/pspp.list - << EOF
+1.1 NPAR TESTS.  x
++--------#----------+----------+--------+
+|        #Observed N|Expected N|Residual|
++--------#----------+----------+--------+
+|    1.00#         3|      2.33|     .67|
+|    2.00#         3|      2.33|     .67|
+|    3.10#         4|      2.33|    1.67|
+|    3.20#         1|      2.33|   -1.33|
+|    4.00#         2|      2.33|    -.33|
+|    5.00#         1|      2.33|   -1.33|
+|Total   #        14|          |        |
++--------#----------+----------+--------+
+
+1.2 NPAR TESTS.  y
++--------#----------+----------+--------+
+|        #Observed N|Expected N|Residual|
++--------#----------+----------+--------+
+|    1.00#         7|      3.50|    3.50|
+|    2.00#         4|      3.50|     .50|
+|    3.00#         1|      3.50|   -2.50|
+|    4.00#         2|      3.50|   -1.50|
+|Total   #        14|          |        |
++--------#----------+----------+--------+
+
+1.3 NPAR TESTS.  Test Statistics
++-----------#-----+-----+
+|           #  x  |  y  |
++-----------#-----+-----+
+|Chi-Square #3.143|6.000|
+|df         #    5|    3|
+|Asymp. Sig.# .678| .112|
++-----------#-----+-----+
+
+2.1 NPAR TESTS.  y
++--------#----------+----------+--------+
+|        #Observed N|Expected N|Residual|
++--------#----------+----------+--------+
+|    1.00#         7|      2.63|    4.38|
+|    2.00#         4|      3.50|     .50|
+|    3.00#         1|      4.38|   -3.38|
+|    4.00#         2|      3.50|   -1.50|
+|Total   #        14|          |        |
++--------#----------+----------+--------+
+
+2.2 NPAR TESTS.  Test Statistics
++-----------#------+
+|           #   y  |
++-----------#------+
+|Chi-Square #10.610|
+|df         #     3|
+|Asymp. Sig.#  .014|
++-----------#------+
+
+3.1 NPAR TESTS.  Frequencies
++-----#---------------------------------------#---------------------------------------+
+|     #                   x                   #                   y                   |
+|     #--------+----------+----------+--------#--------+----------+----------+--------+
+|     #Category|Observed N|Expected N|Residual#Category|Observed N|Expected N|Residual|
++-----#--------+----------+----------+--------#--------+----------+----------+--------+
+|1    #    2.00|         3|      3.16|    -.16#    2.00|         4|      2.21|    1.79|
+|2    #    3.00|         5|      5.26|    -.26#    3.00|         1|      3.68|   -2.68|
+|3    #    4.00|         2|      1.58|     .42#    4.00|         2|      1.11|     .89|
+|Total#        |        10|          |        #        |         7|          |        |
++-----#--------+----------+----------+--------#--------+----------+----------+--------+
+
+3.2 NPAR TESTS.  Test Statistics
++-----------#----+-----+
+|           #  x |  y  |
++-----------#----+-----+
+|Chi-Square #.133|4.129|
+|df         #   2|    2|
+|Asymp. Sig.#.936| .127|
++-----------#----+-----+
+
+EOF
+if [ $? -ne 0 ] ; then fail ; fi
+
+
+activity="create file 2"
+cat <<EOF > $TESTFILE
+DATA LIST NOTABLE LIST /x * y * w *.
+BEGIN DATA.
+1   2  1
+2   1  3
+3.1 1  4
+3.2 2  1
+4   2  2
+5   3  1
+1   4  2
+END DATA.
+
+WEIGHT BY w.
+
+NPAR TESTS
+  CHISQUARE=y
+  /EXPECTED = 3 4 5 4 3 1
+  .
+EOF
+if [ $? -ne 0 ] ; then no_result ; fi
+
+
+activity="run program 2"
+$SUPERVISOR $PSPP --testing-mode -o raw-ascii $TESTFILE  > $TEMPDIR/output
+if [ $? -eq 0 ] ; then no_result ; fi
+
+activity="compare errors 2"
+diff  $TEMPDIR/output - << EOF
+error: CHISQUARE test specified 6 expected values, but 4 distinct values were encountered in variable y.
+EOF
+if [ $? -ne 0 ] ; then fail ; fi
+
+
+activity="create file 3"
+cat <<EOF > $TESTFILE
+DATA LIST NOTABLE LIST /x * y * w * .
+BEGIN DATA.
+1   2  1 
+2   1  3
+3.1 1  4
+3.2 2  1
+4   2  2
+5   3  1
+1   4  2
+.   5  1
+END DATA.
+
+WEIGHT BY w.
+
+MISSING VALUES x (4).
+
+NPAR TESTS
+  CHISQUARE=x y(-2,5)
+  /MISSING=ANALYSIS
+  /STATISTICS=DESCRIPTIVES
+  .
+EOF
+if [ $? -ne 0 ] ; then no_result ; fi
+
+
+activity="run program 3"
+$SUPERVISOR $PSPP --testing-mode -o raw-ascii $TESTFILE 
+if [ $? -ne 0 ] ; then no_result ; fi
+
+
+activity="compare output 3"
+diff $TEMPDIR/pspp.list - <<EOF
+1.1 NPAR TESTS.  Frequencies
++-----#---------------------------------------#---------------------------------------+
+|     #                   x                   #                   y                   |
+|     #--------+----------+----------+--------#--------+----------+----------+--------+
+|     #Category|Observed N|Expected N|Residual#Category|Observed N|Expected N|Residual|
++-----#--------+----------+----------+--------#--------+----------+----------+--------+
+|1    #   -2.00|         0|      1.50|   -1.50#   -2.00|         0|      1.88|   -1.88|
+|2    #   -1.00|         0|      1.50|   -1.50#   -1.00|         0|      1.88|   -1.88|
+|3    #     .00|         0|      1.50|   -1.50#     .00|         0|      1.88|   -1.88|
+|4    #    1.00|         3|      1.50|    1.50#    1.00|         7|      1.88|    5.13|
+|5    #    2.00|         3|      1.50|    1.50#    2.00|         4|      1.88|    2.13|
+|6    #    3.00|         5|      1.50|    3.50#    3.00|         1|      1.88|    -.88|
+|7    #    4.00|         0|      1.50|   -1.50#    4.00|         2|      1.88|     .13|
+|8    #    5.00|         1|      1.50|    -.50#    5.00|         1|      1.88|    -.88|
+|Total#        |        12|          |        #        |        15|          |        |
++-----#--------+----------+----------+--------#--------+----------+----------+--------+
+
+1.2 NPAR TESTS.  Test Statistics
++-----------#------+------+
+|           #   x  |   y  |
++-----------#------+------+
+|Chi-Square #17.333|22.867|
+|df         #     7|     7|
+|Asymp. Sig.#  .015|  .002|
++-----------#------+------+
+
+1.3 NPAR TESTS.  Descriptive Statistics
++-#--+-----+-----+-----+-----+
+| # N| Mean| Std.|Minim|Maxim|
+| #  |     |Devia|  um |  um |
+#=#==#=====#=====#=====#=====#
+|x#12|2.467|1.193|1.000|5.000|
+|y#15|2.067|1.335|1.000|5.000|
++-#--+-----+-----+-----+-----+
+
+EOF
+if [ $? -ne 0 ] ; then fail ; fi
+
+
+activity="create file 4"
+cat <<EOF > $TESTFILE
+DATA LIST NOTABLE LIST /x * y * w * .
+BEGIN DATA.
+1   2  1 
+2   1  3
+3.1 1  4
+3.2 2  1
+4   2  2
+5   3  1
+1   4  2
+.   5  1
+END DATA.
+
+WEIGHT BY w.
+
+* MISSING VALUES x (4).
+
+NPAR TESTS
+  CHISQUARE=x y(-2,5)
+  /MISSING=LISTWISE
+  /STATISTICS=DESCRIPTIVES
+  .
+EOF
+if [ $? -ne 0 ] ; then no_result ; fi
+
+
+activity="run program 4"
+$SUPERVISOR $PSPP --testing-mode -o raw-ascii $TESTFILE 
+if [ $? -ne 0 ] ; then no_result ; fi
+
+
+activity="compare output 4"
+diff $TEMPDIR/pspp.list - <<EOF
+1.1 NPAR TESTS.  Frequencies
++-----#---------------------------------------#---------------------------------------+
+|     #                   x                   #                   y                   |
+|     #--------+----------+----------+--------#--------+----------+----------+--------+
+|     #Category|Observed N|Expected N|Residual#Category|Observed N|Expected N|Residual|
++-----#--------+----------+----------+--------#--------+----------+----------+--------+
+|1    #   -2.00|         0|      1.75|   -1.75#   -2.00|         0|      1.75|   -1.75|
+|2    #   -1.00|         0|      1.75|   -1.75#   -1.00|         0|      1.75|   -1.75|
+|3    #     .00|         0|      1.75|   -1.75#     .00|         0|      1.75|   -1.75|
+|4    #    1.00|         3|      1.75|    1.25#    1.00|         7|      1.75|    5.25|
+|5    #    2.00|         3|      1.75|    1.25#    2.00|         4|      1.75|    2.25|
+|6    #    3.00|         5|      1.75|    3.25#    3.00|         1|      1.75|    -.75|
+|7    #    4.00|         2|      1.75|     .25#    4.00|         2|      1.75|     .25|
+|8    #    5.00|         1|      1.75|    -.75#    5.00|         0|      1.75|   -1.75|
+|Total#        |        14|          |        #        |        14|          |        |
++-----#--------+----------+----------+--------#--------+----------+----------+--------+
+
+1.2 NPAR TESTS.  Test Statistics
++-----------#------+------+
+|           #   x  |   y  |
++-----------#------+------+
+|Chi-Square #13.429|26.000|
+|df         #     7|     7|
+|Asymp. Sig.#  .062|  .001|
++-----------#------+------+
+
+1.3 NPAR TESTS.  Descriptive Statistics
++-#--+-----+-----+-----+-----+
+| # N| Mean| Std.|Minim|Maxim|
+| #  |     |Devia|  um |  um |
+#=#==#=====#=====#=====#=====#
+|x#14|2.686|1.231|1.000|5.000|
+|y#14|1.857|1.099|1.000|4.000|
++-#--+-----+-----+-----+-----+
+
+EOF
+if [ $? -ne 0 ] ; then fail ; fi
+
+
+pass;
