treewide: Replace <name>_cnt by n_<name>s and <name>_cap by allocated_<name>.
[pspp] / src / language / stats / aggregate.c
index 7430b819e90a2889aa4cb57be6575b180293335a..9815f1e25adb1062dacd2c0e1220087abf245762 100644 (file)
@@ -1,5 +1,5 @@
 /* PSPP - a program for statistical analysis.
-   Copyright (C) 1997-9, 2000, 2006, 2008, 2009, 2010 Free Software Foundation, Inc.
+   Copyright (C) 1997-9, 2000, 2006, 2008, 2009, 2010, 2011, 2012, 2014 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 <config.h>
 
+#include "language/stats/aggregate.h"
+
 #include <stdlib.h>
 
-#include <data/any-writer.h>
-#include <data/case.h>
-#include <data/casegrouper.h>
-#include <data/casereader.h>
-#include <data/casewriter.h>
-#include <data/dictionary.h>
-#include <data/file-handle-def.h>
-#include <data/format.h>
-#include <data/procedure.h>
-#include <data/settings.h>
-#include <data/subcase.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/assertion.h>
-#include <libpspp/message.h>
-#include <libpspp/misc.h>
-#include <libpspp/pool.h>
-#include <libpspp/str.h>
-#include <math/moments.h>
-#include <math/sort.h>
-#include <math/statistic.h>
-#include <math/percentiles.h>
-
-#include "aggregate.h"
-
-#include "minmax.h"
-#include "xalloc.h"
+#include "data/any-writer.h"
+#include "data/case.h"
+#include "data/casegrouper.h"
+#include "data/casereader.h"
+#include "data/casewriter.h"
+#include "data/dataset.h"
+#include "data/dictionary.h"
+#include "data/file-handle-def.h"
+#include "data/format.h"
+#include "data/settings.h"
+#include "data/subcase.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/assertion.h"
+#include "libpspp/i18n.h"
+#include "libpspp/message.h"
+#include "libpspp/misc.h"
+#include "libpspp/pool.h"
+#include "libpspp/str.h"
+#include "math/moments.h"
+#include "math/percentiles.h"
+#include "math/sort.h"
+#include "math/statistic.h"
+
+#include "gl/c-strcase.h"
+#include "gl/minmax.h"
+#include "gl/xalloc.h"
 
 #include "gettext.h"
 #define _(msgid) gettext (msgid)
@@ -87,41 +89,31 @@ struct agr_var
     struct casewriter *writer;
   };
 
-/* Aggregation functions. */
-enum
-  {
-    SUM, MEAN, MEDIAN, SD, MAX, MIN, PGT, PLT, PIN, POUT, FGT, FLT, FIN,
-    FOUT, N, NU, NMISS, NUMISS, FIRST, LAST,
-
-    FUNC = 0x1f, /* Function mask. */
-    FSTRING = 1<<5, /* String function bit. */
-  };
-
 
 /* Attributes of aggregation functions. */
 const struct agr_func agr_func_tab[] =
   {
-    {"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}},
+    {"SUM",     N_("Sum of values"),                         AGR_SV_YES, 0, -1,          { .type = FMT_F, .w = 8, .d = 2 }},
+    {"MEAN",   N_("Mean average"),                          AGR_SV_YES, 0, -1,          { .type = FMT_F, .w = 8, .d = 2 }},
+    {"MEDIAN", N_("Median average"),                        AGR_SV_YES, 0, -1,          { .type = FMT_F, .w = 8, .d = 2 }},
+    {"SD",      N_("Standard deviation"),                    AGR_SV_YES, 0, -1,          { .type = FMT_F, .w = 8, .d = 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, { .type = FMT_F, .w = 5, .d = 1 }},
+    {"PLT",     N_("Percentage less than"),                  AGR_SV_YES, 1, VAL_NUMERIC, { .type = FMT_F, .w = 5, .d = 1 }},
+    {"PIN",     N_("Percentage included in range"),          AGR_SV_YES, 2, VAL_NUMERIC, { .type = FMT_F, .w = 5, .d = 1 }},
+    {"POUT",    N_("Percentage excluded from range"),        AGR_SV_YES, 2, VAL_NUMERIC, { .type = FMT_F, .w = 5, .d = 1 }},
+    {"FGT",     N_("Fraction greater than"),                 AGR_SV_YES, 1, VAL_NUMERIC, { .type = FMT_F, .w = 5, .d = 3 }},
+    {"FLT",     N_("Fraction less than"),                    AGR_SV_YES, 1, VAL_NUMERIC, { .type = FMT_F, .w = 5, .d = 3 }},
+    {"FIN",     N_("Fraction included in range"),            AGR_SV_YES, 2, VAL_NUMERIC, { .type = FMT_F, .w = 5, .d = 3 }},
+    {"FOUT",    N_("Fraction excluded from range"),          AGR_SV_YES, 2, VAL_NUMERIC, { .type = FMT_F, .w = 5, .d = 3 }},
+    {"N",       N_("Number of cases"),                       AGR_SV_NO,  0, VAL_NUMERIC, { .type = FMT_F, .w = 7, .d = 0 }},
+    {"NU",      N_("Number of cases (unweighted)"),          AGR_SV_OPT, 0, VAL_NUMERIC, { .type = FMT_F, .w = 7, .d = 0 }},
+    {"NMISS",   N_("Number of missing values"),              AGR_SV_YES, 0, VAL_NUMERIC, { .type = FMT_F, .w = 7, .d = 0 }},
+    {"NUMISS",  N_("Number of missing values (unweighted)"), AGR_SV_YES, 0, VAL_NUMERIC, { .type = FMT_F, .w = 7, .d = 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. */
@@ -137,13 +129,13 @@ struct agr_proc
     /* Break variables. */
     struct subcase sort;                /* Sort criteria (break variables). */
     const struct variable **break_vars;       /* Break variables. */
-    size_t break_var_cnt;               /* Number of break variables. */
+    size_t break_n_vars;                /* Number 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. */
+    int n_cases;                        /* Counts aggregated cases. */
 
     bool add_variables;                 /* True iff the aggregated variables should
                                           be appended to the existing dictionary */
@@ -185,20 +177,20 @@ cmd_aggregate (struct lexer *lexer, struct dataset *ds)
   subcase_init_empty (&agr.sort);
 
   /* OUTFILE subcommand must be first. */
-  lex_match (lexer, '/');
+  lex_match (lexer, T_SLASH);
   if (!lex_force_match_id (lexer, "OUTFILE"))
     goto error;
-  lex_match (lexer, '=');
-  if (!lex_match (lexer, '*'))
+  lex_match (lexer, T_EQUALS);
+  if (!lex_match (lexer, T_ASTERISK))
     {
-      out_file = fh_parse (lexer, FH_REF_FILE | FH_REF_SCRATCH);
+      out_file = fh_parse (lexer, FH_REF_FILE, dataset_session (ds));
       if (out_file == NULL)
         goto error;
     }
 
   if (out_file == NULL && lex_match_id (lexer, "MODE"))
     {
-      lex_match (lexer, '=');
+      lex_match (lexer, T_EQUALS);
       if (lex_match_id (lexer, "ADDVARIABLES"))
        {
          agr.add_variables = true;
@@ -214,10 +206,10 @@ cmd_aggregate (struct lexer *lexer, struct dataset *ds)
        goto error;
     }
 
-  if ( agr.add_variables )
+  if (agr.add_variables)
     agr.dict = dict_clone (dict);
   else
-    agr.dict = dict_create ();    
+    agr.dict = dict_create (dict_get_encoding (dict));
 
   dict_set_label (agr.dict, dict_get_label (dict));
   dict_set_documents (agr.dict, dict_get_documents (dict));
@@ -225,14 +217,14 @@ cmd_aggregate (struct lexer *lexer, struct dataset *ds)
   /* Read most of the subcommands. */
   for (;;)
     {
-      lex_match (lexer, '/');
+      lex_match (lexer, T_SLASH);
 
       if (lex_match_id (lexer, "MISSING"))
        {
-         lex_match (lexer, '=');
+         lex_match (lexer, T_EQUALS);
          if (!lex_match_id (lexer, "COLUMNWISE"))
            {
-             lex_error (lexer, _("while expecting COLUMNWISE"));
+             lex_error_expecting (lexer, "COLUMNWISE");
               goto error;
            }
          agr.missing = COLUMNWISE;
@@ -241,28 +233,26 @@ 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;
 
-         lex_match (lexer, '=');
+         lex_match (lexer, T_EQUALS);
           if (!parse_sort_criteria (lexer, dict, &agr.sort, &agr.break_vars,
                                     &saw_direction))
             goto error;
-          agr.break_var_cnt = subcase_get_n_fields (&agr.sort);
+          agr.break_n_vars = subcase_get_n_fields (&agr.sort);
 
          if  (! agr.add_variables)
-           for (i = 0; i < agr.break_var_cnt; i++)
+           for (i = 0; i < agr.break_n_vars; 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 "
@@ -270,7 +260,7 @@ cmd_aggregate (struct lexer *lexer, struct dataset *ds)
                "the same way as the input data."));
 
   /* Read in the aggregate functions. */
-  lex_match (lexer, '/');
+  lex_match (lexer, T_SLASH);
   if (!parse_aggregate_functions (lexer, dict, &agr))
     goto error;
 
@@ -282,11 +272,11 @@ cmd_aggregate (struct lexer *lexer, struct dataset *ds)
   dict_set_split_vars (agr.dict, NULL, 0);
 
   /* Initialize. */
-  agr.case_cnt = 0;
+  agr.n_cases = 0;
 
   if (out_file == NULL)
     {
-      /* The active file will be replaced by the aggregated data,
+      /* The active dataset will be replaced by the aggregated data,
          so TEMPORARY is moot. */
       proc_cancel_temporary_transformations (ds);
       proc_discard_output (ds);
@@ -307,7 +297,7 @@ cmd_aggregate (struct lexer *lexer, struct dataset *ds)
     }
 
   for (grouper = casegrouper_create_vars (input, agr.break_vars,
-                                          agr.break_var_cnt);
+                                          agr.break_n_vars);
        casegrouper_get_next_group (grouper, &group);
        casereader_destroy (group))
     {
@@ -322,7 +312,7 @@ cmd_aggregate (struct lexer *lexer, struct dataset *ds)
 
       initialize_aggregate_info (&agr);
 
-      if ( agr.add_variables )
+      if (agr.add_variables)
        placeholder = casereader_clone (group);
 
       {
@@ -343,8 +333,8 @@ cmd_aggregate (struct lexer *lexer, struct dataset *ds)
       else
        {
          dump_aggregate_info (&agr, output, c);
-         case_unref (c);
        }
+      case_unref (c);
     }
   if (!casegrouper_destroy (grouper))
     goto error;
@@ -362,7 +352,8 @@ cmd_aggregate (struct lexer *lexer, struct dataset *ds)
       if (next_input == NULL)
         goto error;
 
-      proc_set_active_file (ds, next_input, agr.dict);
+      dataset_set_dict (ds, agr.dict);
+      dataset_set_source (ds, next_input);
       agr.dict = NULL;
     }
   else
@@ -424,11 +415,11 @@ parse_aggregate_functions (struct lexer *lexer, const struct dictionary *dict,
       ds_init_empty (&function_name);
 
       /* Parse the list of target variables. */
-      while (!lex_match (lexer, '='))
+      while (!lex_match (lexer, T_EQUALS))
        {
          size_t n_dest_prev = n_dest;
 
-         if (!parse_DATA_LIST_vars (lexer, &dest, &n_dest,
+         if (!parse_DATA_LIST_vars (lexer, dict, &dest, &n_dest,
                                      (PV_APPEND | PV_SINGLE | PV_NO_SCRATCH
                                       | PV_NO_DUPLICATE)))
            goto error;
@@ -444,15 +435,10 @@ 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));
-
-             ds_truncate (&label, 255);
-             dest_label[n_dest - 1] = ds_xstrdup (&label);
+             dest_label[n_dest - 1] = xstrdup (lex_tokcstr (lexer));
              lex_get (lexer);
-             ds_destroy (&label);
            }
        }
 
@@ -463,17 +449,11 @@ 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;
+      ds_assign_substring (&function_name, lex_tokss (lexer));
+      exclude = ds_chomp_byte (&function_name, '.') ? MV_SYSTEM : MV_ANY;
 
       for (function = agr_func_tab; function->name; function++)
-       if (!strcasecmp (function->name, ds_cstr (&function_name)))
+       if (!c_strcasecmp (function->name, ds_cstr (&function_name)))
          break;
       if (NULL == function->name)
        {
@@ -486,11 +466,10 @@ parse_aggregate_functions (struct lexer *lexer, const struct dictionary *dict,
       lex_get (lexer);
 
       /* Check for leading lparen. */
-      if (!lex_match (lexer, '('))
+      if (!lex_match (lexer, T_LPAREN))
        {
          if (function->src_vars == AGR_SV_YES)
            {
-             lex_error (lexer, _("expecting `('"));
              goto error;
            }
        }
@@ -516,10 +495,12 @@ parse_aggregate_functions (struct lexer *lexer, const struct dictionary *dict,
              {
                int type;
 
-               lex_match (lexer, ',');
-               if (lex_token (lexer) == T_STRING)
+               lex_match (lexer, T_COMMA);
+               if (lex_is_string (lexer))
                  {
-                   arg[i].c = ds_xstrdup (lex_tokstr (lexer));
+                   arg[i].c = recode_string (dict_get_encoding (agr->dict),
+                                              "UTF-8", lex_tokcstr (lexer),
+                                              -1);
                    type = VAL_STRING;
                  }
                else if (lex_is_number (lexer))
@@ -546,11 +527,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, T_RPAREN))
+            goto error;
 
          /* Now check that the number of source variables match
             the number of target variables.  If we check earlier
@@ -587,7 +565,7 @@ parse_aggregate_functions (struct lexer *lexer, const struct dictionary *dict,
          variables. */
       for (i = 0; i < n_dest; i++)
        {
-         struct agr_var *v = xzalloc (sizeof *v);
+         struct agr_var *v = XZALLOC (struct agr_var);
 
          /* Add variable to chain. */
          if (agr->agr_vars != NULL)
@@ -637,12 +615,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 && 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)
              {
@@ -687,9 +669,9 @@ parse_aggregate_functions (struct lexer *lexer, const struct dictionary *dict,
       free (dest);
       free (dest_label);
 
-      if (!lex_match (lexer, '/'))
+      if (!lex_match (lexer, T_SLASH))
        {
-         if (lex_token (lexer) == '.')
+         if (lex_token (lexer) == T_ENDCMD)
            return true;
 
          lex_error (lexer, "expecting end of command");
@@ -751,7 +733,7 @@ agr_destroy (struct agr_proc *agr)
       free (iter);
     }
   if (agr->dict != NULL)
-    dict_destroy (agr->dict);
+    dict_unref (agr->dict);
 }
 \f
 /* Execution. */
@@ -807,12 +789,11 @@ accumulate_aggregate_info (struct agr_proc *agr, const struct ccase *input)
 
               cout = case_create (casewriter_get_proto (iter->writer));
 
-             case_data_rw (cout, iter->subject)->f
-                = case_data (input, iter->src)->f;
+             *case_num_rw (cout, iter->subject) = case_num (input, iter->src);
 
              wv = dict_get_case_weight (agr->src_dict, input, NULL);
 
-             case_data_rw (cout, iter->weight)->f = wv;
+             *case_num_rw (cout, iter->weight) = wv;
 
              iter->cc += wv;
 
@@ -827,8 +808,9 @@ accumulate_aggregate_info (struct agr_proc *agr, const struct ccase *input)
            iter->int1 = 1;
            break;
          case MAX | FSTRING:
-           if (memcmp (iter->string, value_str (v, src_width), src_width) < 0)
-             memcpy (iter->string, value_str (v, src_width), src_width);
+            /* Need to do some kind of Unicode collation thingy here */
+           if (memcmp (iter->string, v->s, src_width) < 0)
+             memcpy (iter->string, v->s, src_width);
            iter->int1 = 1;
            break;
          case MIN:
@@ -836,8 +818,8 @@ accumulate_aggregate_info (struct agr_proc *agr, const struct ccase *input)
            iter->int1 = 1;
            break;
          case MIN | FSTRING:
-           if (memcmp (iter->string, value_str (v, src_width), src_width) > 0)
-             memcpy (iter->string, value_str (v, src_width), src_width);
+           if (memcmp (iter->string, v->s, src_width) > 0)
+             memcpy (iter->string, v->s, src_width);
            iter->int1 = 1;
            break;
          case FGT:
@@ -848,8 +830,7 @@ accumulate_aggregate_info (struct agr_proc *agr, const struct ccase *input)
             break;
          case FGT | FSTRING:
          case PGT | FSTRING:
-            if (memcmp (iter->arg[0].c,
-                        value_str (v, src_width), src_width) < 0)
+            if (memcmp (iter->arg[0].c, v->s, src_width) < 0)
               iter->dbl[0] += weight;
             iter->dbl[1] += weight;
             break;
@@ -861,8 +842,7 @@ accumulate_aggregate_info (struct agr_proc *agr, const struct ccase *input)
             break;
          case FLT | FSTRING:
          case PLT | FSTRING:
-            if (memcmp (iter->arg[0].c,
-                        value_str (v, src_width), src_width) > 0)
+            if (memcmp (iter->arg[0].c, v->s, src_width) > 0)
               iter->dbl[0] += weight;
             iter->dbl[1] += weight;
             break;
@@ -874,10 +854,8 @@ accumulate_aggregate_info (struct agr_proc *agr, const struct ccase *input)
             break;
          case FIN | FSTRING:
          case PIN | FSTRING:
-            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)
+            if (memcmp (iter->arg[0].c, v->s, src_width) <= 0
+                && memcmp (iter->arg[1].c, v->s, src_width) >= 0)
               iter->dbl[0] += weight;
             iter->dbl[1] += weight;
             break;
@@ -889,10 +867,8 @@ accumulate_aggregate_info (struct agr_proc *agr, const struct ccase *input)
             break;
          case FOUT | FSTRING:
          case POUT | FSTRING:
-            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)
+            if (memcmp (iter->arg[0].c, v->s, src_width) > 0
+                || memcmp (iter->arg[1].c, v->s, src_width) < 0)
               iter->dbl[0] += weight;
             iter->dbl[1] += weight;
             break;
@@ -914,7 +890,7 @@ accumulate_aggregate_info (struct agr_proc *agr, const struct ccase *input)
          case FIRST | FSTRING:
            if (iter->int1 == 0)
              {
-               memcpy (iter->string, value_str (v, src_width), src_width);
+               memcpy (iter->string, v->s, src_width);
                iter->int1 = 1;
              }
            break;
@@ -923,7 +899,7 @@ accumulate_aggregate_info (struct agr_proc *agr, const struct ccase *input)
            iter->int1 = 1;
            break;
          case LAST | FSTRING:
-           memcpy (iter->string, value_str (v, src_width), src_width);
+           memcpy (iter->string, v->s, src_width);
            iter->int1 = 1;
            break;
           case NMISS:
@@ -957,16 +933,16 @@ dump_aggregate_info (const struct agr_proc *agr, struct casewriter *output, cons
 {
   struct ccase *c = case_create (dict_get_proto (agr->dict));
 
-  if ( agr->add_variables)
+  if (agr->add_variables)
     {
-      case_copy (c, 0, break_case, 0, dict_get_var_cnt (agr->src_dict));
+      case_copy (c, 0, break_case, 0, dict_get_n_vars (agr->src_dict));
     }
   else
     {
       int value_idx = 0;
       int i;
 
-      for (i = 0; i < agr->break_var_cnt; i++)
+      for (i = 0; i < agr->break_n_vars; i++)
        {
          const struct variable *v = agr->break_vars[i];
          value_copy (case_data_rw_idx (c, value_idx),
@@ -1003,21 +979,22 @@ dump_aggregate_info (const struct agr_proc *agr, struct casewriter *output, cons
            break;
          case MEDIAN:
            {
-             struct casereader *sorted_reader;
-             struct percentile *median = percentile_create (0.5, i->cc);
-              struct order_stats *os = &median->parent;
-
-             sorted_reader = casewriter_make_reader (i->writer);
-
-             order_stats_accumulate (&os, 1,
-                                     sorted_reader,
-                                     i->weight,
-                                     i->subject,
-                                     i->exclude);
-
-             v->f = percentile_calculate (median, PC_HAVERAGE);
-
-             statistic_destroy (&median->parent.parent);
+             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:
@@ -1040,7 +1017,7 @@ dump_aggregate_info (const struct agr_proc *agr, struct casewriter *output, cons
          case MAX | FSTRING:
          case MIN | FSTRING:
            if (i->int1)
-             memcpy (value_str_rw (v, width), i->string, width);
+             memcpy (v->s, i->string, width);
            else
               value_set_missing (v, width);
            break;
@@ -1079,7 +1056,7 @@ dump_aggregate_info (const struct agr_proc *agr, struct casewriter *output, cons
          case FIRST | FSTRING:
          case LAST | FSTRING:
            if (i->int1)
-             memcpy (value_str_rw (v, width), i->string, width);
+             memcpy (v->s, i->string, width);
            else
               value_set_missing (v, width);
            break;
@@ -1134,10 +1111,10 @@ initialize_aggregate_info (struct agr_proc *agr)
             proto = caseproto_add_width (proto, 0);
             proto = caseproto_add_width (proto, 0);
 
-           if ( ! iter->subject)
+           if (! iter->subject)
              iter->subject = dict_create_internal_var (0, 0);
 
-           if ( ! iter->weight)
+           if (! iter->weight)
              iter->weight = dict_create_internal_var (1, 0);
 
             subcase_init_var (&ordering, iter->subject, SC_ASCEND);