chart: Fix format string issues in chart_write_title callers.
[pspp] / 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 val_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, VAL_STRING,  {-1, -1, -1}},
107     {"MIN",     0, VAL_STRING,  {-1, -1, -1}},
108     {"PGT",     1, VAL_NUMERIC, {FMT_F, 5, 1}},
109     {"PLT",     1, VAL_NUMERIC, {FMT_F, 5, 1}},
110     {"PIN",     2, VAL_NUMERIC, {FMT_F, 5, 1}},
111     {"POUT",    2, VAL_NUMERIC, {FMT_F, 5, 1}},
112     {"FGT",     1, VAL_NUMERIC, {FMT_F, 5, 3}},
113     {"FLT",     1, VAL_NUMERIC, {FMT_F, 5, 3}},
114     {"FIN",     2, VAL_NUMERIC, {FMT_F, 5, 3}},
115     {"FOUT",    2, VAL_NUMERIC, {FMT_F, 5, 3}},
116     {"N",       0, VAL_NUMERIC, {FMT_F, 7, 0}},
117     {"NU",      0, VAL_NUMERIC, {FMT_F, 7, 0}},
118     {"NMISS",   0, VAL_NUMERIC, {FMT_F, 7, 0}},
119     {"NUMISS",  0, VAL_NUMERIC, {FMT_F, 7, 0}},
120     {"FIRST",   0, VAL_STRING,  {-1, -1, -1}},
121     {"LAST",    0, VAL_STRING,  {-1, -1, -1}},
122     {NULL,      0, -1,          {-1, -1, -1}},
123     {"N",       0, VAL_NUMERIC, {FMT_F, 7, 0}},
124     {"NU",      0, VAL_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   fh_unref (out_file);
334   return CMD_SUCCESS;
335
336 error:
337   if (input != NULL)
338     proc_commit (ds);
339   casewriter_destroy (output);
340   agr_destroy (&agr);
341   fh_unref (out_file);
342   return CMD_CASCADING_FAILURE;
343 }
344
345 /* Parse all the aggregate functions. */
346 static bool
347 parse_aggregate_functions (struct lexer *lexer, const struct dictionary *dict, struct agr_proc *agr)
348 {
349   struct agr_var *tail; /* Tail of linked list starting at agr->vars. */
350
351   /* Parse everything. */
352   tail = NULL;
353   for (;;)
354     {
355       char **dest;
356       char **dest_label;
357       size_t n_dest;
358       struct string function_name;
359
360       enum mv_class exclude;
361       const struct agr_func *function;
362       int func_index;
363
364       union agr_argument arg[2];
365
366       const struct variable **src;
367       size_t n_src;
368
369       size_t i;
370
371       dest = NULL;
372       dest_label = NULL;
373       n_dest = 0;
374       src = NULL;
375       function = NULL;
376       n_src = 0;
377       arg[0].c = NULL;
378       arg[1].c = NULL;
379       ds_init_empty (&function_name);
380
381       /* Parse the list of target variables. */
382       while (!lex_match (lexer, '='))
383         {
384           size_t n_dest_prev = n_dest;
385
386           if (!parse_DATA_LIST_vars (lexer, &dest, &n_dest,
387                                      PV_APPEND | PV_SINGLE | PV_NO_SCRATCH))
388             goto error;
389
390           /* Assign empty labels. */
391           {
392             int j;
393
394             dest_label = xnrealloc (dest_label, n_dest, sizeof *dest_label);
395             for (j = n_dest_prev; j < n_dest; j++)
396               dest_label[j] = NULL;
397           }
398
399
400
401           if (lex_token (lexer) == T_STRING)
402             {
403               struct string label;
404               ds_init_string (&label, lex_tokstr (lexer));
405
406               ds_truncate (&label, 255);
407               dest_label[n_dest - 1] = ds_xstrdup (&label);
408               lex_get (lexer);
409               ds_destroy (&label);
410             }
411         }
412
413       /* Get the name of the aggregation function. */
414       if (lex_token (lexer) != T_ID)
415         {
416           lex_error (lexer, _("expecting aggregation function"));
417           goto error;
418         }
419
420       exclude = MV_ANY;
421
422       ds_assign_string (&function_name, lex_tokstr (lexer));
423
424       ds_chomp (&function_name, '.');
425
426       if (lex_tokid(lexer)[strlen (lex_tokid (lexer)) - 1] == '.')
427         exclude = MV_SYSTEM;
428
429       for (function = agr_func_tab; function->name; function++)
430         if (!strcasecmp (function->name, ds_cstr (&function_name)))
431           break;
432       if (NULL == function->name)
433         {
434           msg (SE, _("Unknown aggregation function %s."),
435                ds_cstr (&function_name));
436           goto error;
437         }
438       ds_destroy (&function_name);
439       func_index = function - agr_func_tab;
440       lex_get (lexer);
441
442       /* Check for leading lparen. */
443       if (!lex_match (lexer, '('))
444         {
445           if (func_index == N)
446             func_index = N_NO_VARS;
447           else if (func_index == NU)
448             func_index = NU_NO_VARS;
449           else
450             {
451               lex_error (lexer, _("expecting `('"));
452               goto error;
453             }
454         }
455       else
456         {
457           /* Parse list of source variables. */
458           {
459             int pv_opts = PV_NO_SCRATCH;
460
461             if (func_index == SUM || func_index == MEAN || func_index == SD)
462               pv_opts |= PV_NUMERIC;
463             else if (function->n_args)
464               pv_opts |= PV_SAME_TYPE;
465
466             if (!parse_variables_const (lexer, dict, &src, &n_src, pv_opts))
467               goto error;
468           }
469
470           /* Parse function arguments, for those functions that
471              require arguments. */
472           if (function->n_args != 0)
473             for (i = 0; i < function->n_args; i++)
474               {
475                 int type;
476
477                 lex_match (lexer, ',');
478                 if (lex_token (lexer) == T_STRING)
479                   {
480                     arg[i].c = ds_xstrdup (lex_tokstr (lexer));
481                     type = VAL_STRING;
482                   }
483                 else if (lex_is_number (lexer))
484                   {
485                     arg[i].f = lex_tokval (lexer);
486                     type = VAL_NUMERIC;
487                   }
488                 else
489                   {
490                     msg (SE, _("Missing argument %zu to %s."),
491                          i + 1, function->name);
492                     goto error;
493                   }
494
495                 lex_get (lexer);
496
497                 if (type != var_get_type (src[0]))
498                   {
499                     msg (SE, _("Arguments to %s must be of same type as "
500                                "source variables."),
501                          function->name);
502                     goto error;
503                   }
504               }
505
506           /* Trailing rparen. */
507           if (!lex_match (lexer, ')'))
508             {
509               lex_error (lexer, _("expecting `)'"));
510               goto error;
511             }
512
513           /* Now check that the number of source variables match
514              the number of target variables.  If we check earlier
515              than this, the user can get very misleading error
516              message, i.e. `AGGREGATE x=SUM(y t).' will get this
517              error message when a proper message would be more
518              like `unknown variable t'. */
519           if (n_src != n_dest)
520             {
521               msg (SE, _("Number of source variables (%zu) does not match "
522                          "number of target variables (%zu)."),
523                     n_src, n_dest);
524               goto error;
525             }
526
527           if ((func_index == PIN || func_index == POUT
528               || func_index == FIN || func_index == FOUT)
529               && (var_is_numeric (src[0])
530                   ? arg[0].f > arg[1].f
531                   : str_compare_rpad (arg[0].c, arg[1].c) > 0))
532             {
533               union agr_argument t = arg[0];
534               arg[0] = arg[1];
535               arg[1] = t;
536
537               msg (SW, _("The value arguments passed to the %s function "
538                          "are out-of-order.  They will be treated as if "
539                          "they had been specified in the correct order."),
540                    function->name);
541             }
542         }
543
544       /* Finally add these to the linked list of aggregation
545          variables. */
546       for (i = 0; i < n_dest; i++)
547         {
548           struct agr_var *v = xmalloc (sizeof *v);
549
550           /* Add variable to chain. */
551           if (agr->agr_vars != NULL)
552             tail->next = v;
553           else
554             agr->agr_vars = v;
555           tail = v;
556           tail->next = NULL;
557           v->moments = NULL;
558
559           /* Create the target variable in the aggregate
560              dictionary. */
561           {
562             struct variable *destvar;
563
564             v->function = func_index;
565
566             if (src)
567               {
568                 v->src = src[i];
569
570                 if (var_is_alpha (src[i]))
571                   {
572                     v->function |= FSTRING;
573                     v->string = xmalloc (var_get_width (src[i]));
574                   }
575
576                 if (function->alpha_type == VAL_STRING)
577                   destvar = dict_clone_var (agr->dict, v->src, dest[i]);
578                 else
579                   {
580                     assert (var_is_numeric (v->src)
581                             || function->alpha_type == VAL_NUMERIC);
582                     destvar = dict_create_var (agr->dict, dest[i], 0);
583                     if (destvar != NULL)
584                       {
585                         struct fmt_spec f;
586                         if ((func_index == N || func_index == NMISS)
587                             && dict_get_weight (dict) != NULL)
588                           f = fmt_for_output (FMT_F, 8, 2);
589                         else
590                           f = function->format;
591                         var_set_both_formats (destvar, &f);
592                       }
593                   }
594               } else {
595                 struct fmt_spec f;
596                 v->src = NULL;
597                 destvar = dict_create_var (agr->dict, dest[i], 0);
598                 if (func_index == N_NO_VARS && dict_get_weight (dict) != NULL)
599                   f = fmt_for_output (FMT_F, 8, 2);
600                 else
601                   f = function->format;
602                 var_set_both_formats (destvar, &f);
603               }
604
605             if (!destvar)
606               {
607                 msg (SE, _("Variable name %s is not unique within the "
608                            "aggregate file dictionary, which contains "
609                            "the aggregate variables and the break "
610                            "variables."),
611                      dest[i]);
612                 goto error;
613               }
614
615             free (dest[i]);
616             if (dest_label[i])
617               var_set_label (destvar, dest_label[i]);
618
619             v->dest = destvar;
620           }
621
622           v->exclude = exclude;
623
624           if (v->src != NULL)
625             {
626               int j;
627
628               if (var_is_numeric (v->src))
629                 for (j = 0; j < function->n_args; j++)
630                   v->arg[j].f = arg[j].f;
631               else
632                 for (j = 0; j < function->n_args; j++)
633                   v->arg[j].c = xstrdup (arg[j].c);
634             }
635         }
636
637       if (src != NULL && var_is_alpha (src[0]))
638         for (i = 0; i < function->n_args; i++)
639           {
640             free (arg[i].c);
641             arg[i].c = NULL;
642           }
643
644       free (src);
645       free (dest);
646       free (dest_label);
647
648       if (!lex_match (lexer, '/'))
649         {
650           if (lex_token (lexer) == '.')
651             return true;
652
653           lex_error (lexer, "expecting end of command");
654           return false;
655         }
656       continue;
657
658     error:
659       ds_destroy (&function_name);
660       for (i = 0; i < n_dest; i++)
661         {
662           free (dest[i]);
663           free (dest_label[i]);
664         }
665       free (dest);
666       free (dest_label);
667       free (arg[0].c);
668       free (arg[1].c);
669       if (src && n_src && var_is_alpha (src[0]))
670         for (i = 0; i < function->n_args; i++)
671           {
672             free (arg[i].c);
673             arg[i].c = NULL;
674           }
675       free (src);
676
677       return false;
678     }
679 }
680
681 /* Destroys AGR. */
682 static void
683 agr_destroy (struct agr_proc *agr)
684 {
685   struct agr_var *iter, *next;
686
687   case_ordering_destroy (agr->sort);
688   free (agr->break_vars);
689   case_destroy (&agr->break_case);
690   for (iter = agr->agr_vars; iter; iter = next)
691     {
692       next = iter->next;
693
694       if (iter->function & FSTRING)
695         {
696           size_t n_args;
697           size_t i;
698
699           n_args = agr_func_tab[iter->function & FUNC].n_args;
700           for (i = 0; i < n_args; i++)
701             free (iter->arg[i].c);
702           free (iter->string);
703         }
704       else if (iter->function == SD)
705         moments1_destroy (iter->moments);
706       free (iter);
707     }
708   if (agr->dict != NULL)
709     dict_destroy (agr->dict);
710 }
711 \f
712 /* Execution. */
713
714 /* Accumulates aggregation data from the case INPUT. */
715 static void
716 accumulate_aggregate_info (struct agr_proc *agr, const struct ccase *input)
717 {
718   struct agr_var *iter;
719   double weight;
720   bool bad_warn = true;
721
722   weight = dict_get_case_weight (agr->src_dict, input, &bad_warn);
723
724   for (iter = agr->agr_vars; iter; iter = iter->next)
725     if (iter->src)
726       {
727         const union value *v = case_data (input, iter->src);
728         int src_width = var_get_width (iter->src);
729
730         if (var_is_value_missing (iter->src, v, iter->exclude))
731           {
732             switch (iter->function)
733               {
734               case NMISS:
735               case NMISS | FSTRING:
736                 iter->dbl[0] += weight;
737                 break;
738               case NUMISS:
739               case NUMISS | FSTRING:
740                 iter->int1++;
741                 break;
742               }
743             iter->saw_missing = true;
744             continue;
745           }
746
747         /* This is horrible.  There are too many possibilities. */
748         switch (iter->function)
749           {
750           case SUM:
751             iter->dbl[0] += v->f * weight;
752             iter->int1 = 1;
753             break;
754           case MEAN:
755             iter->dbl[0] += v->f * weight;
756             iter->dbl[1] += weight;
757             break;
758           case SD:
759             moments1_add (iter->moments, v->f, weight);
760             break;
761           case MAX:
762             iter->dbl[0] = MAX (iter->dbl[0], v->f);
763             iter->int1 = 1;
764             break;
765           case MAX | FSTRING:
766             if (memcmp (iter->string, v->s, src_width) < 0)
767               memcpy (iter->string, v->s, src_width);
768             iter->int1 = 1;
769             break;
770           case MIN:
771             iter->dbl[0] = MIN (iter->dbl[0], v->f);
772             iter->int1 = 1;
773             break;
774           case MIN | FSTRING:
775             if (memcmp (iter->string, v->s, src_width) > 0)
776               memcpy (iter->string, v->s, src_width);
777             iter->int1 = 1;
778             break;
779           case FGT:
780           case PGT:
781             if (v->f > iter->arg[0].f)
782               iter->dbl[0] += weight;
783             iter->dbl[1] += weight;
784             break;
785           case FGT | FSTRING:
786           case PGT | FSTRING:
787             if (memcmp (iter->arg[0].c, v->s, src_width) < 0)
788               iter->dbl[0] += weight;
789             iter->dbl[1] += weight;
790             break;
791           case FLT:
792           case PLT:
793             if (v->f < iter->arg[0].f)
794               iter->dbl[0] += weight;
795             iter->dbl[1] += weight;
796             break;
797           case FLT | FSTRING:
798           case PLT | FSTRING:
799             if (memcmp (iter->arg[0].c, v->s, src_width) > 0)
800               iter->dbl[0] += weight;
801             iter->dbl[1] += weight;
802             break;
803           case FIN:
804           case PIN:
805             if (iter->arg[0].f <= v->f && v->f <= iter->arg[1].f)
806               iter->dbl[0] += weight;
807             iter->dbl[1] += weight;
808             break;
809           case FIN | FSTRING:
810           case PIN | FSTRING:
811             if (memcmp (iter->arg[0].c, v->s, src_width) <= 0
812                 && memcmp (iter->arg[1].c, v->s, src_width) >= 0)
813               iter->dbl[0] += weight;
814             iter->dbl[1] += weight;
815             break;
816           case FOUT:
817           case POUT:
818             if (iter->arg[0].f > v->f || v->f > iter->arg[1].f)
819               iter->dbl[0] += weight;
820             iter->dbl[1] += weight;
821             break;
822           case FOUT | FSTRING:
823           case POUT | FSTRING:
824             if (memcmp (iter->arg[0].c, v->s, src_width) > 0
825                 || memcmp (iter->arg[1].c, v->s, src_width) < 0)
826               iter->dbl[0] += weight;
827             iter->dbl[1] += weight;
828             break;
829           case N:
830           case N | FSTRING:
831             iter->dbl[0] += weight;
832             break;
833           case NU:
834           case NU | FSTRING:
835             iter->int1++;
836             break;
837           case FIRST:
838             if (iter->int1 == 0)
839               {
840                 iter->dbl[0] = v->f;
841                 iter->int1 = 1;
842               }
843             break;
844           case FIRST | FSTRING:
845             if (iter->int1 == 0)
846               {
847                 memcpy (iter->string, v->s, src_width);
848                 iter->int1 = 1;
849               }
850             break;
851           case LAST:
852             iter->dbl[0] = v->f;
853             iter->int1 = 1;
854             break;
855           case LAST | FSTRING:
856             memcpy (iter->string, v->s, src_width);
857             iter->int1 = 1;
858             break;
859           case NMISS:
860           case NMISS | FSTRING:
861           case NUMISS:
862           case NUMISS | FSTRING:
863             /* Our value is not missing or it would have been
864                caught earlier.  Nothing to do. */
865             break;
866           default:
867             NOT_REACHED ();
868           }
869     } else {
870       switch (iter->function)
871         {
872         case N_NO_VARS:
873           iter->dbl[0] += weight;
874           break;
875         case NU_NO_VARS:
876           iter->int1++;
877           break;
878         default:
879           NOT_REACHED ();
880         }
881     }
882 }
883
884 /* Writes an aggregated record to OUTPUT. */
885 static void
886 dump_aggregate_info (struct agr_proc *agr, struct casewriter *output)
887 {
888   struct ccase c;
889
890   case_create (&c, dict_get_next_value_idx (agr->dict));
891
892   {
893     int value_idx = 0;
894     int i;
895
896     for (i = 0; i < agr->break_var_cnt; i++)
897       {
898         const struct variable *v = agr->break_vars[i];
899         size_t value_cnt = var_get_value_cnt (v);
900         memcpy (case_data_rw_idx (&c, value_idx),
901                 case_data (&agr->break_case, v),
902                 sizeof (union value) * value_cnt);
903         value_idx += value_cnt;
904       }
905   }
906
907   {
908     struct agr_var *i;
909
910     for (i = agr->agr_vars; i; i = i->next)
911       {
912         union value *v = case_data_rw (&c, i->dest);
913
914         if (agr->missing == COLUMNWISE && i->saw_missing
915             && (i->function & FUNC) != N && (i->function & FUNC) != NU
916             && (i->function & FUNC) != NMISS && (i->function & FUNC) != NUMISS)
917           {
918             if (var_is_alpha (i->dest))
919               memset (v->s, ' ', var_get_width (i->dest));
920             else
921               v->f = SYSMIS;
922             continue;
923           }
924
925         switch (i->function)
926           {
927           case SUM:
928             v->f = i->int1 ? i->dbl[0] : SYSMIS;
929             break;
930           case MEAN:
931             v->f = i->dbl[1] != 0.0 ? i->dbl[0] / i->dbl[1] : SYSMIS;
932             break;
933           case SD:
934             {
935               double variance;
936
937               /* FIXME: we should use two passes. */
938               moments1_calculate (i->moments, NULL, NULL, &variance,
939                                  NULL, NULL);
940               if (variance != SYSMIS)
941                 v->f = sqrt (variance);
942               else
943                 v->f = SYSMIS;
944             }
945             break;
946           case MAX:
947           case MIN:
948             v->f = i->int1 ? i->dbl[0] : SYSMIS;
949             break;
950           case MAX | FSTRING:
951           case MIN | FSTRING:
952             if (i->int1)
953               memcpy (v->s, i->string, var_get_width (i->dest));
954             else
955               memset (v->s, ' ', var_get_width (i->dest));
956             break;
957           case FGT:
958           case FGT | FSTRING:
959           case FLT:
960           case FLT | FSTRING:
961           case FIN:
962           case FIN | FSTRING:
963           case FOUT:
964           case FOUT | FSTRING:
965             v->f = i->dbl[1] ? i->dbl[0] / i->dbl[1] : SYSMIS;
966             break;
967           case PGT:
968           case PGT | FSTRING:
969           case PLT:
970           case PLT | FSTRING:
971           case PIN:
972           case PIN | FSTRING:
973           case POUT:
974           case POUT | FSTRING:
975             v->f = i->dbl[1] ? i->dbl[0] / i->dbl[1] * 100.0 : SYSMIS;
976             break;
977           case N:
978           case N | FSTRING:
979             v->f = i->dbl[0];
980             break;
981           case NU:
982           case NU | FSTRING:
983             v->f = i->int1;
984             break;
985           case FIRST:
986           case LAST:
987             v->f = i->int1 ? i->dbl[0] : SYSMIS;
988             break;
989           case FIRST | FSTRING:
990           case LAST | FSTRING:
991             if (i->int1)
992               memcpy (v->s, i->string, var_get_width (i->dest));
993             else
994               memset (v->s, ' ', var_get_width (i->dest));
995             break;
996           case N_NO_VARS:
997             v->f = i->dbl[0];
998             break;
999           case NU_NO_VARS:
1000             v->f = i->int1;
1001             break;
1002           case NMISS:
1003           case NMISS | FSTRING:
1004             v->f = i->dbl[0];
1005             break;
1006           case NUMISS:
1007           case NUMISS | FSTRING:
1008             v->f = i->int1;
1009             break;
1010           default:
1011             NOT_REACHED ();
1012           }
1013       }
1014   }
1015
1016   casewriter_write (output, &c);
1017 }
1018
1019 /* Resets the state for all the aggregate functions. */
1020 static void
1021 initialize_aggregate_info (struct agr_proc *agr, const struct ccase *input)
1022 {
1023   struct agr_var *iter;
1024
1025   case_destroy (&agr->break_case);
1026   case_clone (&agr->break_case, input);
1027
1028   for (iter = agr->agr_vars; iter; iter = iter->next)
1029     {
1030       iter->saw_missing = false;
1031       iter->dbl[0] = iter->dbl[1] = iter->dbl[2] = 0.0;
1032       iter->int1 = iter->int2 = 0;
1033       switch (iter->function)
1034         {
1035         case MIN:
1036           iter->dbl[0] = DBL_MAX;
1037           break;
1038         case MIN | FSTRING:
1039           memset (iter->string, 255, var_get_width (iter->src));
1040           break;
1041         case MAX:
1042           iter->dbl[0] = -DBL_MAX;
1043           break;
1044         case MAX | FSTRING:
1045           memset (iter->string, 0, var_get_width (iter->src));
1046           break;
1047         case SD:
1048           if (iter->moments == NULL)
1049             iter->moments = moments1_create (MOMENT_VARIANCE);
1050           else
1051             moments1_clear (iter->moments);
1052           break;
1053         default:
1054           break;
1055         }
1056     }
1057 }