Actually implement the new procedure code and adapt all of its clients
[pspp-builds.git] / src / language / stats / crosstabs.q
index fc8571c46aee0f8c59c9066c27f94b085b728de9..1d2bdf74b4f0f6e7508b067dcf70e8e36100a5b5 100644 (file)
@@ -1,6 +1,5 @@
 /* PSPP - computes sample statistics.
    Copyright (C) 1997-9, 2000, 2006 Free Software Foundation, Inc.
-   Written by Ben Pfaff <blp@gnu.org>.
 
    This program is free software; you can redistribute it and/or
    modify it under the terms of the GNU General Public License as
 #include <stdio.h>
 
 #include <data/case.h>
+#include <data/casegrouper.h>
+#include <data/casereader.h>
 #include <data/data-out.h>
 #include <data/dictionary.h>
+#include <data/format.h>
 #include <data/procedure.h>
 #include <data/value-labels.h>
 #include <data/variable.h>
@@ -116,7 +118,7 @@ struct crosstab
     int nvar;                  /* Number of variables. */
     double missing;            /* Missing cases count. */
     int ofs;                   /* Integer mode: Offset into sorted_tab[]. */
-    struct variable *vars[2];  /* At least two variables; sorted by
+    const struct variable *vars[2];    /* At least two variables; sorted by
                                   larger indices first. */
   };
 
@@ -129,11 +131,9 @@ struct var_range
   };
 
 static inline struct var_range *
-get_var_range (struct variable *v) 
+get_var_range (const struct variable *v) 
 {
-  assert (v != NULL);
-  assert (v->aux != NULL);
-  return v->aux;
+  return var_get_aux (v);
 }
 
 /* Indexes into crosstab.v. */
@@ -149,7 +149,7 @@ static int n_sorted_tab;            /* Number of entries in sorted_tab. */
 static struct table_entry **sorted_tab;        /* Sorted table. */
 
 /* Variables specifies on VARIABLES. */
-static struct variable **variables;
+static const struct variable **variables;
 static size_t variables_cnt;
 
 /* TABLES. */
@@ -169,7 +169,7 @@ static int num_cells;               /* Number of cells requested. */
 static int cells[8];           /* Cells requested. */
 
 /* WRITE. */
-static int write;              /* One of WR_* that specifies the WRITE style. */
+static int write_style;                /* One of WR_* that specifies the WRITE style. */
 
 /* Command parsing info. */
 static struct cmd_crosstabs cmd;
@@ -179,10 +179,10 @@ static struct pool *pl_tc;        /* For table cells. */
 static struct pool *pl_col;    /* For column data. */
 
 static int internal_cmd_crosstabs (struct lexer *lexer, struct dataset *ds);
-static void precalc (const struct ccase *, void *, const struct dataset *);
-static bool calc_general (const struct ccase *, void *, const struct dataset *);
-static bool calc_integer (const struct ccase *, void *, const struct dataset *);
-static bool postcalc (void *, const struct dataset *);
+static void precalc (struct casereader *, const struct dataset *);
+static void calc_general (struct ccase *, const struct dataset *);
+static void calc_integer (struct ccase *, const struct dataset *);
+static void postcalc (void);
 static void submit (struct tab_table *);
 
 static void format_short (char *s, const struct fmt_spec *fp,
@@ -205,8 +205,10 @@ cmd_crosstabs (struct lexer *lexer, struct dataset *ds)
 static int
 internal_cmd_crosstabs (struct lexer *lexer, struct dataset *ds)
 {
-  int i;
+  struct casegrouper *grouper;
+  struct casereader *input, *group;
   bool ok;
+  int i;
 
   variables = NULL;
   variables_cnt = 0;
@@ -290,15 +292,34 @@ internal_cmd_crosstabs (struct lexer *lexer, struct dataset *ds)
          + cmd.a_write[CRS_WR_CELLS] == 0))
     cmd.a_write[CRS_WR_CELLS] = 1;
   if (cmd.a_write[CRS_WR_CELLS])
-    write = CRS_WR_CELLS;
+    write_style = CRS_WR_CELLS;
   else if (cmd.a_write[CRS_WR_ALL])
-    write = CRS_WR_ALL;
+    write_style = CRS_WR_ALL;
   else
-    write = CRS_WR_NONE;
+    write_style = CRS_WR_NONE;
 
-  ok = procedure_with_splits (ds, precalc,
-                              mode == GENERAL ? calc_general : calc_integer,
-                              postcalc, NULL);
+  input = casereader_create_filter_weight (proc_open (ds), dataset_dict (ds),
+                                           NULL, NULL);
+  grouper = casegrouper_create_splits (input, dataset_dict (ds));
+  while (casegrouper_get_next_group (grouper, &group)) 
+    {
+      struct ccase c;
+      
+      precalc (group, ds);
+      
+      for (; casereader_read (group, &c); case_destroy (&c)) 
+        {
+          if (mode == GENERAL)
+            calc_general (&c, ds);
+          else
+            calc_integer (&c, ds); 
+        }
+      casereader_destroy (group);
+
+      postcalc ();
+    }
+  ok = casegrouper_destroy (grouper);
+  ok = proc_commit (ds) && ok;
 
   return ok ? CMD_SUCCESS : CMD_CASCADING_FAILURE;
 }
@@ -307,31 +328,32 @@ internal_cmd_crosstabs (struct lexer *lexer, struct dataset *ds)
 static int
 crs_custom_tables (struct lexer *lexer, struct dataset *ds, struct cmd_crosstabs *cmd UNUSED, void *aux UNUSED)
 {
-  struct var_set *var_set;
+  struct const_var_set *var_set;
   int n_by;
-  struct variable ***by = NULL;
+  const struct variable ***by = NULL;
   size_t *by_nvar = NULL;
   size_t nx = 1;
   int success = 0;
 
   /* Ensure that this is a TABLES subcommand. */
   if (!lex_match_id (lexer, "TABLES")
-      && (lex_token (lexer) != T_ID || dict_lookup_var (dataset_dict (ds), lex_tokid (lexer)) == NULL)
+      && (lex_token (lexer) != T_ID || 
+         dict_lookup_var (dataset_dict (ds), lex_tokid (lexer)) == NULL)
       && lex_token (lexer) != T_ALL)
     return 2;
   lex_match (lexer, '=');
 
   if (variables != NULL)
-    var_set = var_set_create_from_array (variables, variables_cnt);
+    var_set = const_var_set_create_from_array (variables, variables_cnt);
   else
-    var_set = var_set_create_from_dict (dataset_dict (ds));
+    var_set = const_var_set_create_from_dict (dataset_dict (ds));
   assert (var_set != NULL);
   
   for (n_by = 0; ;)
     {
       by = xnrealloc (by, n_by + 1, sizeof *by);
       by_nvar = xnrealloc (by_nvar, n_by + 1, sizeof *by_nvar);
-      if (!parse_var_set_vars (lexer, var_set, &by[n_by], &by_nvar[n_by],
+      if (!parse_const_var_set_vars (lexer, var_set, &by[n_by], &by_nvar[n_by],
                                PV_NO_DUPLICATE | PV_NO_SCRATCH))
        goto done;
       if (xalloc_oversized (nx, by_nvar[n_by])) 
@@ -402,7 +424,7 @@ crs_custom_tables (struct lexer *lexer, struct dataset *ds, struct cmd_crosstabs
     free (by_nvar);
   }
 
-  var_set_destroy (var_set);
+  const_var_set_destroy (var_set);
 
   return success;
 }
@@ -426,7 +448,7 @@ crs_custom_variables (struct lexer *lexer, struct dataset *ds, struct cmd_crosst
 
       long min, max;
       
-      if (!parse_variables (lexer, dataset_dict (ds), 
+      if (!parse_variables_const (lexer, dataset_dict (ds), 
                            &variables, &variables_cnt,
                            (PV_APPEND | PV_NUMERIC
                             | PV_NO_DUPLICATE | PV_NO_SCRATCH)))
@@ -491,10 +513,16 @@ static int compare_table_entry (const void *, const void *, const void *);
 static unsigned hash_table_entry (const void *, const void *);
 
 /* Set up the crosstabulation tables for processing. */
-static  void
-precalc (const struct ccase *first, void *aux UNUSED, const struct dataset *ds)
+static void
+precalc (struct casereader *input, const struct dataset *ds)
 {
-  output_split_file_values (ds, first);
+  struct ccase c;
+
+  if (!casereader_peek (input, 0, &c))
+    return;
+  output_split_file_values (ds, &c);
+  case_destroy (&c);
+
   if (mode == GENERAL)
     {
       gen_tab = hsh_create (512, compare_table_entry, hash_table_entry,
@@ -566,13 +594,16 @@ precalc (const struct ccase *first, void *aux UNUSED, const struct dataset *ds)
 }
 
 /* Form crosstabulations for general mode. */
-static bool
-calc_general (const struct ccase *c, void *aux UNUSED, const struct dataset *ds)
+static void
+calc_general (struct ccase *c, const struct dataset *ds)
 {
-  bool bad_warn = true;
+  /* Missing values to exclude. */
+  enum mv_class exclude = (cmd.miss == CRS_TABLE ? MV_ANY
+                           : cmd.miss == CRS_INCLUDE ? MV_SYSTEM
+                           : MV_NEVER);
 
   /* Case weight. */
-  double weight = dict_get_case_weight (dataset_dict (ds), c, &bad_warn);
+  double weight = dict_get_case_weight (dataset_dict (ds), c, NULL);
 
   /* Flattened current table index. */
   int t;
@@ -592,20 +623,18 @@ calc_general (const struct ccase *c, void *aux UNUSED, const struct dataset *ds)
        assert (x != NULL);
        for (j = 0; j < x->nvar; j++)
          {
-            const union value *v = case_data (c, x->vars[j]->fv);
-           if ((cmd.miss == CRS_TABLE && var_is_value_missing (x->vars[j], v))
-               || (cmd.miss == CRS_INCLUDE
-                   && var_is_value_system_missing (x->vars[j], v)))
+            const union value *v = case_data (c, x->vars[j]);
+            if (var_is_value_missing (x->vars[j], v, exclude))
              {
                x->missing += weight;
                goto next_crosstab;
              }
              
            if (var_is_numeric (x->vars[j]))
-             te->values[j].f = case_num (c, x->vars[j]->fv);
+             te->values[j].f = case_num (c, x->vars[j]);
            else
              {
-               memcpy (te->values[j].s, case_str (c, x->vars[j]->fv),
+               memcpy (te->values[j].s, case_str (c, x->vars[j]),
                         var_get_width (x->vars[j]));
              
                /* Necessary in order to simplify comparisons. */
@@ -635,12 +664,10 @@ calc_general (const struct ccase *c, void *aux UNUSED, const struct dataset *ds)
     next_crosstab:
       local_free (te);
     }
-  
-  return true;
 }
 
-static bool
-calc_integer (const struct ccase *c, void *aux UNUSED, const struct dataset *ds)
+static void
+calc_integer (struct ccase *c, const struct dataset *ds)
 {
   bool bad_warn = true;
 
@@ -659,13 +686,14 @@ calc_integer (const struct ccase *c, void *aux UNUSED, const struct dataset *ds)
       ofs = x->ofs;
       for (i = 0; i < x->nvar; i++)
        {
-         struct variable *const v = x->vars[i];
+         const struct variable *const v = x->vars[i];
           struct var_range *vr = get_var_range (v);
-         double value = case_num (c, v->fv);
+         double value = case_num (c, v);
          
          /* Note that the first test also rules out SYSMIS. */
          if ((value < vr->min || value >= vr->max)
-             || (cmd.miss == CRS_TABLE && var_is_num_user_missing (v, value)))
+             || (cmd.miss == CRS_TABLE
+                  && var_is_num_missing (v, value, MV_USER)))
            {
              x->missing += weight;
              goto next_crosstab;
@@ -679,11 +707,11 @@ calc_integer (const struct ccase *c, void *aux UNUSED, const struct dataset *ds)
        }
       
       {
-        struct variable *row_var = x->vars[ROW_VAR];
-       const int row = case_num (c, row_var->fv) - get_var_range (row_var)->min;
+        const struct variable *row_var = x->vars[ROW_VAR];
+       const int row = case_num (c, row_var) - get_var_range (row_var)->min;
 
-        struct variable *col_var = x->vars[COL_VAR];
-       const int col = case_num (c, col_var->fv) - get_var_range (col_var)->min;
+        const struct variable *col_var = x->vars[COL_VAR];
+       const int col = case_num (c, col_var) - get_var_range (col_var)->min;
 
        const int col_dim = get_var_range (col_var)->count;
 
@@ -692,8 +720,6 @@ calc_integer (const struct ccase *c, void *aux UNUSED, const struct dataset *ds)
       
     next_crosstab: ;
     }
-  
-  return true;
 }
 
 /* Compare the table_entry's at A and B and return a strcmp()-type
@@ -761,8 +787,8 @@ static void output_pivot_table (struct table_entry **, struct table_entry **,
                                int *, int *, int *);
 static void make_summary_table (void);
 
-static bool
-postcalc (void *aux UNUSED, const struct dataset *ds UNUSED)
+static void
+postcalc (void)
 {
   if (mode == GENERAL)
     {
@@ -798,8 +824,6 @@ postcalc (void *aux UNUSED, const struct dataset *ds UNUSED)
   }
   
   hsh_destroy (gen_tab);
-
-  return true;
 }
 
 static void insert_summary (struct tab_table *, int tab_index, double valid);
@@ -1423,7 +1447,7 @@ delete_missing (void)
     int r;
 
     for (r = 0; r < n_rows; r++)
-      if (var_is_num_user_missing (x->vars[ROW_VAR], rows[r].f))
+      if (var_is_num_missing (x->vars[ROW_VAR], rows[r].f, MV_USER))
        {
          int c;
 
@@ -1437,7 +1461,7 @@ delete_missing (void)
     int c;
 
     for (c = 0; c < n_cols; c++)
-      if (var_is_num_user_missing (x->vars[COL_VAR], cols[c].f))
+      if (var_is_num_missing (x->vars[COL_VAR], cols[c].f, MV_USER))
        {
          int r;
 
@@ -1631,7 +1655,7 @@ static void
 enum_var_values (struct table_entry **entries, int entry_cnt, int var_idx,
                  union value **values, int *value_cnt)
 {
-  struct variable *v = xtab[(*entries)->table]->vars[var_idx];
+  const struct variable *v = xtab[(*entries)->table]->vars[var_idx];
 
   if (mode == GENERAL)
     {
@@ -1667,7 +1691,7 @@ table_value_missing (struct tab_table *table, int c, int r, unsigned char opt,
   struct substring s;
   const struct fmt_spec *print = var_get_print_format (var);
 
-  const char *label = val_labs_find (var->val_labs, *v);
+  const char *label = var_lookup_value_label (var, v);
   if (label) 
     {
       tab_text (table, c, r, TAB_LEFT, label);
@@ -1677,7 +1701,7 @@ table_value_missing (struct tab_table *table, int c, int r, unsigned char opt,
   s.string = tab_alloc (table, print->w);
   format_short (s.string, print, v);
   s.length = strlen (s.string);
-  if (cmd.miss == CRS_REPORT && var_is_num_user_missing (var, v->f))
+  if (cmd.miss == CRS_REPORT && var_is_num_missing (var, v->f, MV_USER))
     s.string[s.length++] = 'M';
   while (s.length && *s.string == ' ')
     {
@@ -1760,8 +1784,9 @@ display_crosstabulation (void)
             bool mark_missing = false;
             double expected_value = row_tot[r] * col_tot[c] / W;
             if (cmd.miss == CRS_REPORT
-                && (var_is_num_user_missing (x->vars[COL_VAR], cols[c].f)
-                    || var_is_num_user_missing (x->vars[ROW_VAR], rows[r].f)))
+                && (var_is_num_missing (x->vars[COL_VAR], cols[c].f, MV_USER)
+                    || var_is_num_missing (x->vars[ROW_VAR], rows[r].f,
+                                           MV_USER)))
               mark_missing = true;
            for (i = 0; i < num_cells; i++)
              {
@@ -1825,7 +1850,7 @@ display_crosstabulation (void)
         bool mark_missing = false;
 
         if (cmd.miss == CRS_REPORT
-            && var_is_num_user_missing (x->vars[ROW_VAR], rows[r].f))
+            && var_is_num_missing (x->vars[ROW_VAR], rows[r].f, MV_USER))
           mark_missing = true;
 
         for (i = 0; i < num_cells; i++)
@@ -1880,7 +1905,7 @@ display_crosstabulation (void)
         int i;
            
         if (cmd.miss == CRS_REPORT && c < n_cols 
-            && var_is_num_user_missing (x->vars[COL_VAR], cols[c].f))
+            && var_is_num_missing (x->vars[COL_VAR], cols[c].f, MV_USER))
           mark_missing = true;
 
         for (i = 0; i < num_cells; i++)