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