lexer: Use lex_is_string() more consistently.
[pspp] / src / language / stats / aggregate.c
index 02257f9434f29555bcb2538f5b321a961d099788..a8e3f6d71d267545b1d1d85ef60490433149c0f8 100644 (file)
@@ -1,5 +1,5 @@
 /* PSPP - a program for statistical analysis.
-   Copyright (C) 1997-9, 2000, 2006, 2008 Free Software Foundation, Inc.
+   Copyright (C) 1997-9, 2000, 2006, 2008, 2009, 2010 Free Software Foundation, Inc.
 
    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
 #include <math/statistic.h>
 #include <math/percentiles.h>
 
+#include "aggregate.h"
+
 #include "minmax.h"
 #include "xalloc.h"
 
 #include "gettext.h"
 #define _(msgid) gettext (msgid)
+#define N_(msgid) msgid
 
 /* Argument for AGGREGATE function. */
 union agr_argument
@@ -84,52 +87,31 @@ struct agr_var
     struct casewriter *writer;
   };
 
-/* Aggregation functions. */
-enum
-  {
-    NONE, SUM, MEAN, MEDIAN, SD, MAX, MIN, PGT, PLT, PIN, POUT, FGT, FLT, FIN,
-    FOUT, N, NU, NMISS, NUMISS, FIRST, LAST,
-    N_AGR_FUNCS, N_NO_VARS, NU_NO_VARS,
-    FUNC = 0x1f, /* Function mask. */
-    FSTRING = 1<<5, /* String function bit. */
-  };
-
-/* Attributes of an aggregation function. */
-struct agr_func
-  {
-    const char *name;          /* Aggregation function name. */
-    size_t n_args;              /* Number of arguments. */
-    enum val_type alpha_type;   /* When given ALPHA arguments, output type. */
-    struct fmt_spec format;    /* Format spec if alpha_type != ALPHA. */
-  };
 
 /* Attributes of aggregation functions. */
-static const struct agr_func agr_func_tab[] =
+const struct agr_func agr_func_tab[] =
   {
-    {"<NONE>",  0, -1,          {0, 0, 0}},
-    {"SUM",     0, -1,          {FMT_F, 8, 2}},
-    {"MEAN",   0, -1,          {FMT_F, 8, 2}},
-    {"MEDIAN", 0, -1,          {FMT_F, 8, 2}},
-    {"SD",      0, -1,          {FMT_F, 8, 2}},
-    {"MAX",     0, VAL_STRING,  {-1, -1, -1}},
-    {"MIN",     0, VAL_STRING,  {-1, -1, -1}},
-    {"PGT",     1, VAL_NUMERIC, {FMT_F, 5, 1}},
-    {"PLT",     1, VAL_NUMERIC, {FMT_F, 5, 1}},
-    {"PIN",     2, VAL_NUMERIC, {FMT_F, 5, 1}},
-    {"POUT",    2, VAL_NUMERIC, {FMT_F, 5, 1}},
-    {"FGT",     1, VAL_NUMERIC, {FMT_F, 5, 3}},
-    {"FLT",     1, VAL_NUMERIC, {FMT_F, 5, 3}},
-    {"FIN",     2, VAL_NUMERIC, {FMT_F, 5, 3}},
-    {"FOUT",    2, VAL_NUMERIC, {FMT_F, 5, 3}},
-    {"N",       0, VAL_NUMERIC, {FMT_F, 7, 0}},
-    {"NU",      0, VAL_NUMERIC, {FMT_F, 7, 0}},
-    {"NMISS",   0, VAL_NUMERIC, {FMT_F, 7, 0}},
-    {"NUMISS",  0, VAL_NUMERIC, {FMT_F, 7, 0}},
-    {"FIRST",   0, VAL_STRING,  {-1, -1, -1}},
-    {"LAST",    0, VAL_STRING,  {-1, -1, -1}},
-    {NULL,      0, -1,          {-1, -1, -1}},
-    {"N",       0, VAL_NUMERIC, {FMT_F, 7, 0}},
-    {"NU",      0, VAL_NUMERIC, {FMT_F, 7, 0}},
+    {"SUM",     N_("Sum of values"), AGR_SV_YES, 0, -1,          {FMT_F, 8, 2}},
+    {"MEAN",   N_("Mean average"), AGR_SV_YES, 0, -1,          {FMT_F, 8, 2}},
+    {"MEDIAN", N_("Median average"), AGR_SV_YES, 0, -1,          {FMT_F, 8, 2}},
+    {"SD",      N_("Standard deviation"), AGR_SV_YES, 0, -1,          {FMT_F, 8, 2}},
+    {"MAX",     N_("Maximum value"), AGR_SV_YES, 0, VAL_STRING,  {-1, -1, -1}},
+    {"MIN",     N_("Minimum value"), AGR_SV_YES, 0, VAL_STRING,  {-1, -1, -1}},
+    {"PGT",     N_("Percentage greater than"), AGR_SV_YES, 1, VAL_NUMERIC, {FMT_F, 5, 1}},
+    {"PLT",     N_("Percentage less than"), AGR_SV_YES, 1, VAL_NUMERIC, {FMT_F, 5, 1}},
+    {"PIN",     N_("Percentage included in range"), AGR_SV_YES, 2, VAL_NUMERIC, {FMT_F, 5, 1}},
+    {"POUT",    N_("Percentage excluded from range"), AGR_SV_YES, 2, VAL_NUMERIC, {FMT_F, 5, 1}},
+    {"FGT",     N_("Fraction greater than"), AGR_SV_YES, 1, VAL_NUMERIC, {FMT_F, 5, 3}},
+    {"FLT",     N_("Fraction less than"), AGR_SV_YES, 1, VAL_NUMERIC, {FMT_F, 5, 3}},
+    {"FIN",     N_("Fraction included in range"), AGR_SV_YES, 2, VAL_NUMERIC, {FMT_F, 5, 3}},
+    {"FOUT",    N_("Fraction excluded from range"), AGR_SV_YES, 2, VAL_NUMERIC, {FMT_F, 5, 3}},
+    {"N",       N_("Number of cases"), AGR_SV_NO, 0, VAL_NUMERIC, {FMT_F, 7, 0}},
+    {"NU",      N_("Number of cases (unweighted)"), AGR_SV_OPT, 0, VAL_NUMERIC, {FMT_F, 7, 0}},
+    {"NMISS",   N_("Number of missing values"), AGR_SV_YES, 0, VAL_NUMERIC, {FMT_F, 7, 0}},
+    {"NUMISS",  N_("Number of missing values (unweighted)"), AGR_SV_YES, 0, VAL_NUMERIC, {FMT_F, 7, 0}},
+    {"FIRST",   N_("First non-missing value"), AGR_SV_YES, 0, VAL_STRING,  {-1, -1, -1}},
+    {"LAST",    N_("Last non-missing value"), AGR_SV_YES, 0, VAL_STRING,  {-1, -1, -1}},
+    {NULL,      NULL, AGR_SV_NO, 0, -1,          {-1, -1, -1}},
   };
 
 /* Missing value types. */
@@ -146,17 +128,18 @@ struct agr_proc
     struct subcase sort;                /* Sort criteria (break variables). */
     const struct variable **break_vars;       /* Break variables. */
     size_t break_var_cnt;               /* Number of break variables. */
-    struct ccase break_case;            /* Last values of break variables. */
 
     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. */
+
+    bool add_variables;                 /* True iff the aggregated variables should
+                                          be appended to the existing dictionary */
   };
 
-static void initialize_aggregate_info (struct agr_proc *,
-                                       const struct ccase *);
+static void initialize_aggregate_info (struct agr_proc *);
 
 static void accumulate_aggregate_info (struct agr_proc *,
                                        const struct ccase *);
@@ -164,8 +147,9 @@ static void accumulate_aggregate_info (struct agr_proc *,
 static bool parse_aggregate_functions (struct lexer *, const struct dictionary *,
                                       struct agr_proc *);
 static void agr_destroy (struct agr_proc *);
-static void dump_aggregate_info (struct agr_proc *agr,
-                                 struct casewriter *output);
+static void dump_aggregate_info (const struct agr_proc *agr,
+                                 struct casewriter *output,
+                                const struct ccase *break_case);
 \f
 /* Parsing. */
 
@@ -187,15 +171,11 @@ cmd_aggregate (struct lexer *lexer, struct dataset *ds)
 
   memset(&agr, 0 , sizeof (agr));
   agr.missing = ITEMWISE;
-  case_nullify (&agr.break_case);
-
-  agr.dict = dict_create ();
   agr.src_dict = dict;
   subcase_init_empty (&agr.sort);
-  dict_set_label (agr.dict, dict_get_label (dict));
-  dict_set_documents (agr.dict, dict_get_documents (dict));
 
   /* OUTFILE subcommand must be first. */
+  lex_match (lexer, '/');
   if (!lex_force_match_id (lexer, "OUTFILE"))
     goto error;
   lex_match (lexer, '=');
@@ -206,6 +186,32 @@ cmd_aggregate (struct lexer *lexer, struct dataset *ds)
         goto error;
     }
 
+  if (out_file == NULL && lex_match_id (lexer, "MODE"))
+    {
+      lex_match (lexer, '=');
+      if (lex_match_id (lexer, "ADDVARIABLES"))
+       {
+         agr.add_variables = true;
+
+         /* presorted is assumed in ADDVARIABLES mode */
+         presorted = true;
+       }
+      else if (lex_match_id (lexer, "REPLACE"))
+       {
+         agr.add_variables = false;
+       }
+      else
+       goto error;
+    }
+
+  if ( agr.add_variables )
+    agr.dict = dict_clone (dict);
+  else
+    agr.dict = dict_create ();    
+
+  dict_set_label (agr.dict, dict_get_label (dict));
+  dict_set_documents (agr.dict, dict_get_documents (dict));
+
   /* Read most of the subcommands. */
   for (;;)
     {
@@ -216,7 +222,7 @@ cmd_aggregate (struct lexer *lexer, struct dataset *ds)
          lex_match (lexer, '=');
          if (!lex_match_id (lexer, "COLUMNWISE"))
            {
-             lex_error (lexer, _("while expecting COLUMNWISE"));
+             lex_error (lexer, _("expecting %s"), "COLUMNWISE");
               goto error;
            }
          agr.missing = COLUMNWISE;
@@ -225,7 +231,7 @@ cmd_aggregate (struct lexer *lexer, struct dataset *ds)
         copy_documents = true;
       else if (lex_match_id (lexer, "PRESORTED"))
         presorted = true;
-      else if (lex_match_id (lexer, "BREAK"))
+      else if (lex_force_match_id (lexer, "BREAK"))
        {
           int i;
 
@@ -235,18 +241,16 @@ cmd_aggregate (struct lexer *lexer, struct dataset *ds)
             goto error;
           agr.break_var_cnt = subcase_get_n_fields (&agr.sort);
 
-          for (i = 0; i < agr.break_var_cnt; i++)
-            dict_clone_var_assert (agr.dict, agr.break_vars[i],
-                                   var_get_name (agr.break_vars[i]));
+         if  (! agr.add_variables)
+           for (i = 0; i < agr.break_var_cnt; i++)
+             dict_clone_var_assert (agr.dict, agr.break_vars[i]);
 
           /* BREAK must follow the options. */
           break;
        }
       else
-        {
-          lex_error (lexer, _("expecting BREAK"));
-          goto error;
-        }
+        goto error;
+
     }
   if (presorted && saw_direction)
     msg (SW, _("When PRESORTED is specified, specifying sorting directions "
@@ -274,7 +278,7 @@ cmd_aggregate (struct lexer *lexer, struct dataset *ds)
          so TEMPORARY is moot. */
       proc_cancel_temporary_transformations (ds);
       proc_discard_output (ds);
-      output = autopaging_writer_create (dict_get_next_value_idx (agr.dict));
+      output = autopaging_writer_create (dict_get_proto (agr.dict));
     }
   else
     {
@@ -295,19 +299,40 @@ cmd_aggregate (struct lexer *lexer, struct dataset *ds)
        casegrouper_get_next_group (grouper, &group);
        casereader_destroy (group))
     {
-      struct ccase c;
+      struct casereader *placeholder = NULL;
+      struct ccase *c = casereader_peek (group, 0);
 
-      if (!casereader_peek (group, 0, &c))
+      if (c == NULL)
         {
           casereader_destroy (group);
           continue;
         }
-      initialize_aggregate_info (&agr, &c);
-      case_destroy (&c);
 
-      for (; casereader_read (group, &c); case_destroy (&c))
-        accumulate_aggregate_info (&agr, &c);
-      dump_aggregate_info (&agr, output);
+      initialize_aggregate_info (&agr);
+
+      if ( agr.add_variables )
+       placeholder = casereader_clone (group);
+
+      {
+       struct ccase *cg;
+       for (; (cg = casereader_read (group)) != NULL; case_unref (cg))
+         accumulate_aggregate_info (&agr, cg);
+      }
+
+
+      if  (agr.add_variables)
+       {
+         struct ccase *cg;
+         for (; (cg = casereader_read (placeholder)) != NULL; case_unref (cg))
+           dump_aggregate_info (&agr, output, cg);
+
+         casereader_destroy (placeholder);
+       }
+      else
+       {
+         dump_aggregate_info (&agr, output, c);
+         case_unref (c);
+       }
     }
   if (!casegrouper_destroy (grouper))
     goto error;
@@ -392,7 +417,8 @@ parse_aggregate_functions (struct lexer *lexer, const struct dictionary *dict,
          size_t n_dest_prev = n_dest;
 
          if (!parse_DATA_LIST_vars (lexer, &dest, &n_dest,
-                                     PV_APPEND | PV_SINGLE | PV_NO_SCRATCH))
+                                     (PV_APPEND | PV_SINGLE | PV_NO_SCRATCH
+                                      | PV_NO_DUPLICATE)))
            goto error;
 
          /* Assign empty labels. */
@@ -406,7 +432,7 @@ parse_aggregate_functions (struct lexer *lexer, const struct dictionary *dict,
 
 
 
-         if (lex_token (lexer) == T_STRING)
+         if (lex_is_string (lexer))
            {
              struct string label;
              ds_init_string (&label, lex_tokstr (lexer));
@@ -425,14 +451,8 @@ parse_aggregate_functions (struct lexer *lexer, const struct dictionary *dict,
          goto error;
        }
 
-      exclude = MV_ANY;
-
       ds_assign_string (&function_name, lex_tokstr (lexer));
-
-      ds_chomp (&function_name, '.');
-
-      if (lex_tokid(lexer)[strlen (lex_tokid (lexer)) - 1] == '.')
-        exclude = MV_SYSTEM;
+      exclude = ds_chomp (&function_name, '.') ? MV_SYSTEM : MV_ANY;
 
       for (function = agr_func_tab; function->name; function++)
        if (!strcasecmp (function->name, ds_cstr (&function_name)))
@@ -450,13 +470,9 @@ parse_aggregate_functions (struct lexer *lexer, const struct dictionary *dict,
       /* Check for leading lparen. */
       if (!lex_match (lexer, '('))
        {
-         if (func_index == N)
-           func_index = N_NO_VARS;
-         else if (func_index == NU)
-           func_index = NU_NO_VARS;
-         else
+         if (function->src_vars == AGR_SV_YES)
            {
-             lex_error (lexer, _("expecting `('"));
+              lex_force_match (lexer, '(');
              goto error;
            }
        }
@@ -483,7 +499,7 @@ parse_aggregate_functions (struct lexer *lexer, const struct dictionary *dict,
                int type;
 
                lex_match (lexer, ',');
-               if (lex_token (lexer) == T_STRING)
+               if (lex_is_string (lexer))
                  {
                    arg[i].c = ds_xstrdup (lex_tokstr (lexer));
                    type = VAL_STRING;
@@ -512,11 +528,8 @@ parse_aggregate_functions (struct lexer *lexer, const struct dictionary *dict,
              }
 
          /* Trailing rparen. */
-         if (!lex_match (lexer, ')'))
-           {
-             lex_error (lexer, _("expecting `)'"));
-             goto error;
-           }
+         if (!lex_force_match (lexer, ')'))
+            goto error;
 
          /* Now check that the number of source variables match
             the number of target variables.  If we check earlier
@@ -582,7 +595,7 @@ parse_aggregate_functions (struct lexer *lexer, const struct dictionary *dict,
                  }
 
                if (function->alpha_type == VAL_STRING)
-                 destvar = dict_clone_var (agr->dict, v->src, dest[i]);
+                 destvar = dict_clone_var_as (agr->dict, v->src, dest[i]);
                else
                   {
                     assert (var_is_numeric (v->src)
@@ -603,12 +616,16 @@ parse_aggregate_functions (struct lexer *lexer, const struct dictionary *dict,
                 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 (dict) != NULL)
-                  f = fmt_for_output (FMT_F, 8, 2);
-                else
-                  f = function->format;
-                var_set_both_formats (destvar, &f);
-             }
+               if (destvar != NULL)
+                 {
+                   if ((func_index == N || func_index == NMISS)
+                       && dict_get_weight (dict) != NULL)
+                     f = fmt_for_output (FMT_F, 8, 2);
+                   else
+                     f = function->format;
+                   var_set_both_formats (destvar, &f);
+                 }
+           }
 
            if (!destvar)
              {
@@ -694,7 +711,6 @@ agr_destroy (struct agr_proc *agr)
 
   subcase_destroy (&agr->sort);
   free (agr->break_vars);
-  case_destroy (&agr->break_case);
   for (iter = agr->agr_vars; iter; iter = next)
     {
       next = iter->next;
@@ -712,8 +728,8 @@ agr_destroy (struct agr_proc *agr)
       else if (iter->function == SD)
         moments1_destroy (iter->moments);
 
-      var_destroy (iter->subject);
-      var_destroy (iter->weight);
+      dict_destroy_internal_var (iter->subject);
+      dict_destroy_internal_var (iter->weight);
 
       free (iter);
     }
@@ -770,20 +786,20 @@ accumulate_aggregate_info (struct agr_proc *agr, const struct ccase *input)
          case MEDIAN:
            {
              double wv ;
-             struct ccase cout;
-             case_create (&cout, 2);
+             struct ccase *cout;
+
+              cout = case_create (casewriter_get_proto (iter->writer));
 
-             case_data_rw (&cout, iter->subject)->f =
-               case_data (input, iter->src)->f;
+             case_data_rw (cout, iter->subject)->f
+                = case_data (input, iter->src)->f;
 
              wv = dict_get_case_weight (agr->src_dict, input, NULL);
 
-             case_data_rw (&cout, iter->weight)->f = wv;
+             case_data_rw (cout, iter->weight)->f = wv;
 
              iter->cc += wv;
 
-             casewriter_write (iter->writer, &cout);
-             case_destroy (&cout);
+             casewriter_write (iter->writer, cout);
            }
            break;
          case SD:
@@ -794,8 +810,8 @@ accumulate_aggregate_info (struct agr_proc *agr, const struct ccase *input)
            iter->int1 = 1;
            break;
          case MAX | FSTRING:
-           if (memcmp (iter->string, v->s, src_width) < 0)
-             memcpy (iter->string, v->s, src_width);
+           if (memcmp (iter->string, value_str (v, src_width), src_width) < 0)
+             memcpy (iter->string, value_str (v, src_width), src_width);
            iter->int1 = 1;
            break;
          case MIN:
@@ -803,8 +819,8 @@ accumulate_aggregate_info (struct agr_proc *agr, const struct ccase *input)
            iter->int1 = 1;
            break;
          case MIN | FSTRING:
-           if (memcmp (iter->string, v->s, src_width) > 0)
-             memcpy (iter->string, v->s, src_width);
+           if (memcmp (iter->string, value_str (v, src_width), src_width) > 0)
+             memcpy (iter->string, value_str (v, src_width), src_width);
            iter->int1 = 1;
            break;
          case FGT:
@@ -815,7 +831,8 @@ accumulate_aggregate_info (struct agr_proc *agr, const struct ccase *input)
             break;
          case FGT | FSTRING:
          case PGT | FSTRING:
-            if (memcmp (iter->arg[0].c, v->s, src_width) < 0)
+            if (memcmp (iter->arg[0].c,
+                        value_str (v, src_width), src_width) < 0)
               iter->dbl[0] += weight;
             iter->dbl[1] += weight;
             break;
@@ -827,7 +844,8 @@ accumulate_aggregate_info (struct agr_proc *agr, const struct ccase *input)
             break;
          case FLT | FSTRING:
          case PLT | FSTRING:
-            if (memcmp (iter->arg[0].c, v->s, src_width) > 0)
+            if (memcmp (iter->arg[0].c,
+                        value_str (v, src_width), src_width) > 0)
               iter->dbl[0] += weight;
             iter->dbl[1] += weight;
             break;
@@ -839,8 +857,10 @@ accumulate_aggregate_info (struct agr_proc *agr, const struct ccase *input)
             break;
          case FIN | FSTRING:
          case PIN | FSTRING:
-            if (memcmp (iter->arg[0].c, v->s, src_width) <= 0
-                && memcmp (iter->arg[1].c, v->s, src_width) >= 0)
+            if (memcmp (iter->arg[0].c,
+                        value_str (v, src_width), src_width) <= 0
+                && memcmp (iter->arg[1].c,
+                           value_str (v, src_width), src_width) >= 0)
               iter->dbl[0] += weight;
             iter->dbl[1] += weight;
             break;
@@ -852,8 +872,10 @@ accumulate_aggregate_info (struct agr_proc *agr, const struct ccase *input)
             break;
          case FOUT | FSTRING:
          case POUT | FSTRING:
-            if (memcmp (iter->arg[0].c, v->s, src_width) > 0
-                || memcmp (iter->arg[1].c, v->s, src_width) < 0)
+            if (memcmp (iter->arg[0].c,
+                        value_str (v, src_width), src_width) > 0
+                || memcmp (iter->arg[1].c,
+                           value_str (v, src_width), src_width) < 0)
               iter->dbl[0] += weight;
             iter->dbl[1] += weight;
             break;
@@ -875,7 +897,7 @@ accumulate_aggregate_info (struct agr_proc *agr, const struct ccase *input)
          case FIRST | FSTRING:
            if (iter->int1 == 0)
              {
-               memcpy (iter->string, v->s, src_width);
+               memcpy (iter->string, value_str (v, src_width), src_width);
                iter->int1 = 1;
              }
            break;
@@ -884,7 +906,7 @@ accumulate_aggregate_info (struct agr_proc *agr, const struct ccase *input)
            iter->int1 = 1;
            break;
          case LAST | FSTRING:
-           memcpy (iter->string, v->s, src_width);
+           memcpy (iter->string, value_str (v, src_width), src_width);
            iter->int1 = 1;
            break;
           case NMISS:
@@ -897,13 +919,13 @@ accumulate_aggregate_info (struct agr_proc *agr, const struct ccase *input)
          default:
            NOT_REACHED ();
          }
-    } else {
+      } else {
       switch (iter->function)
        {
-       case N_NO_VARS:
+       case N:
          iter->dbl[0] += weight;
          break;
-       case NU_NO_VARS:
+       case NU:
          iter->int1++;
          break;
        default:
@@ -914,46 +936,43 @@ accumulate_aggregate_info (struct agr_proc *agr, const struct ccase *input)
 
 /* Writes an aggregated record to OUTPUT. */
 static void
-dump_aggregate_info (struct agr_proc *agr, struct casewriter *output)
+dump_aggregate_info (const struct agr_proc *agr, struct casewriter *output, const struct ccase *break_case)
 {
-  struct ccase c;
+  struct ccase *c = case_create (dict_get_proto (agr->dict));
 
-  case_create (&c, dict_get_next_value_idx (agr->dict));
-
-  {
-    int value_idx = 0;
-    int i;
+  if ( agr->add_variables)
+    {
+      case_copy (c, 0, break_case, 0, dict_get_var_cnt (agr->src_dict));
+    }
+  else
+    {
+      int value_idx = 0;
+      int i;
 
-    for (i = 0; i < agr->break_var_cnt; i++)
-      {
-        const struct variable *v = agr->break_vars[i];
-        size_t value_cnt = var_get_value_cnt (v);
-        memcpy (case_data_rw_idx (&c, value_idx),
-                case_data (&agr->break_case, v),
-                sizeof (union value) * value_cnt);
-        value_idx += value_cnt;
-      }
-  }
+      for (i = 0; i < agr->break_var_cnt; i++)
+       {
+         const struct variable *v = agr->break_vars[i];
+         value_copy (case_data_rw_idx (c, value_idx),
+                     case_data (break_case, v),
+                     var_get_width (v));
+         value_idx++;
+       }
+    }
 
   {
     struct agr_var *i;
 
     for (i = agr->agr_vars; i; i = i->next)
       {
-       union value *v = case_data_rw (&c, i->dest);
-
+       union value *v = case_data_rw (c, i->dest);
+        int width = var_get_width (i->dest);
 
        if (agr->missing == COLUMNWISE && i->saw_missing
            && (i->function & FUNC) != N && (i->function & FUNC) != NU
            && (i->function & FUNC) != NMISS && (i->function & FUNC) != NUMISS)
          {
-           if (var_is_alpha (i->dest))
-             memset (v->s, ' ', var_get_width (i->dest));
-           else
-             v->f = SYSMIS;
-
+            value_set_missing (v, width);
            casewriter_destroy (i->writer);
-
            continue;
          }
 
@@ -967,21 +986,22 @@ dump_aggregate_info (struct agr_proc *agr, struct casewriter *output)
            break;
          case MEDIAN:
            {
-             struct casereader *sorted_reader;
-             struct order_stats *median = percentile_create (0.5, i->cc);
-
-             sorted_reader = casewriter_make_reader (i->writer);
-
-             order_stats_accumulate (&median, 1,
-                                     sorted_reader,
-                                     i->weight,
-                                     i->subject,
-                                     i->exclude);
-
-             v->f = percentile_calculate ((struct percentile *) median,
-                                          PC_HAVERAGE);
-
-             statistic_destroy ((struct statistic *) median);
+             if ( i->writer)
+               {
+                 struct percentile *median = percentile_create (0.5, i->cc);
+                 struct order_stats *os = &median->parent;
+                 struct casereader *sorted_reader = casewriter_make_reader (i->writer);
+                 i->writer = NULL;
+
+                 order_stats_accumulate (&os, 1,
+                                         sorted_reader,
+                                         i->weight,
+                                         i->subject,
+                                         i->exclude);
+                 i->dbl[0] = percentile_calculate (median, PC_HAVERAGE);
+                 statistic_destroy (&median->parent.parent);
+               }
+             v->f = i->dbl[0];
            }
            break;
          case SD:
@@ -1004,9 +1024,9 @@ dump_aggregate_info (struct agr_proc *agr, struct casewriter *output)
          case MAX | FSTRING:
          case MIN | FSTRING:
            if (i->int1)
-             memcpy (v->s, i->string, var_get_width (i->dest));
+             memcpy (value_str_rw (v, width), i->string, width);
            else
-             memset (v->s, ' ', var_get_width (i->dest));
+              value_set_missing (v, width);
            break;
          case FGT:
          case FGT | FSTRING:
@@ -1030,7 +1050,7 @@ dump_aggregate_info (struct agr_proc *agr, struct casewriter *output)
            break;
          case N:
          case N | FSTRING:
-           v->f = i->dbl[0];
+             v->f = i->dbl[0];
             break;
          case NU:
          case NU | FSTRING:
@@ -1043,15 +1063,9 @@ dump_aggregate_info (struct agr_proc *agr, struct casewriter *output)
          case FIRST | FSTRING:
          case LAST | FSTRING:
            if (i->int1)
-             memcpy (v->s, i->string, var_get_width (i->dest));
+             memcpy (value_str_rw (v, width), i->string, width);
            else
-             memset (v->s, ' ', var_get_width (i->dest));
-           break;
-         case N_NO_VARS:
-           v->f = i->dbl[0];
-           break;
-         case NU_NO_VARS:
-           v->f = i->int1;
+              value_set_missing (v, width);
            break;
          case NMISS:
          case NMISS | FSTRING:
@@ -1067,18 +1081,15 @@ dump_aggregate_info (struct agr_proc *agr, struct casewriter *output)
       }
   }
 
-  casewriter_write (output, &c);
+  casewriter_write (output, c);
 }
 
 /* Resets the state for all the aggregate functions. */
 static void
-initialize_aggregate_info (struct agr_proc *agr, const struct ccase *input)
+initialize_aggregate_info (struct agr_proc *agr)
 {
   struct agr_var *iter;
 
-  case_destroy (&agr->break_case);
-  case_clone (&agr->break_case, input);
-
   for (iter = agr->agr_vars; iter; iter = iter->next)
     {
       iter->saw_missing = false;
@@ -1100,17 +1111,23 @@ initialize_aggregate_info (struct agr_proc *agr, const struct ccase *input)
          break;
        case MEDIAN:
          {
+            struct caseproto *proto;
             struct subcase ordering;
 
+            proto = caseproto_create ();
+            proto = caseproto_add_width (proto, 0);
+            proto = caseproto_add_width (proto, 0);
+
            if ( ! iter->subject)
-             iter->subject = var_create_internal (0);
+             iter->subject = dict_create_internal_var (0, 0);
 
            if ( ! iter->weight)
-             iter->weight = var_create_internal (1);
+             iter->weight = dict_create_internal_var (1, 0);
 
             subcase_init_var (&ordering, iter->subject, SC_ASCEND);
-           iter->writer = sort_create_writer (&ordering, 2);
+           iter->writer = sort_create_writer (&ordering, proto);
             subcase_destroy (&ordering);
+            caseproto_unref (proto);
 
            iter->cc = 0;
          }