4e3101121ad0c9e889fd719df7abd54eb84dcdef
[pspp-builds.git] / src / language / stats / aggregate.c
1 /* PSPP - computes sample statistics.
2    Copyright (C) 1997-9, 2000, 2006 Free Software Foundation, Inc.
3
4    This program is free software; you can redistribute it and/or
5    modify it under the terms of the GNU General Public License as
6    published by the Free Software Foundation; either version 2 of the
7    License, or (at your option) any later version.
8
9    This program is distributed in the hope that it will be useful, but
10    WITHOUT ANY WARRANTY; without even the implied warranty of
11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12    General Public License for more details.
13
14    You should have received a copy of the GNU General Public License
15    along with this program; if not, write to the Free Software
16    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
17    02110-1301, USA. */
18
19 #include <config.h>
20
21 #include <stdlib.h>
22
23 #include <data/any-writer.h>
24 #include <data/case-ordering.h>
25 #include <data/case.h>
26 #include <data/casegrouper.h>
27 #include <data/casereader.h>
28 #include <data/casewriter.h>
29 #include <data/dictionary.h>
30 #include <data/file-handle-def.h>
31 #include <data/format.h>
32 #include <data/procedure.h>
33 #include <data/settings.h>
34 #include <data/sys-file-writer.h>
35 #include <data/variable.h>
36 #include <language/command.h>
37 #include <language/data-io/file-handle.h>
38 #include <language/lexer/lexer.h>
39 #include <language/lexer/variable-parser.h>
40 #include <language/stats/sort-criteria.h>
41 #include <libpspp/alloc.h>
42 #include <libpspp/assertion.h>
43 #include <libpspp/message.h>
44 #include <libpspp/misc.h>
45 #include <libpspp/pool.h>
46 #include <libpspp/str.h>
47 #include <math/moments.h>
48 #include <math/sort.h>
49
50 #include "minmax.h"
51
52 #include "gettext.h"
53 #define _(msgid) gettext (msgid)
54
55 /* Argument for AGGREGATE function. */
56 union agr_argument
57   {
58     double f;                           /* Numeric. */
59     char *c;                            /* Short or long string. */
60   };
61
62 /* Specifies how to make an aggregate variable. */
63 struct agr_var
64   {
65     struct agr_var *next;               /* Next in list. */
66
67     /* Collected during parsing. */
68     const struct variable *src; /* Source variable. */
69     struct variable *dest;      /* Target variable. */
70     int function;               /* Function. */
71     enum mv_class exclude;      /* Classes of missing values to exclude. */
72     union agr_argument arg[2];  /* Arguments. */
73
74     /* Accumulated during AGGREGATE execution. */
75     double dbl[3];
76     int int1, int2;
77     char *string;
78     bool saw_missing;
79     struct moments1 *moments;
80   };
81
82 /* Aggregation functions. */
83 enum
84   {
85     NONE, SUM, MEAN, SD, MAX, MIN, PGT, PLT, PIN, POUT, FGT, FLT, FIN,
86     FOUT, N, NU, NMISS, NUMISS, FIRST, LAST,
87     N_AGR_FUNCS, N_NO_VARS, NU_NO_VARS,
88     FUNC = 0x1f, /* Function mask. */
89     FSTRING = 1<<5, /* String function bit. */
90   };
91
92 /* Attributes of an aggregation function. */
93 struct agr_func
94   {
95     const char *name;           /* Aggregation function name. */
96     size_t n_args;              /* Number of arguments. */
97     enum var_type alpha_type;   /* When given ALPHA arguments, output type. */
98     struct fmt_spec format;     /* Format spec if alpha_type != ALPHA. */
99   };
100
101 /* Attributes of aggregation functions. */
102 static const struct agr_func agr_func_tab[] =
103   {
104     {"<NONE>",  0, -1,          {0, 0, 0}},
105     {"SUM",     0, -1,          {FMT_F, 8, 2}},
106     {"MEAN",    0, -1,          {FMT_F, 8, 2}},
107     {"SD",      0, -1,          {FMT_F, 8, 2}},
108     {"MAX",     0, VAR_STRING,  {-1, -1, -1}},
109     {"MIN",     0, VAR_STRING,  {-1, -1, -1}},
110     {"PGT",     1, VAR_NUMERIC, {FMT_F, 5, 1}},
111     {"PLT",     1, VAR_NUMERIC, {FMT_F, 5, 1}},
112     {"PIN",     2, VAR_NUMERIC, {FMT_F, 5, 1}},
113     {"POUT",    2, VAR_NUMERIC, {FMT_F, 5, 1}},
114     {"FGT",     1, VAR_NUMERIC, {FMT_F, 5, 3}},
115     {"FLT",     1, VAR_NUMERIC, {FMT_F, 5, 3}},
116     {"FIN",     2, VAR_NUMERIC, {FMT_F, 5, 3}},
117     {"FOUT",    2, VAR_NUMERIC, {FMT_F, 5, 3}},
118     {"N",       0, VAR_NUMERIC, {FMT_F, 7, 0}},
119     {"NU",      0, VAR_NUMERIC, {FMT_F, 7, 0}},
120     {"NMISS",   0, VAR_NUMERIC, {FMT_F, 7, 0}},
121     {"NUMISS",  0, VAR_NUMERIC, {FMT_F, 7, 0}},
122     {"FIRST",   0, VAR_STRING,  {-1, -1, -1}},
123     {"LAST",    0, VAR_STRING,  {-1, -1, -1}},
124     {NULL,      0, -1,          {-1, -1, -1}},
125     {"N",       0, VAR_NUMERIC, {FMT_F, 7, 0}},
126     {"NU",      0, VAR_NUMERIC, {FMT_F, 7, 0}},
127   };
128
129 /* Missing value types. */
130 enum missing_treatment
131   {
132     ITEMWISE,           /* Missing values item by item. */
133     COLUMNWISE          /* Missing values column by column. */
134   };
135
136 /* An entire AGGREGATE procedure. */
137 struct agr_proc
138   {
139     /* Break variables. */
140     struct case_ordering *sort;         /* Sort criteria. */
141     const struct variable **break_vars;       /* Break variables. */
142     size_t break_var_cnt;               /* Number of break variables. */
143     struct ccase break_case;            /* Last values of break variables. */
144
145     enum missing_treatment missing;     /* How to treat missing values. */
146     struct agr_var *agr_vars;           /* First aggregate variable. */
147     struct dictionary *dict;            /* Aggregate dictionary. */
148     const struct dictionary *src_dict;  /* Dict of the source */
149     int case_cnt;                       /* Counts aggregated cases. */
150   };
151
152 static void initialize_aggregate_info (struct agr_proc *,
153                                        const struct ccase *);
154 static void accumulate_aggregate_info (struct agr_proc *,
155                                        const struct ccase *);
156 /* Prototypes. */
157 static bool parse_aggregate_functions (struct lexer *, const struct dictionary *,
158                                        struct agr_proc *);
159 static void agr_destroy (struct agr_proc *);
160 static void dump_aggregate_info (struct agr_proc *agr,
161                                  struct casewriter *output);
162 \f
163 /* Parsing. */
164
165 /* Parses and executes the AGGREGATE procedure. */
166 int
167 cmd_aggregate (struct lexer *lexer, struct dataset *ds)
168 {
169   struct dictionary *dict = dataset_dict (ds);
170   struct agr_proc agr;
171   struct file_handle *out_file = NULL;
172   struct casereader *input = NULL, *group;
173   struct casegrouper *grouper;
174   struct casewriter *output = NULL;
175
176   bool copy_documents = false;
177   bool presorted = false;
178   bool saw_direction;
179   bool ok;
180
181   memset(&agr, 0 , sizeof (agr));
182   agr.missing = ITEMWISE;
183   case_nullify (&agr.break_case);
184
185   agr.dict = dict_create ();
186   agr.src_dict = dict;
187   dict_set_label (agr.dict, dict_get_label (dict));
188   dict_set_documents (agr.dict, dict_get_documents (dict));
189
190   /* OUTFILE subcommand must be first. */
191   if (!lex_force_match_id (lexer, "OUTFILE"))
192     goto error;
193   lex_match (lexer, '=');
194   if (!lex_match (lexer, '*'))
195     {
196       out_file = fh_parse (lexer, FH_REF_FILE | FH_REF_SCRATCH);
197       if (out_file == NULL)
198         goto error;
199     }
200
201   /* Read most of the subcommands. */
202   for (;;)
203     {
204       lex_match (lexer, '/');
205
206       if (lex_match_id (lexer, "MISSING"))
207         {
208           lex_match (lexer, '=');
209           if (!lex_match_id (lexer, "COLUMNWISE"))
210             {
211               lex_error (lexer, _("while expecting COLUMNWISE"));
212               goto error;
213             }
214           agr.missing = COLUMNWISE;
215         }
216       else if (lex_match_id (lexer, "DOCUMENT"))
217         copy_documents = true;
218       else if (lex_match_id (lexer, "PRESORTED"))
219         presorted = true;
220       else if (lex_match_id (lexer, "BREAK"))
221         {
222           int i;
223
224           lex_match (lexer, '=');
225           agr.sort = parse_case_ordering (lexer, dict,
226
227                                           &saw_direction);
228           if (agr.sort == NULL)
229             goto error;
230           case_ordering_get_vars (agr.sort,
231                                   &agr.break_vars, &agr.break_var_cnt);
232
233           for (i = 0; i < agr.break_var_cnt; i++)
234             dict_clone_var_assert (agr.dict, agr.break_vars[i],
235                                    var_get_name (agr.break_vars[i]));
236
237           /* BREAK must follow the options. */
238           break;
239         }
240       else
241         {
242           lex_error (lexer, _("expecting BREAK"));
243           goto error;
244         }
245     }
246   if (presorted && saw_direction)
247     msg (SW, _("When PRESORTED is specified, specifying sorting directions "
248                "with (A) or (D) has no effect.  Output data will be sorted "
249                "the same way as the input data."));
250
251   /* Read in the aggregate functions. */
252   lex_match (lexer, '/');
253   if (!parse_aggregate_functions (lexer, dict, &agr))
254     goto error;
255
256   /* Delete documents. */
257   if (!copy_documents)
258     dict_clear_documents (agr.dict);
259
260   /* Cancel SPLIT FILE. */
261   dict_set_split_vars (agr.dict, NULL, 0);
262
263   /* Initialize. */
264   agr.case_cnt = 0;
265
266   if (out_file == NULL)
267     {
268       /* The active file will be replaced by the aggregated data,
269          so TEMPORARY is moot. */
270       proc_cancel_temporary_transformations (ds);
271       proc_discard_output (ds);
272       output = autopaging_writer_create (dict_get_next_value_idx (agr.dict));
273     }
274   else
275     {
276       output = any_writer_open (out_file, agr.dict);
277       if (output == NULL)
278         goto error;
279     }
280
281   input = proc_open (ds);
282   if (agr.sort != NULL && !presorted)
283     {
284       input = sort_execute (input, agr.sort);
285       agr.sort = NULL;
286     }
287
288   for (grouper = casegrouper_create_vars (input, agr.break_vars,
289                                           agr.break_var_cnt);
290        casegrouper_get_next_group (grouper, &group);
291        casereader_destroy (group))
292     {
293       struct ccase c;
294
295       if (!casereader_peek (group, 0, &c))
296         continue;
297       initialize_aggregate_info (&agr, &c);
298       case_destroy (&c);
299
300       for (; casereader_read (group, &c); case_destroy (&c))
301         accumulate_aggregate_info (&agr, &c);
302       dump_aggregate_info (&agr, output);
303     }
304   if (!casegrouper_destroy (grouper))
305     goto error;
306
307   if (!proc_commit (ds))
308     {
309       input = NULL;
310       goto error;
311     }
312   input = NULL;
313
314   if (out_file == NULL)
315     {
316       struct casereader *next_input = casewriter_make_reader (output);
317       if (next_input == NULL)
318         goto error;
319
320       proc_set_active_file (ds, next_input, agr.dict);
321       agr.dict = NULL;
322     }
323   else
324     {
325       ok = casewriter_destroy (output);
326       output = NULL;
327       if (!ok)
328         goto error;
329     }
330
331   agr_destroy (&agr);
332   return CMD_SUCCESS;
333
334 error:
335   if (input != NULL)
336     proc_commit (ds);
337   casewriter_destroy (output);
338   agr_destroy (&agr);
339   return CMD_CASCADING_FAILURE;
340 }
341
342 /* Parse all the aggregate functions. */
343 static bool
344 parse_aggregate_functions (struct lexer *lexer, const struct dictionary *dict, struct agr_proc *agr)
345 {
346   struct agr_var *tail; /* Tail of linked list starting at agr->vars. */
347
348   /* Parse everything. */
349   tail = NULL;
350   for (;;)
351     {
352       char **dest;
353       char **dest_label;
354       size_t n_dest;
355       struct string function_name;
356
357       enum mv_class exclude;
358       const struct agr_func *function;
359       int func_index;
360
361       union agr_argument arg[2];
362
363       const struct variable **src;
364       size_t n_src;
365
366       size_t i;
367
368       dest = NULL;
369       dest_label = NULL;
370       n_dest = 0;
371       src = NULL;
372       function = NULL;
373       n_src = 0;
374       arg[0].c = NULL;
375       arg[1].c = NULL;
376       ds_init_empty (&function_name);
377
378       /* Parse the list of target variables. */
379       while (!lex_match (lexer, '='))
380         {
381           size_t n_dest_prev = n_dest;
382
383           if (!parse_DATA_LIST_vars (lexer, &dest, &n_dest,
384                                      PV_APPEND | PV_SINGLE | PV_NO_SCRATCH))
385             goto error;
386
387           /* Assign empty labels. */
388           {
389             int j;
390
391             dest_label = xnrealloc (dest_label, n_dest, sizeof *dest_label);
392             for (j = n_dest_prev; j < n_dest; j++)
393               dest_label[j] = NULL;
394           }
395
396
397
398           if (lex_token (lexer) == T_STRING)
399             {
400               struct string label;
401               ds_init_string (&label, lex_tokstr (lexer));
402
403               ds_truncate (&label, 255);
404               dest_label[n_dest - 1] = ds_xstrdup (&label);
405               lex_get (lexer);
406               ds_destroy (&label);
407             }
408         }
409
410       /* Get the name of the aggregation function. */
411       if (lex_token (lexer) != T_ID)
412         {
413           lex_error (lexer, _("expecting aggregation function"));
414           goto error;
415         }
416
417       exclude = MV_ANY;
418
419       ds_assign_string (&function_name, lex_tokstr (lexer));
420
421       ds_chomp (&function_name, '.');
422
423       if (lex_tokid(lexer)[strlen (lex_tokid (lexer)) - 1] == '.')
424         exclude = MV_SYSTEM;
425
426       for (function = agr_func_tab; function->name; function++)
427         if (!strcasecmp (function->name, ds_cstr (&function_name)))
428           break;
429       if (NULL == function->name)
430         {
431           msg (SE, _("Unknown aggregation function %s."),
432                ds_cstr (&function_name));
433           goto error;
434         }
435       ds_destroy (&function_name);
436       func_index = function - agr_func_tab;
437       lex_get (lexer);
438
439       /* Check for leading lparen. */
440       if (!lex_match (lexer, '('))
441         {
442           if (func_index == N)
443             func_index = N_NO_VARS;
444           else if (func_index == NU)
445             func_index = NU_NO_VARS;
446           else
447             {
448               lex_error (lexer, _("expecting `('"));
449               goto error;
450             }
451         }
452       else
453         {
454           /* Parse list of source variables. */
455           {
456             int pv_opts = PV_NO_SCRATCH;
457
458             if (func_index == SUM || func_index == MEAN || func_index == SD)
459               pv_opts |= PV_NUMERIC;
460             else if (function->n_args)
461               pv_opts |= PV_SAME_TYPE;
462
463             if (!parse_variables_const (lexer, dict, &src, &n_src, pv_opts))
464               goto error;
465           }
466
467           /* Parse function arguments, for those functions that
468              require arguments. */
469           if (function->n_args != 0)
470             for (i = 0; i < function->n_args; i++)
471               {
472                 int type;
473
474                 lex_match (lexer, ',');
475                 if (lex_token (lexer) == T_STRING)
476                   {
477                     arg[i].c = ds_xstrdup (lex_tokstr (lexer));
478                     type = VAR_STRING;
479                   }
480                 else if (lex_is_number (lexer))
481                   {
482                     arg[i].f = lex_tokval (lexer);
483                     type = VAR_NUMERIC;
484                   }
485                 else
486                   {
487                     msg (SE, _("Missing argument %d to %s."),
488                          (int) i + 1, function->name);
489                     goto error;
490                   }
491
492                 lex_get (lexer);
493
494                 if (type != var_get_type (src[0]))
495                   {
496                     msg (SE, _("Arguments to %s must be of same type as "
497                                "source variables."),
498                          function->name);
499                     goto error;
500                   }
501               }
502
503           /* Trailing rparen. */
504           if (!lex_match (lexer, ')'))
505             {
506               lex_error (lexer, _("expecting `)'"));
507               goto error;
508             }
509
510           /* Now check that the number of source variables match
511              the number of target variables.  If we check earlier
512              than this, the user can get very misleading error
513              message, i.e. `AGGREGATE x=SUM(y t).' will get this
514              error message when a proper message would be more
515              like `unknown variable t'. */
516           if (n_src != n_dest)
517             {
518               msg (SE, _("Number of source variables (%u) does not match "
519                          "number of target variables (%u)."),
520                    (unsigned) n_src, (unsigned) n_dest);
521               goto error;
522             }
523
524           if ((func_index == PIN || func_index == POUT
525               || func_index == FIN || func_index == FOUT)
526               && (var_is_numeric (src[0])
527                   ? arg[0].f > arg[1].f
528                   : str_compare_rpad (arg[0].c, arg[1].c) > 0))
529             {
530               union agr_argument t = arg[0];
531               arg[0] = arg[1];
532               arg[1] = t;
533
534               msg (SW, _("The value arguments passed to the %s function "
535                          "are out-of-order.  They will be treated as if "
536                          "they had been specified in the correct order."),
537                    function->name);
538             }
539         }
540
541       /* Finally add these to the linked list of aggregation
542          variables. */
543       for (i = 0; i < n_dest; i++)
544         {
545           struct agr_var *v = xmalloc (sizeof *v);
546
547           /* Add variable to chain. */
548           if (agr->agr_vars != NULL)
549             tail->next = v;
550           else
551             agr->agr_vars = v;
552           tail = v;
553           tail->next = NULL;
554           v->moments = NULL;
555
556           /* Create the target variable in the aggregate
557              dictionary. */
558           {
559             struct variable *destvar;
560
561             v->function = func_index;
562
563             if (src)
564               {
565                 v->src = src[i];
566
567                 if (var_is_alpha (src[i]))
568                   {
569                     v->function |= FSTRING;
570                     v->string = xmalloc (var_get_width (src[i]));
571                   }
572
573                 if (function->alpha_type == VAR_STRING)
574                   destvar = dict_clone_var (agr->dict, v->src, dest[i]);
575                 else
576                   {
577                     assert (var_is_numeric (v->src)
578                             || function->alpha_type == VAR_NUMERIC);
579                     destvar = dict_create_var (agr->dict, dest[i], 0);
580                     if (destvar != NULL)
581                       {
582                         struct fmt_spec f;
583                         if ((func_index == N || func_index == NMISS)
584                             && dict_get_weight (dict) != NULL)
585                           f = fmt_for_output (FMT_F, 8, 2);
586                         else
587                           f = function->format;
588                         var_set_both_formats (destvar, &f);
589                       }
590                   }
591               } else {
592                 struct fmt_spec f;
593                 v->src = NULL;
594                 destvar = dict_create_var (agr->dict, dest[i], 0);
595                 if (func_index == N_NO_VARS && dict_get_weight (dict) != NULL)
596                   f = fmt_for_output (FMT_F, 8, 2);
597                 else
598                   f = function->format;
599                 var_set_both_formats (destvar, &f);
600               }
601
602             if (!destvar)
603               {
604                 msg (SE, _("Variable name %s is not unique within the "
605                            "aggregate file dictionary, which contains "
606                            "the aggregate variables and the break "
607                            "variables."),
608                      dest[i]);
609                 goto error;
610               }
611
612             free (dest[i]);
613             if (dest_label[i])
614               var_set_label (destvar, dest_label[i]);
615
616             v->dest = destvar;
617           }
618
619           v->exclude = exclude;
620
621           if (v->src != NULL)
622             {
623               int j;
624
625               if (var_is_numeric (v->src))
626                 for (j = 0; j < function->n_args; j++)
627                   v->arg[j].f = arg[j].f;
628               else
629                 for (j = 0; j < function->n_args; j++)
630                   v->arg[j].c = xstrdup (arg[j].c);
631             }
632         }
633
634       if (src != NULL && var_is_alpha (src[0]))
635         for (i = 0; i < function->n_args; i++)
636           {
637             free (arg[i].c);
638             arg[i].c = NULL;
639           }
640
641       free (src);
642       free (dest);
643       free (dest_label);
644
645       if (!lex_match (lexer, '/'))
646         {
647           if (lex_token (lexer) == '.')
648             return true;
649
650           lex_error (lexer, "expecting end of command");
651           return false;
652         }
653       continue;
654
655     error:
656       ds_destroy (&function_name);
657       for (i = 0; i < n_dest; i++)
658         {
659           free (dest[i]);
660           free (dest_label[i]);
661         }
662       free (dest);
663       free (dest_label);
664       free (arg[0].c);
665       free (arg[1].c);
666       if (src && n_src && var_is_alpha (src[0]))
667         for (i = 0; i < function->n_args; i++)
668           {
669             free (arg[i].c);
670             arg[i].c = NULL;
671           }
672       free (src);
673
674       return false;
675     }
676 }
677
678 /* Destroys AGR. */
679 static void
680 agr_destroy (struct agr_proc *agr)
681 {
682   struct agr_var *iter, *next;
683
684   case_ordering_destroy (agr->sort);
685   free (agr->break_vars);
686   case_destroy (&agr->break_case);
687   for (iter = agr->agr_vars; iter; iter = next)
688     {
689       next = iter->next;
690
691       if (iter->function & FSTRING)
692         {
693           size_t n_args;
694           size_t i;
695
696           n_args = agr_func_tab[iter->function & FUNC].n_args;
697           for (i = 0; i < n_args; i++)
698             free (iter->arg[i].c);
699           free (iter->string);
700         }
701       else if (iter->function == SD)
702         moments1_destroy (iter->moments);
703       free (iter);
704     }
705   if (agr->dict != NULL)
706     dict_destroy (agr->dict);
707 }
708 \f
709 /* Execution. */
710
711 /* Accumulates aggregation data from the case INPUT. */
712 static void
713 accumulate_aggregate_info (struct agr_proc *agr, const struct ccase *input)
714 {
715   struct agr_var *iter;
716   double weight;
717   bool bad_warn = true;
718
719   weight = dict_get_case_weight (agr->src_dict, input, &bad_warn);
720
721   for (iter = agr->agr_vars; iter; iter = iter->next)
722     if (iter->src)
723       {
724         const union value *v = case_data (input, iter->src);
725         int src_width = var_get_width (iter->src);
726
727         if (var_is_value_missing (iter->src, v, iter->exclude))
728           {
729             switch (iter->function)
730               {
731               case NMISS:
732               case NMISS | FSTRING:
733                 iter->dbl[0] += weight;
734                 break;
735               case NUMISS:
736               case NUMISS | FSTRING:
737                 iter->int1++;
738                 break;
739               }
740             iter->saw_missing = true;
741             continue;
742           }
743
744         /* This is horrible.  There are too many possibilities. */
745         switch (iter->function)
746           {
747           case SUM:
748             iter->dbl[0] += v->f * weight;
749             iter->int1 = 1;
750             break;
751           case MEAN:
752             iter->dbl[0] += v->f * weight;
753             iter->dbl[1] += weight;
754             break;
755           case SD:
756             moments1_add (iter->moments, v->f, weight);
757             break;
758           case MAX:
759             iter->dbl[0] = MAX (iter->dbl[0], v->f);
760             iter->int1 = 1;
761             break;
762           case MAX | FSTRING:
763             if (memcmp (iter->string, v->s, src_width) < 0)
764               memcpy (iter->string, v->s, src_width);
765             iter->int1 = 1;
766             break;
767           case MIN:
768             iter->dbl[0] = MIN (iter->dbl[0], v->f);
769             iter->int1 = 1;
770             break;
771           case MIN | FSTRING:
772             if (memcmp (iter->string, v->s, src_width) > 0)
773               memcpy (iter->string, v->s, src_width);
774             iter->int1 = 1;
775             break;
776           case FGT:
777           case PGT:
778             if (v->f > iter->arg[0].f)
779               iter->dbl[0] += weight;
780             iter->dbl[1] += weight;
781             break;
782           case FGT | FSTRING:
783           case PGT | FSTRING:
784             if (memcmp (iter->arg[0].c, v->s, src_width) < 0)
785               iter->dbl[0] += weight;
786             iter->dbl[1] += weight;
787             break;
788           case FLT:
789           case PLT:
790             if (v->f < iter->arg[0].f)
791               iter->dbl[0] += weight;
792             iter->dbl[1] += weight;
793             break;
794           case FLT | FSTRING:
795           case PLT | FSTRING:
796             if (memcmp (iter->arg[0].c, v->s, src_width) > 0)
797               iter->dbl[0] += weight;
798             iter->dbl[1] += weight;
799             break;
800           case FIN:
801           case PIN:
802             if (iter->arg[0].f <= v->f && v->f <= iter->arg[1].f)
803               iter->dbl[0] += weight;
804             iter->dbl[1] += weight;
805             break;
806           case FIN | FSTRING:
807           case PIN | FSTRING:
808             if (memcmp (iter->arg[0].c, v->s, src_width) <= 0
809                 && memcmp (iter->arg[1].c, v->s, src_width) >= 0)
810               iter->dbl[0] += weight;
811             iter->dbl[1] += weight;
812             break;
813           case FOUT:
814           case POUT:
815             if (iter->arg[0].f > v->f || v->f > iter->arg[1].f)
816               iter->dbl[0] += weight;
817             iter->dbl[1] += weight;
818             break;
819           case FOUT | FSTRING:
820           case POUT | FSTRING:
821             if (memcmp (iter->arg[0].c, v->s, src_width) > 0
822                 || memcmp (iter->arg[1].c, v->s, src_width) < 0)
823               iter->dbl[0] += weight;
824             iter->dbl[1] += weight;
825             break;
826           case N:
827           case N | FSTRING:
828             iter->dbl[0] += weight;
829             break;
830           case NU:
831           case NU | FSTRING:
832             iter->int1++;
833             break;
834           case FIRST:
835             if (iter->int1 == 0)
836               {
837                 iter->dbl[0] = v->f;
838                 iter->int1 = 1;
839               }
840             break;
841           case FIRST | FSTRING:
842             if (iter->int1 == 0)
843               {
844                 memcpy (iter->string, v->s, src_width);
845                 iter->int1 = 1;
846               }
847             break;
848           case LAST:
849             iter->dbl[0] = v->f;
850             iter->int1 = 1;
851             break;
852           case LAST | FSTRING:
853             memcpy (iter->string, v->s, src_width);
854             iter->int1 = 1;
855             break;
856           case NMISS:
857           case NMISS | FSTRING:
858           case NUMISS:
859           case NUMISS | FSTRING:
860             /* Our value is not missing or it would have been
861                caught earlier.  Nothing to do. */
862             break;
863           default:
864             NOT_REACHED ();
865           }
866     } else {
867       switch (iter->function)
868         {
869         case N_NO_VARS:
870           iter->dbl[0] += weight;
871           break;
872         case NU_NO_VARS:
873           iter->int1++;
874           break;
875         default:
876           NOT_REACHED ();
877         }
878     }
879 }
880
881 /* Writes an aggregated record to OUTPUT. */
882 static void
883 dump_aggregate_info (struct agr_proc *agr, struct casewriter *output)
884 {
885   struct ccase c;
886
887   case_create (&c, dict_get_next_value_idx (agr->dict));
888
889   {
890     int value_idx = 0;
891     int i;
892
893     for (i = 0; i < agr->break_var_cnt; i++)
894       {
895         const struct variable *v = agr->break_vars[i];
896         size_t value_cnt = var_get_value_cnt (v);
897         memcpy (case_data_rw_idx (&c, value_idx),
898                 case_data (&agr->break_case, v),
899                 sizeof (union value) * value_cnt);
900         value_idx += value_cnt;
901       }
902   }
903
904   {
905     struct agr_var *i;
906
907     for (i = agr->agr_vars; i; i = i->next)
908       {
909         union value *v = case_data_rw (&c, i->dest);
910
911         if (agr->missing == COLUMNWISE && i->saw_missing
912             && (i->function & FUNC) != N && (i->function & FUNC) != NU
913             && (i->function & FUNC) != NMISS && (i->function & FUNC) != NUMISS)
914           {
915             if (var_is_alpha (i->dest))
916               memset (v->s, ' ', var_get_width (i->dest));
917             else
918               v->f = SYSMIS;
919             continue;
920           }
921
922         switch (i->function)
923           {
924           case SUM:
925             v->f = i->int1 ? i->dbl[0] : SYSMIS;
926             break;
927           case MEAN:
928             v->f = i->dbl[1] != 0.0 ? i->dbl[0] / i->dbl[1] : SYSMIS;
929             break;
930           case SD:
931             {
932               double variance;
933
934               /* FIXME: we should use two passes. */
935               moments1_calculate (i->moments, NULL, NULL, &variance,
936                                  NULL, NULL);
937               if (variance != SYSMIS)
938                 v->f = sqrt (variance);
939               else
940                 v->f = SYSMIS;
941             }
942             break;
943           case MAX:
944           case MIN:
945             v->f = i->int1 ? i->dbl[0] : SYSMIS;
946             break;
947           case MAX | FSTRING:
948           case MIN | FSTRING:
949             if (i->int1)
950               memcpy (v->s, i->string, var_get_width (i->dest));
951             else
952               memset (v->s, ' ', var_get_width (i->dest));
953             break;
954           case FGT:
955           case FGT | FSTRING:
956           case FLT:
957           case FLT | FSTRING:
958           case FIN:
959           case FIN | FSTRING:
960           case FOUT:
961           case FOUT | FSTRING:
962             v->f = i->dbl[1] ? i->dbl[0] / i->dbl[1] : SYSMIS;
963             break;
964           case PGT:
965           case PGT | FSTRING:
966           case PLT:
967           case PLT | FSTRING:
968           case PIN:
969           case PIN | FSTRING:
970           case POUT:
971           case POUT | FSTRING:
972             v->f = i->dbl[1] ? i->dbl[0] / i->dbl[1] * 100.0 : SYSMIS;
973             break;
974           case N:
975           case N | FSTRING:
976             v->f = i->dbl[0];
977             break;
978           case NU:
979           case NU | FSTRING:
980             v->f = i->int1;
981             break;
982           case FIRST:
983           case LAST:
984             v->f = i->int1 ? i->dbl[0] : SYSMIS;
985             break;
986           case FIRST | FSTRING:
987           case LAST | FSTRING:
988             if (i->int1)
989               memcpy (v->s, i->string, var_get_width (i->dest));
990             else
991               memset (v->s, ' ', var_get_width (i->dest));
992             break;
993           case N_NO_VARS:
994             v->f = i->dbl[0];
995             break;
996           case NU_NO_VARS:
997             v->f = i->int1;
998             break;
999           case NMISS:
1000           case NMISS | FSTRING:
1001             v->f = i->dbl[0];
1002             break;
1003           case NUMISS:
1004           case NUMISS | FSTRING:
1005             v->f = i->int1;
1006             break;
1007           default:
1008             NOT_REACHED ();
1009           }
1010       }
1011   }
1012
1013   casewriter_write (output, &c);
1014 }
1015
1016 /* Resets the state for all the aggregate functions. */
1017 static void
1018 initialize_aggregate_info (struct agr_proc *agr, const struct ccase *input)
1019 {
1020   struct agr_var *iter;
1021
1022   case_destroy (&agr->break_case);
1023   case_clone (&agr->break_case, input);
1024
1025   for (iter = agr->agr_vars; iter; iter = iter->next)
1026     {
1027       iter->saw_missing = false;
1028       iter->dbl[0] = iter->dbl[1] = iter->dbl[2] = 0.0;
1029       iter->int1 = iter->int2 = 0;
1030       switch (iter->function)
1031         {
1032         case MIN:
1033           iter->dbl[0] = DBL_MAX;
1034           break;
1035         case MIN | FSTRING:
1036           memset (iter->string, 255, var_get_width (iter->src));
1037           break;
1038         case MAX:
1039           iter->dbl[0] = -DBL_MAX;
1040           break;
1041         case MAX | FSTRING:
1042           memset (iter->string, 0, var_get_width (iter->src));
1043           break;
1044         case SD:
1045           if (iter->moments == NULL)
1046             iter->moments = moments1_create (MOMENT_VARIANCE);
1047           else
1048             moments1_clear (iter->moments);
1049           break;
1050         default:
1051           break;
1052         }
1053     }
1054 }