We've had a mix of min, max from libpspp/misc.h and MIN, MAX from
[pspp-builds.git] / src / language / stats / aggregate.c
index 4b8bf46b365d8ae46025700c3361ea0852074055..648e8896b64bd2b6c6f8ca8ed9e7bfb49e12ff4b 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
    02110-1301, USA. */
 
 #include <config.h>
-#include <libpspp/message.h>
+
 #include <stdlib.h>
-#include <libpspp/alloc.h>
+
 #include <data/any-writer.h>
+#include <data/case-sink.h>
 #include <data/case.h>
 #include <data/casefile.h>
-#include <language/command.h>
 #include <data/dictionary.h>
-#include <libpspp/message.h>
 #include <data/file-handle-def.h>
+#include <data/procedure.h>
+#include <data/settings.h>
+#include <data/storage-stream.h>
+#include <data/sys-file-writer.h>
+#include <data/variable.h>
+#include <language/command.h>
 #include <language/data-io/file-handle.h>
 #include <language/lexer/lexer.h>
+#include <language/lexer/variable-parser.h>
 #include <language/stats/sort-criteria.h>
+#include <libpspp/alloc.h>
+#include <libpspp/assertion.h>
+#include <libpspp/message.h>
 #include <libpspp/misc.h>
-#include <math/moments.h>
 #include <libpspp/pool.h>
-#include <data/settings.h>
-#include <data/sys-file-writer.h>
-#include <math/sort.h>
 #include <libpspp/str.h>
-#include <data/variable.h>
-#include <procedure.h>
+#include <math/moments.h>
+#include <math/sort.h>
+
+#include "minmax.h"
 
 #include "gettext.h"
 #define _(msgid) gettext (msgid)
 
+/* Argument for AGGREGATE function. */
+union agr_argument
+  {
+    double f;                           /* Numeric. */
+    char *c;                            /* Short or long string. */
+  };
+
 /* Specifies how to make an aggregate variable. */
 struct agr_var
   {
@@ -54,7 +68,7 @@ struct agr_var
     struct variable *dest;     /* Target variable. */
     int function;              /* Function. */
     int include_missing;       /* 1=Include user-missing values. */
-    union value arg[2];                /* Arguments. */
+    union agr_argument arg[2]; /* Arguments. */
 
     /* Accumulated during AGGREGATE execution. */
     double dbl[3];
@@ -134,6 +148,7 @@ struct agr_proc
     enum missing_treatment missing;     /* How to treat missing values. */
     struct agr_var *agr_vars;           /* First aggregate variable. */
     struct dictionary *dict;            /* Aggregate dictionary. */
+    const struct dictionary *src_dict;  /* Dict of the source */
     int case_cnt;                       /* Counts aggregated cases. */
     struct ccase agr_case;              /* Aggregate case for output. */
   };
@@ -142,25 +157,27 @@ static void initialize_aggregate_info (struct agr_proc *,
                                        const struct ccase *);
 
 /* Prototypes. */
-static int parse_aggregate_functions (struct agr_proc *);
+static bool parse_aggregate_functions (struct lexer *, const struct dictionary *,
+                                      struct agr_proc *);
 static void agr_destroy (struct agr_proc *);
-static int aggregate_single_case (struct agr_proc *agr,
-                                  const struct ccase *input,
-                                  struct ccase *output);
+static bool aggregate_single_case (struct agr_proc *agr,
+                                  const struct ccase *input,
+                                  struct ccase *output);
 static void dump_aggregate_info (struct agr_proc *agr, struct ccase *output);
 
 /* Aggregating to the active file. */
-static bool agr_to_active_file (struct ccase *, void *aux);
+static bool agr_to_active_file (const struct ccase *, void *aux, const struct dataset *);
 
 /* Aggregating to a system file. */
-static bool presorted_agr_to_sysfile (struct ccase *, void *aux);
+static bool presorted_agr_to_sysfile (const struct ccase *, void *aux, const struct dataset *);
 \f
 /* Parsing. */
 
 /* Parses and executes the AGGREGATE procedure. */
 int
-cmd_aggregate (void)
+cmd_aggregate (struct lexer *lexer, struct dataset *ds)
 {
+  struct dictionary *dict = dataset_dict (ds);
   struct agr_proc agr;
   struct file_handle *out_file = NULL;
 
@@ -173,16 +190,17 @@ cmd_aggregate (void)
   case_nullify (&agr.break_case);
   
   agr.dict = dict_create ();
-  dict_set_label (agr.dict, dict_get_label (default_dict));
-  dict_set_documents (agr.dict, dict_get_documents (default_dict));
+  agr.src_dict = dict;
+  dict_set_label (agr.dict, dict_get_label (dict));
+  dict_set_documents (agr.dict, dict_get_documents (dict));
 
   /* OUTFILE subcommand must be first. */
-  if (!lex_force_match_id ("OUTFILE"))
+  if (!lex_force_match_id (lexer, "OUTFILE"))
     goto error;
-  lex_match ('=');
-  if (!lex_match ('*'))
+  lex_match (lexer, '=');
+  if (!lex_match (lexer, '*'))
     {
-      out_file = fh_parse (FH_REF_FILE | FH_REF_SCRATCH);
+      out_file = fh_parse (lexer, FH_REF_FILE | FH_REF_SCRATCH);
       if (out_file == NULL)
         goto error;
     }
@@ -190,28 +208,28 @@ cmd_aggregate (void)
   /* Read most of the subcommands. */
   for (;;)
     {
-      lex_match ('/');
+      lex_match (lexer, '/');
       
-      if (lex_match_id ("MISSING"))
+      if (lex_match_id (lexer, "MISSING"))
        {
-         lex_match ('=');
-         if (!lex_match_id ("COLUMNWISE"))
+         lex_match (lexer, '=');
+         if (!lex_match_id (lexer, "COLUMNWISE"))
            {
-             lex_error (_("while expecting COLUMNWISE"));
+             lex_error (lexer, _("while expecting COLUMNWISE"));
               goto error;
            }
          agr.missing = COLUMNWISE;
        }
-      else if (lex_match_id ("DOCUMENT"))
+      else if (lex_match_id (lexer, "DOCUMENT"))
         copy_documents = true;
-      else if (lex_match_id ("PRESORTED"))
+      else if (lex_match_id (lexer, "PRESORTED"))
         presorted = true;
-      else if (lex_match_id ("BREAK"))
+      else if (lex_match_id (lexer, "BREAK"))
        {
           int i;
 
-         lex_match ('=');
-          agr.sort = sort_parse_criteria (default_dict,
+         lex_match (lexer, '=');
+          agr.sort = sort_parse_criteria (lexer, dict,
                                           &agr.break_vars, &agr.break_var_cnt,
                                           &saw_direction, NULL);
           if (agr.sort == NULL)
@@ -226,7 +244,7 @@ cmd_aggregate (void)
        }
       else
         {
-          lex_error (_("expecting BREAK"));
+          lex_error (lexer, _("expecting BREAK"));
           goto error;
         }
     }
@@ -236,8 +254,8 @@ cmd_aggregate (void)
                "the same way as the input data."));
       
   /* Read in the aggregate functions. */
-  lex_match ('/');
-  if (!parse_aggregate_functions (&agr))
+  lex_match (lexer, '/');
+  if (!parse_aggregate_functions (lexer, dict, &agr))
     goto error;
 
   /* Delete documents. */
@@ -256,19 +274,21 @@ cmd_aggregate (void)
     {
       /* The active file will be replaced by the aggregated data,
          so TEMPORARY is moot. */
-      cancel_temporary ();
+      proc_cancel_temporary_transformations (ds);
 
       if (agr.sort != NULL && !presorted) 
         {
-          if (!sort_active_file_in_place (agr.sort))
+          if (!sort_active_file_in_place (ds, agr.sort))
             goto error;
         }
 
       agr.sink = create_case_sink (&storage_sink_class, agr.dict, NULL);
       if (agr.sink->class->open != NULL)
         agr.sink->class->open (agr.sink);
-      vfm_sink = create_case_sink (&null_sink_class, default_dict, NULL);
-      if (!procedure (agr_to_active_file, &agr))
+      proc_set_sink (ds, 
+                    create_case_sink (&null_sink_class, 
+                                      dict, NULL));
+      if (!procedure (ds, agr_to_active_file, &agr))
         goto error;
       if (agr.case_cnt > 0) 
         {
@@ -276,10 +296,12 @@ cmd_aggregate (void)
           if (!agr.sink->class->write (agr.sink, &agr.agr_case))
             goto error;
         }
-      dict_destroy (default_dict);
-      default_dict = agr.dict;
+      discard_variables (ds);
+      dict_destroy (dict);
+      dataset_set_dict (ds, agr.dict);
       agr.dict = NULL;
-      vfm_source = agr.sink->class->make_source (agr.sink);
+      proc_set_source (ds, 
+                      agr.sink->class->make_source (agr.sink));
       free_case_sink (agr.sink);
     }
   else
@@ -296,7 +318,7 @@ cmd_aggregate (void)
           struct ccase c;
           bool ok = true;
           
-          dst = sort_active_file_to_casefile (agr.sort);
+          dst = sort_active_file_to_casefile (ds, agr.sort);
           if (dst == NULL)
             goto error;
           reader = casefile_get_destructive_reader (dst);
@@ -316,7 +338,7 @@ cmd_aggregate (void)
       else 
         {
           /* Active file is already sorted. */
-          if (!procedure (presorted_agr_to_sysfile, &agr))
+          if (!procedure (ds, presorted_agr_to_sysfile, &agr))
             goto error;
         }
       
@@ -338,8 +360,8 @@ error:
 }
 
 /* Parse all the aggregate functions. */
-static int
-parse_aggregate_functions (struct agr_proc *agr)
+static bool
+parse_aggregate_functions (struct lexer *lexer, const struct dictionary *dict, struct agr_proc *agr)
 {
   struct agr_var *tail; /* Tail of linked list starting at agr->vars. */
 
@@ -350,12 +372,13 @@ parse_aggregate_functions (struct agr_proc *agr)
       char **dest;
       char **dest_label;
       size_t n_dest;
+      struct string function_name;
 
       int include_missing;
       const struct agr_func *function;
       int func_index;
 
-      union value arg[2];
+      union agr_argument arg[2];
 
       struct variable **src;
       size_t n_src;
@@ -372,11 +395,11 @@ parse_aggregate_functions (struct agr_proc *agr)
       arg[1].c = NULL;
 
       /* Parse the list of target variables. */
-      while (!lex_match ('='))
+      while (!lex_match (lexer, '='))
        {
          size_t n_dest_prev = n_dest;
          
-         if (!parse_DATA_LIST_vars (&dest, &n_dest,
+         if (!parse_DATA_LIST_vars (lexer, &dest, &n_dest,
                                      PV_APPEND | PV_SINGLE | PV_NO_SCRATCH))
            goto error;
 
@@ -388,42 +411,52 @@ parse_aggregate_functions (struct agr_proc *agr)
            for (j = n_dest_prev; j < n_dest; j++)
              dest_label[j] = NULL;
          }
+
+
          
-         if (token == T_STRING)
+         if (lex_token (lexer) == T_STRING)
            {
-             ds_truncate (&tokstr, 255);
-             dest_label[n_dest - 1] = xstrdup (ds_c_str (&tokstr));
-             lex_get ();
+             struct string label;
+             ds_init_string (&label, lex_tokstr (lexer));
+
+             ds_truncate (&label, 255);
+             dest_label[n_dest - 1] = ds_xstrdup (&label);
+             lex_get (lexer);
+             ds_destroy (&label);
            }
        }
 
       /* Get the name of the aggregation function. */
-      if (token != T_ID)
+      if (lex_token (lexer) != T_ID)
        {
-         lex_error (_("expecting aggregation function"));
+         lex_error (lexer, _("expecting aggregation function"));
          goto error;
        }
 
       include_missing = 0;
-      if (tokid[strlen (tokid) - 1] == '.')
-       {
+
+      ds_init_string (&function_name, lex_tokstr (lexer));
+
+      ds_chomp (&function_name, '.');
+
+      if (lex_tokid(lexer)[strlen (lex_tokid (lexer)) - 1] == '.')
          include_missing = 1;
-         tokid[strlen (tokid) - 1] = 0;
-       }
-      
+
       for (function = agr_func_tab; function->name; function++)
-       if (!strcasecmp (function->name, tokid))
+       if (!strcasecmp (function->name, ds_cstr (&function_name)))
          break;
       if (NULL == function->name)
        {
-         msg (SE, _("Unknown aggregation function %s."), tokid);
+         msg (SE, _("Unknown aggregation function %s."), 
+              ds_cstr (&function_name));
          goto error;
        }
+      ds_destroy (&function_name);
       func_index = function - agr_func_tab;
-      lex_get ();
+      lex_get (lexer);
 
       /* Check for leading lparen. */
-      if (!lex_match ('('))
+      if (!lex_match (lexer, '('))
        {
          if (func_index == N)
            func_index = N_NO_VARS;
@@ -431,7 +464,7 @@ parse_aggregate_functions (struct agr_proc *agr)
            func_index = NU_NO_VARS;
          else
            {
-             lex_error (_("expecting `('"));
+             lex_error (lexer, _("expecting `('"));
              goto error;
            }
        }
@@ -446,7 +479,7 @@ parse_aggregate_functions (struct agr_proc *agr)
            else if (function->n_args)
              pv_opts |= PV_SAME_TYPE;
 
-           if (!parse_variables (default_dict, &src, &n_src, pv_opts))
+           if (!parse_variables (lexer, dict, &src, &n_src, pv_opts))
              goto error;
          }
 
@@ -457,15 +490,15 @@ parse_aggregate_functions (struct agr_proc *agr)
              {
                int type;
            
-               lex_match (',');
-               if (token == T_STRING)
+               lex_match (lexer, ',');
+               if (lex_token (lexer) == T_STRING)
                  {
-                   arg[i].c = xstrdup (ds_c_str (&tokstr));
+                   arg[i].c = ds_xstrdup (lex_tokstr (lexer));
                    type = ALPHA;
                  }
-               else if (lex_is_number ())
+               else if (lex_is_number (lexer))
                  {
-                   arg[i].f = tokval;
+                   arg[i].f = lex_tokval (lexer);
                    type = NUMERIC;
                  } else {
                    msg (SE, _("Missing argument %d to %s."), i + 1,
@@ -473,7 +506,7 @@ parse_aggregate_functions (struct agr_proc *agr)
                    goto error;
                  }
            
-               lex_get ();
+               lex_get (lexer);
 
                if (type != src[0]->type)
                  {
@@ -485,9 +518,9 @@ parse_aggregate_functions (struct agr_proc *agr)
              }
 
          /* Trailing rparen. */
-         if (!lex_match(')'))
+         if (!lex_match (lexer, ')'))
            {
-             lex_error (_("expecting `)'"));
+             lex_error (lexer, _("expecting `)'"));
              goto error;
            }
          
@@ -511,7 +544,7 @@ parse_aggregate_functions (struct agr_proc *agr)
                   || (src[0]->type == ALPHA
                       && str_compare_rpad (arg[0].c, arg[1].c) > 0)))
             {
-              union value t = arg[0];
+              union agr_argument t = arg[0];
               arg[0] = arg[1];
               arg[1] = t;
                   
@@ -563,21 +596,24 @@ parse_aggregate_functions (struct agr_proc *agr)
                     destvar = dict_create_var (agr->dict, dest[i], 0);
                     if (destvar != NULL) 
                       {
+                        struct fmt_spec f;
                         if ((func_index == N || func_index == NMISS)
-                            && dict_get_weight (default_dict) != NULL)
-                          destvar->print = destvar->write = f8_2
+                            && dict_get_weight (dict) != NULL)
+                          f = fmt_for_output (FMT_F, 8, 2)
                         else
-                          destvar->print = destvar->write = function->format;
+                          f = function->format;
+                        destvar->print = destvar->write = f;
                       }
                   }
              } else {
+                struct fmt_spec f;
                v->src = NULL;
                destvar = dict_create_var (agr->dict, dest[i], 0);
-                if (func_index == N_NO_VARS
-                    && dict_get_weight (default_dict) != NULL)
-                  destvar->print = destvar->write = f8_2; 
+                if (func_index == N_NO_VARS && dict_get_weight (dict) != NULL)
+                  f = fmt_for_output (FMT_F, 8, 2); 
                 else
-                  destvar->print = destvar->write = function->format;
+                  f = function->format;
+                destvar->print = destvar->write = f;
              }
          
            if (!destvar)
@@ -591,7 +627,6 @@ parse_aggregate_functions (struct agr_proc *agr)
              }
 
            free (dest[i]);
-            destvar->init = 0;
            if (dest_label[i])
              {
                destvar->label = dest_label[i];
@@ -627,17 +662,18 @@ parse_aggregate_functions (struct agr_proc *agr)
       free (dest);
       free (dest_label);
 
-      if (!lex_match ('/'))
+      if (!lex_match (lexer, '/'))
        {
-         if (token == '.')
-           return 1;
+         if (lex_token (lexer) == '.')
+           return true;
 
-         lex_error ("expecting end of command");
-         return 0;
+         lex_error (lexer, "expecting end of command");
+         return false;
        }
       continue;
       
     error:
+      ds_destroy (&function_name);
       for (i = 0; i < n_dest; i++)
        {
          free (dest[i]);
@@ -655,7 +691,7 @@ parse_aggregate_functions (struct agr_proc *agr)
          }
       free (src);
        
-      return 0;
+      return false;
     }
 }
 
@@ -701,9 +737,9 @@ static void accumulate_aggregate_info (struct agr_proc *,
 static void dump_aggregate_info (struct agr_proc *, struct ccase *);
 
 /* Processes a single case INPUT for aggregation.  If output is
-   warranted, writes it to OUTPUT and returns nonzero.
-   Otherwise, returns zero and OUTPUT is unmodified. */
-static int
+   warranted, writes it to OUTPUT and returns true.
+   Otherwise, returns false and OUTPUT is unmodified. */
+static bool
 aggregate_single_case (struct agr_proc *agr,
                        const struct ccase *input, struct ccase *output)
 {
@@ -731,9 +767,9 @@ accumulate_aggregate_info (struct agr_proc *agr,
 {
   struct agr_var *iter;
   double weight;
-  int bad_warn = 1;
+  bool bad_warn = true;
 
-  weight = dict_get_case_weight (default_dict, input, &bad_warn);
+  weight = dict_get_case_weight (agr->src_dict, input, &bad_warn);
 
   for (iter = agr->agr_vars; iter; iter = iter->next)
     if (iter->src)
@@ -775,7 +811,7 @@ accumulate_aggregate_info (struct agr_proc *agr,
             moments1_add (iter->moments, v->f, weight);
             break;
          case MAX:
-           iter->dbl[0] = max (iter->dbl[0], v->f);
+           iter->dbl[0] = MAX (iter->dbl[0], v->f);
            iter->int1 = 1;
            break;
          case MAX | FSTRING:
@@ -784,7 +820,7 @@ accumulate_aggregate_info (struct agr_proc *agr,
            iter->int1 = 1;
            break;
          case MIN:
-           iter->dbl[0] = min (iter->dbl[0], v->f);
+           iter->dbl[0] = MIN (iter->dbl[0], v->f);
            iter->int1 = 1;
            break;
          case MIN | FSTRING:
@@ -880,7 +916,7 @@ accumulate_aggregate_info (struct agr_proc *agr,
                caught earlier.  Nothing to do. */
             break;
          default:
-           assert (0);
+           NOT_REACHED ();
          }
     } else {
       switch (iter->function)
@@ -892,7 +928,7 @@ accumulate_aggregate_info (struct agr_proc *agr,
          iter->int1++;
          break;
        default:
-         assert (0);
+         NOT_REACHED ();
        }
     }
 }
@@ -1021,7 +1057,7 @@ dump_aggregate_info (struct agr_proc *agr, struct ccase *output)
            v->f = i->int1;
            break;
          default:
-           assert (0);
+           NOT_REACHED ();
          }
       }
   }
@@ -1071,7 +1107,7 @@ initialize_aggregate_info (struct agr_proc *agr, const struct ccase *input)
    are dropped.
    Returns true if successful, false if an I/O error occurred. */
 static bool
-agr_to_active_file (struct ccase *c, void *agr_)
+agr_to_active_file (const struct ccase *c, void *agr_, const struct dataset *ds UNUSED)
 {
   struct agr_proc *agr = agr_;
 
@@ -1084,7 +1120,8 @@ agr_to_active_file (struct ccase *c, void *agr_)
 /* Aggregate the current case and output it if we passed a
    breakpoint. */
 static bool
-presorted_agr_to_sysfile (struct ccase *c, void *agr_) 
+presorted_agr_to_sysfile (const struct ccase *c, void *agr_, 
+                         const struct dataset *ds UNUSED) 
 {
   struct agr_proc *agr = agr_;