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