Added framework for the ONEWAY command.
authorJohn Darrington <john@darrington.wattle.id.au>
Fri, 22 Oct 2004 07:59:34 +0000 (07:59 +0000)
committerJohn Darrington <john@darrington.wattle.id.au>
Fri, 22 Oct 2004 07:59:34 +0000 (07:59 +0000)
src/Makefile.am
src/command.def
src/oneway.q [new file with mode: 0644]
src/q2c.c
src/subclist.c [new file with mode: 0644]
src/subclist.h [new file with mode: 0644]
src/tab.c
src/val.h
src/vars-atr.c

index 1cd16e88cfacb4edf0e443a21157456e52ee3633..e6ce5839031d741d3c164398a8c6f36bf3b013c4 100644 (file)
@@ -43,10 +43,10 @@ $(q_sources_c): q2c$(EXEEXT)
        ./q2c $< $@
 
 q_sources_c = correlations.c crosstabs.c file-handle.c \
-frequencies.c list.c means.c set.c  t-test.c
+frequencies.c list.c means.c oneway.c set.c  t-test.c
 
 q_sources_q = correlations.q crosstabs.q file-handle.q \
-frequencies.q list.q means.q set.q  t-test.q
+frequencies.q list.q means.q oneway.q set.q  t-test.q
 
 pspp_SOURCES = $(q_sources_c) aggregate.c algorithm.c algorithm.h      \
 alloc.c alloc.h apply-dict.c ascii.c autorecode.c bitvector.h          \
@@ -66,8 +66,8 @@ moments.c moments.h numeric.c output.c output.h pfm-read.c pfm-write.c        \
 pfm.h pool.c pool.h postscript.c print.c random.c random.h recode.c    \
 rename-vars.c repeat.c repeat.h sample.c sel-if.c settings.h           \
 sfm-read.c sfm-write.c sfm.h sfmP.h som.c som.h sort.c sort.h          \
-split-file.c str.c str.h sysfile-info.c tab.c tab.h temporary.c                \
-stat.h \
+split-file.c str.c str.h subclist.c \
+sysfile-info.c tab.c tab.h temporary.c stat.h \
 title.c t-test.h val.h val-labs.c value-labels.c value-labels.h                \
 var-labs.c var.h vars-atr.c vars-prs.c vector.c version.c version.h    \
 vfm.c vfm.h vfmP.h weight.c
index cfb74112b06c86600e90be277c9afe4bd27988e6..d4adcf412ba884095642a725448ab35518b79b2e 100644 (file)
@@ -93,6 +93,7 @@ DEFCMD ("N OF CASES",             INIT, INPU, TRAN, TRAN, cmd_n_of_cases)
 UNIMPL ("NUMBERED",               INIT, INPU, TRAN, PROC)
 DEFCMD ("NUMERIC",                ERRO, INPU, TRAN, TRAN, cmd_numeric)
 UNIMPL ("UNNUMBERED",             INIT, INPU, TRAN, PROC)
+DEFCMD ("ONEWAY",                 ERRO, ERRO, PROC, PROC, cmd_oneway)
 DEFCMD ("PEARSON CORRELATIONS",          ERRO, ERRO, PROC, PROC, cmd_correlations)
 UNIMPL ("POINT",                  ERRO, INPU, ERRO, ERRO)
 UNIMPL ("PRESERVE",              INIT, INPU, TRAN, PROC)
diff --git a/src/oneway.q b/src/oneway.q
new file mode 100644 (file)
index 0000000..57fefc7
--- /dev/null
@@ -0,0 +1,600 @@
+/* PSPP - One way ANOVA. -*-c-*-
+
+   Copyright (C) 1997-9, 2000 Free Software Foundation, Inc.
+   Author: John Darrington 2004
+
+   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., 59 Temple Place - Suite 330, Boston, MA
+   02111-1307, USA. */
+
+#include <config.h>
+#include <gsl/gsl_cdf.h>
+#include "error.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+#include "alloc.h"
+#include "str.h"
+#include "case.h"
+#include "command.h"
+#include "lexer.h"
+#include "error.h"
+#include "magic.h"
+#include "misc.h"
+#include "tab.h"
+#include "som.h"
+#include "value-labels.h"
+#include "var.h"
+#include "vfm.h"
+#include "hash.h"
+#include "casefile.h"
+#include "levene.h"
+
+/* (specification)
+   "ONEWAY" (oneway_):
+     *variables=custom;
+     +missing=miss:!analysis/listwise,
+             incl:include/!exclude;
+     contrast= double list;
+     statistics[st_]=descriptives,homogeneity.
+*/
+/* (declarations) */
+/* (functions) */
+
+
+
+static struct cmd_oneway cmd;
+
+/* The independent variable */
+static struct variable *indep_var;
+
+/* A hash of the values of the independent variable */
+struct hsh_table *ind_vals;
+
+/* Number of factors (groups) */
+static int n_groups;
+
+/* Number of dependent variables */
+static int n_vars;
+
+/* The dependent variables */
+static struct variable **vars;
+
+
+
+
+
+/* Function to use for testing for missing values */
+static is_missing_func value_is_missing;
+
+
+static void calculate(const struct casefile *cf, void *_mode);
+
+
+/* Routines to show the output tables */
+static void show_anova_table(void);
+static void show_descriptives(void);
+static void show_homogeneity(void);
+static void show_contrast_coeffs(void);
+static void show_contrast_tests(void);
+
+
+
+int
+cmd_oneway(void)
+{
+  int i;
+
+  if ( !parse_oneway(&cmd) )
+    return CMD_FAILURE;
+
+  /* If /MISSING=INCLUDE is set, then user missing values are ignored */
+  if (cmd.incl == ONEWAY_INCLUDE ) 
+    value_is_missing = is_system_missing;
+  else
+    value_is_missing = is_missing;
+
+  multipass_procedure_with_splits (calculate, &cmd);
+
+  /* Check the sanity of the given contrast values */
+  for (i = 0 ; i < cmd.sbc_contrast ; ++i ) 
+    {
+      int j;
+      double sum = 0;
+
+      if ( subc_list_double_count(&cmd.dl_contrast[i]) != n_groups )
+       {
+         msg(SE, _("Number of contrast coefficients must equal the number of groups"));
+         return CMD_FAILURE;
+       }
+
+      for (j=0; j < n_groups ; ++j )
+       sum += subc_list_double_at(&cmd.dl_contrast[i],j);
+
+      if ( sum != 0.0 ) 
+       msg(SW,_("Coefficients for contrast %d do not total zero"),i + 1);
+    }
+
+
+  /* Show the statistics tables */
+  if ( cmd.sbc_statistics ) 
+    {
+    for (i = 0 ; i < ONEWAY_ST_count ; ++i ) 
+      {
+       if  ( ! cmd.a_statistics[i]  ) continue;
+
+       switch (i) {
+       case ONEWAY_ST_DESCRIPTIVES:
+         show_descriptives();
+         break;
+       case ONEWAY_ST_HOMOGENEITY:
+         show_homogeneity();
+         break;
+       }
+      
+      }
+  }
+
+  show_anova_table();
+     
+  if (cmd.sbc_contrast)
+    {
+      show_contrast_coeffs();
+      show_contrast_tests();
+    }
+
+  hsh_destroy(ind_vals);
+
+  return CMD_SUCCESS;
+}
+
+
+
+
+
+/* Parser for the variables sub command */
+static int
+oneway_custom_variables(struct cmd_oneway *cmd UNUSED)
+{
+
+  lex_match('=');
+
+  if ((token != T_ID || dict_lookup_var (default_dict, tokid) == NULL)
+      && token != T_ALL)
+    return 2;
+  
+
+  if (!parse_variables (default_dict, &vars, &n_vars,
+                       PV_DUPLICATE 
+                       | PV_NUMERIC | PV_NO_SCRATCH) )
+    {
+      free (vars);
+      return 0;
+    }
+
+  assert(n_vars);
+
+  if ( ! lex_match(T_BY))
+    return 2;
+
+
+  indep_var = parse_variable();
+
+  if ( !indep_var ) 
+    {
+      msg(SE,_("`%s' is not a variable name"),tokid);
+      return 0;
+    }
+
+
+  return 1;
+}
+
+
+/* Show the ANOVA table */
+static void  
+show_anova_table(void)
+{
+  int i;
+  int n_cols =7;
+  int n_rows = n_vars * 3 + 1;
+
+  struct tab_table *t;
+
+
+  t = tab_create (n_cols,n_rows,0);
+  tab_headers (t, 2, 0, 1, 0);
+  tab_dim (t, tab_natural_dimensions);
+
+
+  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);
+  tab_vline (t, TAL_0, 1, 0, 0);
+  
+  tab_text (t, 2, 0, TAB_CENTER | TAT_TITLE, _("Sum of Squares"));
+  tab_text (t, 3, 0, TAB_CENTER | TAT_TITLE, _("df"));
+  tab_text (t, 4, 0, TAB_CENTER | TAT_TITLE, _("Mean Square"));
+  tab_text (t, 5, 0, TAB_CENTER | TAT_TITLE, _("F"));
+  tab_text (t, 6, 0, TAB_CENTER | TAT_TITLE, _("Significance"));
+
+
+  for ( i=0 ; i < n_vars ; ++i ) 
+    {
+      char *s = (vars[i]->label) ? vars[i]->label : vars[i]->name;
+
+      tab_text (t, 0, i * 3 + 1, TAB_LEFT | TAT_TITLE, s);
+      tab_text (t, 1, i * 3 + 1, TAB_LEFT | TAT_TITLE, _("Between Groups"));
+      tab_text (t, 1, i * 3 + 2, TAB_LEFT | TAT_TITLE, _("Within Groups"));
+      tab_text (t, 1, i * 3 + 3, TAB_LEFT | TAT_TITLE, _("Total"));
+      
+      if (i > 0)
+       tab_hline(t, TAL_1, 0, n_cols - 1 , i * 3 + 1);
+    }
+
+
+  tab_title (t, 0, "ANOVA");
+  tab_submit (t);
+
+
+}
+
+
+static void 
+calculate(const struct casefile *cf, void *cmd_)
+{
+  struct casereader *r;
+  struct ccase c;
+
+  struct cmd_t_test *cmd = (struct cmd_t_test *) cmd_;
+
+
+  ind_vals = hsh_create(4, (hsh_compare_func *) compare_values, 
+                          (hsh_hash_func *) hash_value, 
+                          0, (void *) indep_var->width );
+
+  for(r = casefile_get_reader (cf);
+      casereader_read (r, &c) ;
+      case_destroy (&c)) 
+    {
+
+         const union value *val = case_data (&c, indep_var->fv);
+         
+         hsh_insert(ind_vals, (void *) val);
+
+         /* 
+         if (! value_is_missing(val,v) )
+           {
+             gs->n+=weight;
+             gs->sum+=weight * val->f;
+             gs->ssq+=weight * val->f * val->f;
+           }
+         */
+  
+    }
+  casereader_destroy (r);
+
+
+  n_groups = hsh_count(ind_vals);
+
+
+}
+
+
+/* Show the descriptives table */
+static void  
+show_descriptives(void)
+{
+  int v;
+  int n_cols =10;
+  int n_rows = n_vars * (n_groups + 1 )+ 2;
+
+  struct tab_table *t;
+
+
+  t = tab_create (n_cols,n_rows,0);
+  tab_headers (t, 2, 0, 2, 0);
+  tab_dim (t, tab_natural_dimensions);
+
+
+  /* Put a frame around the entire box, and vertical lines inside */
+  tab_box (t, 
+          TAL_2, TAL_2,
+          -1, TAL_1,
+          0, 0,
+          n_cols - 1, n_rows - 1);
+
+  /* Underline headers */
+  tab_hline (t, TAL_2, 0, n_cols - 1, 2 );
+  tab_vline (t, TAL_2, 2, 0, n_rows - 1);
+  
+  tab_text (t, 2, 1, TAB_CENTER | TAT_TITLE, _("N"));
+  tab_text (t, 3, 1, TAB_CENTER | TAT_TITLE, _("Mean"));
+  tab_text (t, 4, 1, TAB_CENTER | TAT_TITLE, _("Std. Deviation"));
+  tab_text (t, 5, 1, TAB_CENTER | TAT_TITLE, _("Std. Error"));
+
+
+  tab_vline(t, TAL_0, 7, 0, 0);
+  tab_hline(t, TAL_1, 6, 7, 1);
+  tab_joint_text (t, 6, 0, 7, 0, TAB_CENTER | TAT_TITLE, _("95% Confidence Interval for Mean"));
+
+  tab_text (t, 6, 1, TAB_CENTER | TAT_TITLE, _("Lower Bound"));
+  tab_text (t, 7, 1, TAB_CENTER | TAT_TITLE, _("Upper Bound"));
+
+  tab_text (t, 8, 1, TAB_CENTER | TAT_TITLE, _("Minimum"));
+  tab_text (t, 9, 1, TAB_CENTER | TAT_TITLE, _("Maximum"));
+
+
+  tab_title (t, 0, "Descriptives");
+
+
+  for ( v=0 ; v < n_vars ; ++v ) 
+    {
+      struct hsh_iterator g;
+      union value *group_value;
+      int count = 0 ;      
+      char *s = (vars[v]->label) ? vars[v]->label : vars[v]->name;
+
+      tab_text (t, 0, v * ( n_groups + 1 ) + 2, TAB_LEFT | TAT_TITLE, s);
+      if ( v > 0) 
+       tab_hline(t, TAL_1, 0, n_cols - 1 , v * (n_groups + 1) + 2);
+
+
+      for (group_value =  hsh_first (ind_vals,&g); 
+          group_value != 0; 
+          group_value = hsh_next(ind_vals,&g))
+       {
+         char *lab;
+
+         lab = val_labs_find(indep_var->val_labs,*group_value);
+  
+         if ( lab ) 
+           tab_text (t, 1, v * (n_groups + 1)+ count + 2, 
+                     TAB_LEFT | TAT_TITLE ,lab);
+         else
+           tab_text (t, 1, v * (n_groups + 1) + count + 2, 
+                     TAB_LEFT | TAT_TITLE | TAT_PRINTF, "%g", group_value->f);
+         
+         count++ ; 
+       }
+
+      tab_text (t, 1, v * (n_groups + 1)+ count + 2, 
+                     TAB_LEFT | TAT_TITLE ,_("Total"));
+      
+
+    }
+
+
+  tab_submit (t);
+
+
+}
+
+
+/* Show the homogeneity table */
+static void 
+show_homogeneity(void)
+{
+  int v;
+  int n_cols = 5;
+  int n_rows = n_vars + 1;
+
+  struct tab_table *t;
+
+
+  t = tab_create (n_cols,n_rows,0);
+  tab_headers (t, 1, 0, 1, 0);
+  tab_dim (t, tab_natural_dimensions);
+
+  /* Put a frame around the entire box, and vertical lines inside */
+  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, 1, 0, n_rows - 1);
+
+
+  tab_text (t,  1, 0, TAB_CENTER | TAT_TITLE, _("Levene Statistic"));
+  tab_text (t,  2, 0, TAB_CENTER | TAT_TITLE, _("df1"));
+  tab_text (t,  3, 0, TAB_CENTER | TAT_TITLE, _("df2"));
+  tab_text (t,  4, 0, TAB_CENTER | TAT_TITLE, _("Significance"));
+  
+
+  tab_title (t, 0, _("Test of Homogeneity of Variances"));
+
+  for ( v=0 ; v < n_vars ; ++v ) 
+    {
+      char *s = (vars[v]->label) ? vars[v]->label : vars[v]->name;
+
+      tab_text (t, 0, v + 1, TAB_LEFT | TAT_TITLE, s);
+    }
+
+  tab_submit (t);
+
+
+}
+
+
+/* Show the contrast coefficients table */
+static void 
+show_contrast_coeffs(void)
+{
+  char *s;
+  int n_cols = 2 + n_groups;
+  int n_rows = 2 + cmd.sbc_contrast;
+  struct hsh_iterator g;
+  union value *group_value;
+  int count = 0 ;      
+
+
+  struct tab_table *t;
+
+
+  t = tab_create (n_cols,n_rows,0);
+  tab_headers (t, 2, 0, 2, 0);
+  tab_dim (t, tab_natural_dimensions);
+
+  /* Put a frame around the entire box, and vertical lines inside */
+  tab_box (t, 
+          TAL_2, TAL_2,
+          -1, TAL_1,
+          0, 0,
+          n_cols - 1, n_rows - 1);
+
+
+  tab_box (t, 
+          -1,-1,
+          TAL_0, TAL_0,
+          2, 0,
+          n_cols - 1, 0);
+
+  tab_box (t,
+          -1,-1,
+          TAL_0, TAL_0,
+          0,0,
+          1,1);
+
+
+  tab_hline(t, TAL_1, 2, n_cols - 1, 1);
+
+
+  tab_hline(t, TAL_2, 0, n_cols - 1, 2);
+  tab_vline(t, TAL_2, 2, 0, n_rows - 1);
+
+
+  tab_title (t, 0, _("Contrast Coefficients"));
+
+  tab_text (t,  0, 2, TAB_LEFT | TAT_TITLE, _("Contrast"));
+
+  s = (indep_var->label) ? indep_var->label : indep_var->name;
+
+  tab_joint_text (t, 2, 0, n_cols - 1, 0, TAB_CENTER | TAT_TITLE, s);
+
+  for (group_value =  hsh_first (ind_vals,&g); 
+       group_value != 0; 
+       group_value = hsh_next(ind_vals,&g))
+    {
+      int i;
+      char *lab;
+
+      lab = val_labs_find(indep_var->val_labs,*group_value);
+  
+      if ( lab ) 
+       tab_text (t, count + 2, 1,
+                 TAB_CENTER | TAT_TITLE ,lab);
+      else
+       tab_text (t, count + 2, 1, 
+                 TAB_CENTER | TAT_TITLE | TAT_PRINTF, "%g", group_value->f);
+
+      for (i = 0 ; i < cmd.sbc_contrast ; ++i ) 
+       {
+         tab_text(t, 1, i + 2, TAB_CENTER | TAT_PRINTF, "%d", i + 1);
+         tab_text(t, count + 2, i + 2, TAB_RIGHT | TAT_PRINTF, "%g", 
+                  subc_list_double_at(&cmd.dl_contrast[i],count)
+                  );
+       }
+         
+      count++ ; 
+    }
+
+  tab_submit (t);
+
+}
+
+
+
+/* Show the results of the contrast tests */
+static void 
+show_contrast_tests(void)
+{
+  int v;
+  int n_cols = 8;
+  int n_rows = 1 + n_vars * 2 * cmd.sbc_contrast;
+
+  struct tab_table *t;
+
+  t = tab_create (n_cols,n_rows,0);
+  tab_headers (t, 3, 0, 1, 0);
+  tab_dim (t, tab_natural_dimensions);
+
+  /* Put a frame around the entire box, and vertical lines inside */
+  tab_box (t, 
+          TAL_2, TAL_2,
+          -1, TAL_1,
+          0, 0,
+          n_cols - 1, n_rows - 1);
+
+
+  tab_box (t, 
+          -1,-1,
+          TAL_0, TAL_0,
+          0, 0,
+          2, 0);
+
+  tab_hline(t, TAL_2, 0, n_cols - 1, 1);
+  tab_vline(t, TAL_2, 3, 0, n_rows - 1);
+
+
+  tab_title (t, 0, _("Contrast Tests"));
+
+  tab_text (t,  2, 0, TAB_CENTER | TAT_TITLE, _("Contrast"));
+  tab_text (t,  3, 0, TAB_CENTER | TAT_TITLE, _("Value of Contrast"));
+  tab_text (t,  4, 0, TAB_CENTER | TAT_TITLE, _("Std. Error"));
+  tab_text (t,  5, 0, TAB_CENTER | TAT_TITLE, _("t"));
+  tab_text (t,  6, 0, TAB_CENTER | TAT_TITLE, _("df"));
+  tab_text (t,  7, 0, TAB_CENTER | TAT_TITLE, _("Sig. (2-tailed)"));
+
+  for ( v = 0 ; v < n_vars ; ++v ) 
+    {
+      int i;
+      int lines_per_variable = 2 * cmd.sbc_contrast;
+
+      tab_text (t,  0, (v * lines_per_variable) + 1, TAB_LEFT | TAT_TITLE,
+               vars[v]->label?vars[v]->label:vars[v]->name);
+
+      for ( i = 0 ; i < cmd.sbc_contrast ; ++i ) 
+       {
+         tab_text (t,  1, (v * lines_per_variable) + i*2 + 1, 
+                   TAB_LEFT | TAT_TITLE, 
+                   _("Assume equal variances"));
+
+         tab_text (t,  1, (v * lines_per_variable) + i*2 + 2, 
+                   TAB_LEFT | TAT_TITLE, 
+                   _("Does not assume equal"));
+
+
+         tab_text (t,  2, (v * lines_per_variable) + i*2 + 1, 
+                   TAB_CENTER | TAT_TITLE | TAT_PRINTF, "%d",i+1);
+
+         tab_text (t,  2, (v * lines_per_variable) + i*2 + 2, 
+                   TAB_CENTER | TAT_TITLE | TAT_PRINTF, "%d",i+1);
+
+       }
+
+      if ( v > 0 ) 
+       tab_hline(t, TAL_1, 0, n_cols - 1, (v * lines_per_variable) + 1);
+    }
+
+  tab_submit (t);
+
+}
index 1c0ddb79669d73e99760e5b40c0b8bd77457ed09..e8f09d8d5daa38f195166f44355de5f0a84ce7ea 100644 (file)
--- a/src/q2c.c
+++ b/src/q2c.c
@@ -30,6 +30,7 @@
 #endif
 #include "str.h"
 
+
 /* Brokenness. */
 #ifndef EXIT_SUCCESS
 #define EXIT_SUCCESS 0
@@ -837,7 +838,12 @@ parse_subcommand (subcommand *sbc)
       else if (match_id ("PINT"))
        sbc->type = SBC_PINT;
       else if (match_id ("DOUBLE"))
-       sbc->type = match_id ("LIST") ? SBC_DBL_LIST : SBC_DBL;
+       {
+         if ( match_id ("LIST") )
+           sbc->type = SBC_DBL_LIST;
+         else
+           sbc->type = SBC_DBL;
+       }
       else if (match_id ("STRING"))
        {
          sbc->type = SBC_STRING;
@@ -1033,6 +1039,12 @@ dump_declarations (void)
       }
   }
 
+  /* Write out some type definitions */
+  {
+    dump (0, "#define MAXLISTS 10");
+  }
+
+
   /* For every array subcommand, write out the associated enumerated
      values. */
   {
@@ -1132,6 +1144,17 @@ dump_declarations (void)
            dump (0, "double n_%s;", st_lower (sbc->name));
            break;
 
+         case SBC_DBL_LIST:
+           dump (0, "subc_list_double dl_%s[MAXLISTS];",
+                 st_lower(sbc->name));
+           break;
+
+         case SBC_INT_LIST:
+           dump (0, "subc_list_int il_%s[MAXLISTS];",
+                 st_lower(sbc->name));
+           break;
+
+
          default:;
            /* nothing */
          }
@@ -1227,9 +1250,20 @@ dump_vars_init (int persistent)
          {
            switch (sbc->type)
              {
-             case SBC_DBL:
              case SBC_INT_LIST:
+               break;
+
              case SBC_DBL_LIST:
+               dump (0, "int i;");
+               dump (0, "for (i = 0; i < MAXLISTS; ++i)");
+               dump (1, "{");
+               dump (0, "subc_list_double_create(&p->dl_%s[i]) ;",
+                     st_lower (sbc->name)
+                     );
+               dump (-1, "}");
+               break;
+
+             case SBC_DBL:
              case SBC_CUSTOM:
                /* nothing */
                break;
@@ -1595,7 +1629,7 @@ dump_subcommand (const subcommand *sbc)
        {
          char buf[1024];
          dump (1, "if (!(%s))", sbc->restriction);
-         dump (1, "{");
+         dump (1, "{"); 
           sprintf(buf,sbc->message,sbc->name);
          if ( sbc->translatable ) 
                  dump (0, "msg (SE, gettext(\"%s\"));",buf);
@@ -1615,6 +1649,30 @@ dump_subcommand (const subcommand *sbc)
       dump (-1, "p->n_%s = lex_integer ();", st_lower (sbc->name));
       dump (0, "lex_match (')');");
     }
+  else if (sbc->type == SBC_DBL_LIST)
+    {
+      dump (0, "if ( p->sbc_%s > MAXLISTS)",st_lower(sbc->name));
+      dump (1, "{");
+      dump (0, "msg (SE, \"No more than %%d %s subcommands allowed\",MAXLISTS);",st_lower(sbc->name));
+      dump (0, "goto lossage;");
+      dump (-1,"}");
+
+      dump (1, "while (token != '/' && token != '.')");
+      dump (1, "{");
+      dump (0, "lex_match(',');");
+      dump (0, "if (!lex_force_num ())");
+      dump (1, "{");
+      dump (0, "goto lossage;");
+      dump (-1,"}");
+
+      dump (0, "subc_list_double_push(&p->dl_%s[p->sbc_%s-1],lex_double ());", 
+           st_lower (sbc->name),st_lower (sbc->name)
+           );
+
+      dump (0, "lex_get();");
+      dump (-1,"}");
+
+    }
   else if (sbc->type == SBC_CUSTOM)
     {
       dump (1, "switch (%scustom_%s (p))",
@@ -1927,6 +1985,7 @@ dump_header (void)
 
   dump (0, nullstr);
   dump (0, "#include \"settings.h\"");
+  dump (0, "#include \"subclist.h\"");
   dump (0, nullstr);
 }
 
@@ -1943,8 +2002,13 @@ dump_free (int persistent)
   if ( ! persistent ) 
     {
       for (sbc = subcommands; sbc; sbc = sbc->next)
+       {
        if (sbc->type == SBC_STRING)
          used = 1;
+       if (sbc->type == SBC_DBL_LIST)
+         used = 1;
+       }
+
     }
 
   dump (0, "static void");
@@ -1956,8 +2020,19 @@ dump_free (int persistent)
     {
 
       for (sbc = subcommands; sbc; sbc = sbc->next)
-       if (sbc->type == SBC_STRING)
-         dump (0, "free (p->s_%s);", st_lower (sbc->name));
+       {
+         switch (sbc->type) 
+           {
+           case SBC_STRING:
+             dump (0, "free (p->s_%s);", st_lower (sbc->name));
+             break;
+           case SBC_DBL_LIST:
+             dump (0, "subc_list_double_destroy(p->dl_%s);", st_lower (sbc->name));
+             break;
+           default:
+             break;
+           }
+       }
     }
 
   dump (-1, "}");
diff --git a/src/subclist.c b/src/subclist.c
new file mode 100644 (file)
index 0000000..d2add44
--- /dev/null
@@ -0,0 +1,74 @@
+/* subclist - lists for PSPP subcommands
+
+Copyright (C) 2004 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., 59 Temple Place - Suite 330, Boston, MA
+02111-1307, USA. */
+
+
+#include "subclist.h"
+#include <stdlib.h>
+
+/* I call these objects `lists' but they are in fact simple dynamic arrays */
+
+#define CHUNKSIZE 16
+
+/* Create a  list */
+void
+subc_list_double_create(subc_list_double *l)
+{
+  l->data = (double *) malloc(CHUNKSIZE * sizeof (double));
+  l->sz = CHUNKSIZE;
+  l->n_data = 0;
+}
+
+/* Push a value onto the list */
+void
+subc_list_double_push(subc_list_double *l, double d)
+{
+  l->data[l->n_data++] = d;
+
+  if (l->n_data >= l->sz ) 
+    {
+      l->sz += CHUNKSIZE;
+      l->data = realloc(l->data, l->sz * sizeof(double));
+    }
+
+}
+
+/* Return the number of items in the list */
+int 
+subc_list_double_count(subc_list_double *l)
+{
+  return l->n_data;
+}
+
+
+/* Index into the list (array) */
+double
+subc_list_double_at(subc_list_double *l, int idx)
+{
+  return l->data[idx];
+}
+
+/* Free up the list */
+void
+subc_list_double_destroy(subc_list_double *l)
+{
+  free(l->data);
+}
diff --git a/src/subclist.h b/src/subclist.h
new file mode 100644 (file)
index 0000000..b311bc6
--- /dev/null
@@ -0,0 +1,72 @@
+#ifndef SUBCLIST_H
+#define SUBCLIST_H
+
+/* subclist - lists for PSPP subcommands
+
+   Copyright (C) 2004 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., 59 Temple Place - Suite 330, Boston, MA
+   02111-1307, USA. */
+
+
+
+#include <sys/types.h>
+
+/* This module provides a rudimentary list class
+   It is intended for use by the command line parser for list subcommands
+*/
+
+
+struct subc_list_double {
+  double *data ;
+  size_t sz;
+  int n_data;
+};
+
+struct subc_list_int {
+  int *data ;
+  size_t sz;
+  int n_data;
+};
+
+
+typedef struct subc_list_double subc_list_double ;
+typedef struct subc_list_int subc_list_int ;
+
+/* Create a  list */
+void subc_list_double_create(subc_list_double *l) ;
+void subc_list_int_create(subc_list_int *l) ;
+
+/* Push a value onto the list */
+void subc_list_double_push(subc_list_double *l, double d) ;
+void subc_list_int_push(subc_list_int *l, int i) ;
+
+/* Index into the list */
+double subc_list_double_at(subc_list_double *l, int idx);
+int subc_list_int_at(subc_list_int *l, int idx);
+
+/* Return the number of values in the list */
+int subc_list_double_count(subc_list_double *l);
+int subc_list_int_count(subc_list_int *l);
+
+/* Destroy the list */
+void subc_list_double_destroy(subc_list_double *l) ;
+void subc_list_int_destroy(subc_list_int *l) ;
+
+
+#endif
index 8afb8f0be0e3793d85675de5dc6fd5d87867e6b2..dd901493db8765896b379cdf1d3b2e3a8a7e7fa4 100644 (file)
--- a/src/tab.c
+++ b/src/tab.c
@@ -210,6 +210,12 @@ void
 tab_headers (struct tab_table *table, int l, int r, int t, int b)
 {
   assert (table != NULL);
+  assert (l < table->nc);
+  assert (r < table->nc);
+  assert (t < table->nr);
+  assert (b < table->nr);
+
+
   table->l = l;
   table->r = r;
   table->t = t;
index fe35aad9aeb74f5f799b8004ff395264eac97340..fc098fda975af76ed6559255f371961ab212a1d1 100644 (file)
--- a/src/val.h
+++ b/src/val.h
@@ -69,5 +69,7 @@ union value
 
 int compare_values (const union value *a, const union value *b, int width);
 
+unsigned  hash_value(const union value  *v, int width);
+
 
 #endif /* !val_h */
index cf4ef4e32c2030d5fbd9ab21498f545e782de241..0b6877dbf72c1012d6c2d7ffa44a5e3d654689ef 100644 (file)
@@ -45,6 +45,22 @@ compare_values (const union value *a, const union value *b, int width)
     return memcmp (a->s, b->s, width);
 }
 
+/* Create a hash of v */
+unsigned 
+hash_value(const union value  *v, int width)
+{
+  unsigned id_hash;
+
+  if ( 0 == width ) 
+    id_hash = hsh_hash_double (v->f);
+  else
+    id_hash = hsh_hash_bytes (v->s, width);
+
+  return id_hash;
+}
+
+
+
 /* Discards all the current state in preparation for a data-input
    command like DATA LIST or GET. */
 void