Rewrite and improve formatted output routines.
[pspp-builds.git] / src / language / stats / crosstabs.q
index 55c92d8c4b2e9b6e768fdcecc9aa1085dab14664..67ee5aabaaf0055c25977673e01774624c2d21fa 100644 (file)
@@ -1,5 +1,5 @@
 /* PSPP - computes sample statistics.
-   Copyright (C) 1997-9, 2000 Free Software Foundation, Inc.
+   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
 #include <stdio.h>
 
 #include <data/case.h>
+#include <data/data-out.h>
 #include <data/dictionary.h>
 #include <data/procedure.h>
 #include <data/value-labels.h>
 #include <data/variable.h>
 #include <language/command.h>
+#include <language/dictionary/split-file.h>
 #include <language/lexer/lexer.h>
+#include <language/lexer/variable-parser.h>
 #include <libpspp/alloc.h>
 #include <libpspp/array.h>
+#include <libpspp/assertion.h>
 #include <libpspp/compiler.h>
 #include <libpspp/hash.h>
 #include <libpspp/magic.h>
@@ -66,7 +70,7 @@
    crosstabs (crs_):
      *^tables=custom;
      +variables=custom;
-     +missing=miss:!table/include/report;
+     missing=miss:!table/include/report;
      +write[wr_]=none,cells,all;
      +format=fmt:!labels/nolabels/novallabs,
             val:!avalue/dvalue,
@@ -172,11 +176,11 @@ static struct cmd_crosstabs cmd;
 static struct pool *pl_tc;     /* For table cells. */
 static struct pool *pl_col;    /* For column data. */
 
-static int internal_cmd_crosstabs (void);
-static void precalc (void *);
-static bool calc_general (struct ccase *, void *);
-static bool calc_integer (struct ccase *, void *);
-static void postcalc (void *);
+static int internal_cmd_crosstabs (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 submit (struct tab_table *);
 
 static void format_short (char *s, const struct fmt_spec *fp,
@@ -184,9 +188,9 @@ static void format_short (char *s, const struct fmt_spec *fp,
 
 /* Parse and execute CROSSTABS, then clean up. */
 int
-cmd_crosstabs (void)
+cmd_crosstabs (struct dataset *ds)
 {
-  int result = internal_cmd_crosstabs ();
+  int result = internal_cmd_crosstabs (ds);
 
   free (variables);
   pool_destroy (pl_tc);
@@ -197,7 +201,7 @@ cmd_crosstabs (void)
 
 /* Parses and executes the CROSSTABS procedure. */
 static int
-internal_cmd_crosstabs (void)
+internal_cmd_crosstabs (struct dataset *ds)
 {
   int i;
   bool ok;
@@ -209,7 +213,7 @@ internal_cmd_crosstabs (void)
   pl_tc = pool_create ();
   pl_col = pool_create ();
 
-  if (!parse_crosstabs (&cmd))
+  if (!parse_crosstabs (ds, &cmd, NULL))
     return CMD_FAILURE;
 
   mode = variables ? INTEGER : GENERAL;
@@ -290,7 +294,7 @@ internal_cmd_crosstabs (void)
   else
     write = CRS_WR_NONE;
 
-  ok = procedure_with_splits (precalc,
+  ok = procedure_with_splits (ds, precalc,
                               mode == GENERAL ? calc_general : calc_integer,
                               postcalc, NULL);
 
@@ -299,7 +303,7 @@ internal_cmd_crosstabs (void)
 
 /* Parses the TABLES subcommand. */
 static int
-crs_custom_tables (struct cmd_crosstabs *cmd UNUSED)
+crs_custom_tables (struct dataset *ds, struct cmd_crosstabs *cmd UNUSED, void *aux UNUSED)
 {
   struct var_set *var_set;
   int n_by;
@@ -310,7 +314,7 @@ crs_custom_tables (struct cmd_crosstabs *cmd UNUSED)
 
   /* Ensure that this is a TABLES subcommand. */
   if (!lex_match_id ("TABLES")
-      && (token != T_ID || dict_lookup_var (default_dict, tokid) == NULL)
+      && (token != T_ID || dict_lookup_var (dataset_dict (ds), tokid) == NULL)
       && token != T_ALL)
     return 2;
   lex_match ('=');
@@ -318,7 +322,7 @@ crs_custom_tables (struct cmd_crosstabs *cmd UNUSED)
   if (variables != NULL)
     var_set = var_set_create_from_array (variables, variables_cnt);
   else
-    var_set = var_set_create_from_dict (default_dict);
+    var_set = var_set_create_from_dict (dataset_dict (ds));
   assert (var_set != NULL);
   
   for (n_by = 0; ;)
@@ -403,7 +407,7 @@ crs_custom_tables (struct cmd_crosstabs *cmd UNUSED)
 
 /* Parses the VARIABLES subcommand. */
 static int
-crs_custom_variables (struct cmd_crosstabs *cmd UNUSED)
+crs_custom_variables (struct dataset *ds, struct cmd_crosstabs *cmd UNUSED, void *aux UNUSED)
 {
   if (nxtab)
     {
@@ -420,7 +424,7 @@ crs_custom_variables (struct cmd_crosstabs *cmd UNUSED)
 
       long min, max;
       
-      if (!parse_variables (default_dict, &variables, &variables_cnt,
+      if (!parse_variables (dataset_dict (ds), &variables, &variables_cnt,
                            (PV_APPEND | PV_NUMERIC
                             | PV_NO_DUPLICATE | PV_NO_SCRATCH)))
        return 0;
@@ -480,13 +484,14 @@ crs_custom_variables (struct cmd_crosstabs *cmd UNUSED)
 \f
 /* Data file processing. */
 
-static int compare_table_entry (const void *, const void *, void *);
-static unsigned hash_table_entry (const void *, void *);
+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 (void *aux UNUSED)
+static  void
+precalc (const struct ccase *first, void *aux UNUSED, const struct dataset *ds)
 {
+  output_split_file_values (ds, first);
   if (mode == GENERAL)
     {
       gen_tab = hsh_create (512, compare_table_entry, hash_table_entry,
@@ -554,16 +559,17 @@ precalc (void *aux UNUSED)
                               n_sorted_tab + 1, sizeof *sorted_tab);
       sorted_tab[n_sorted_tab] = NULL;
     }
+
 }
 
 /* Form crosstabulations for general mode. */
 static bool
-calc_general (struct ccase *c, void *aux UNUSED)
+calc_general (const struct ccase *c, void *aux UNUSED, const struct dataset *ds)
 {
-  int bad_warn = 1;
+  bool bad_warn = true;
 
   /* Case weight. */
-  double weight = dict_get_case_weight (default_dict, c, &bad_warn);
+  double weight = dict_get_case_weight (dataset_dict (ds), c, &bad_warn);
 
   /* Flattened current table index. */
   int t;
@@ -632,12 +638,12 @@ calc_general (struct ccase *c, void *aux UNUSED)
 }
 
 static bool
-calc_integer (struct ccase *c, void *aux UNUSED)
+calc_integer (const struct ccase *c, void *aux UNUSED, const struct dataset *ds)
 {
-  int bad_warn = 1;
+  bool bad_warn = true;
 
   /* Case weight. */
-  double weight = dict_get_case_weight (default_dict, c, &bad_warn);
+  double weight = dict_get_case_weight (dataset_dict (ds), c, &bad_warn);
   
   /* Flattened current table index. */
   int t;
@@ -692,7 +698,7 @@ calc_integer (struct ccase *c, void *aux UNUSED)
 /* Compare the table_entry's at A and B and return a strcmp()-type
    result. */
 static int 
-compare_table_entry (const void *a_, const void *b_, void *foo UNUSED)
+compare_table_entry (const void *a_, const void *b_, const void *aux UNUSED)
 {
   const struct table_entry *a = a_;
   const struct table_entry *b = b_;
@@ -732,7 +738,7 @@ compare_table_entry (const void *a_, const void *b_, void *foo UNUSED)
 
 /* Calculate a hash value from table_entry A. */
 static unsigned
-hash_table_entry (const void *a_, void *foo UNUSED)
+hash_table_entry (const void *a_, const void *aux UNUSED)
 {
   const struct table_entry *a = a_;
   unsigned long hash;
@@ -757,8 +763,8 @@ static void output_pivot_table (struct table_entry **, struct table_entry **,
                                int *, int *, int *);
 static void make_summary_table (void);
 
-static void
-postcalc (void *aux UNUSED)
+static bool
+postcalc (void *aux UNUSED, const struct dataset *ds UNUSED)
 {
   if (mode == GENERAL)
     {
@@ -794,6 +800,8 @@ postcalc (void *aux UNUSED)
   }
   
   hsh_destroy (gen_tab);
+
+  return true;
 }
 
 static void insert_summary (struct tab_table *, int tab_index, double valid);
@@ -945,7 +953,7 @@ static int n_rows;
 static int ns_cols, ns_rows;
 
 /* Crosstabulation. */
-static struct crosstab *x;
+static const struct crosstab *x;
 
 /* Number of variables from the crosstabulation to consider.  This is
    either x->nvar, if pivoting is on, or 2, if pivoting is off. */
@@ -1603,7 +1611,7 @@ find_pivot_extent_integer (struct table_entry **tp, int *cnt, int pivot)
    result.  WIDTH_ points to an int which is either 0 for a
    numeric value or a string width for a string value. */
 static int
-compare_value (const void *a_, const void *b_, void *width_)
+compare_value (const void *a_, const void *b_, const void *width_)
 {
   const union value *a = a_;
   const union value *b = b_;
@@ -1659,7 +1667,7 @@ static void
 table_value_missing (struct tab_table *table, int c, int r, unsigned char opt,
                     const union value *v, const struct variable *var)
 {
-  struct fixed_string s;
+  struct substring s;
 
   const char *label = val_labs_find (var->val_labs, *v);
   if (label) 
@@ -1698,20 +1706,20 @@ display_dimensions (struct tab_table *table, int first_difference, struct table_
 }
 
 /* Put VALUE into cell (C,R) of TABLE, suffixed with character
-   SUFFIX if nonzero.  If MARK_MISSING is nonzero the entry is
+   SUFFIX if nonzero.  If MARK_MISSING is true the entry is
    additionally suffixed with a letter `M'. */
 static void
 format_cell_entry (struct tab_table *table, int c, int r, double value,
-                   char suffix, int mark_missing)
+                   char suffix, bool mark_missing)
 {
   const struct fmt_spec f = {FMT_F, 10, 1};
   union value v;
-  struct fixed_string s;
+  struct substring s;
   
   s.length = 10;
   s.string = tab_alloc (table, 16);
   v.f = value;
-  data_out (s.string, &f, &v);
+  data_out (&v, &f, s.string);
   while (*s.string == ' ')
     {
       s.length--;
@@ -1751,13 +1759,13 @@ display_crosstabulation (void)
          tab_hline (table, TAL_1, -1, n_cols, 0);
        for (c = 0; c < n_cols; c++)
          {
-            int mark_missing = 0;
+            bool mark_missing = false;
             double expected_value = row_tot[r] * col_tot[c] / W;
             if (cmd.miss == CRS_REPORT
                 && (mv_is_num_user_missing (&x->vars[COL_VAR]->miss, cols[c].f)
                     || mv_is_num_user_missing (&x->vars[ROW_VAR]->miss,
                                                rows[r].f)))
-              mark_missing = 1;
+              mark_missing = true;
            for (i = 0; i < num_cells; i++)
              {
                double v;
@@ -1796,8 +1804,7 @@ display_crosstabulation (void)
                                 * (1. - col_tot[c] / W)));
                    break;
                  default:
-                   assert (0);
-                    abort ();
+                    NOT_REACHED ();
                  }
 
                 format_cell_entry (table, c, i, v, suffix, mark_missing);
@@ -1818,11 +1825,11 @@ display_crosstabulation (void)
     for (r = 0; r < n_rows; r++) 
       {
         char suffix = 0;
-        int mark_missing = 0;
+        bool mark_missing = false;
 
         if (cmd.miss == CRS_REPORT
             && mv_is_num_user_missing (&x->vars[ROW_VAR]->miss, rows[r].f))
-          mark_missing = 1;
+          mark_missing = true;
 
         for (i = 0; i < num_cells; i++)
           {
@@ -1852,8 +1859,7 @@ display_crosstabulation (void)
                 v = 0.;
                 break;
               default:
-                assert (0);
-                abort ();
+                NOT_REACHED ();
               }
 
             format_cell_entry (table, n_cols, 0, v, suffix, mark_missing);
@@ -1872,13 +1878,13 @@ display_crosstabulation (void)
     for (c = 0; c <= n_cols; c++)
       {
        double ct = c < n_cols ? col_tot[c] : W;
-        int mark_missing = 0;
+        bool mark_missing = false;
         char suffix = 0;
         int i;
            
         if (cmd.miss == CRS_REPORT && c < n_cols 
             && mv_is_num_user_missing (&x->vars[COL_VAR]->miss, cols[c].f))
-          mark_missing = 1;
+          mark_missing = true;
 
         for (i = 0; i < num_cells; i++)
          {
@@ -1908,8 +1914,7 @@ display_crosstabulation (void)
              case CRS_CL_ASRESIDUAL:
                continue;
              default:
-               assert (0);
-                abort ();
+                NOT_REACHED ();
              }
 
             format_cell_entry (table, c, i, v, suffix, mark_missing);
@@ -3182,7 +3187,7 @@ format_short (char *s, const struct fmt_spec *fp, const union value *v)
   struct fmt_spec fmt_subst;
 
   /* Limit to short string width. */
-  if (formats[fp->type].cat & FCAT_STRING
+  if (fmt_is_string (fp->type)
     {
       fmt_subst = *fp;
 
@@ -3196,8 +3201,8 @@ format_short (char *s, const struct fmt_spec *fp, const union value *v)
     }
 
   /* Format. */
-  data_out (s, fp, v);
-  
+  data_out (v, fp, s);
+    
   /* Null terminate. */
   s[fp->w] = '\0';
 }