1 /* PSPP - computes sample statistics.
2 Copyright (C) 1997-9, 2000 Free Software Foundation, Inc.
3 Written by Ben Pfaff <blp@gnu.org>.
5 This program is free software; you can redistribute it and/or
6 modify it under the terms of the GNU General Public License as
7 published by the Free Software Foundation; either version 2 of the
8 License, or (at your option) any later version.
10 This program is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
26 #include "file-handle.h"
38 #include "debug-print.h"
40 /* Specifies how to make an aggregate variable. */
43 struct agr_var *next; /* Next in list. */
45 /* Collected during parsing. */
46 struct variable *src; /* Source variable. */
47 struct variable *dest; /* Target variable. */
48 int function; /* Function. */
49 int include_missing; /* 1=Include user-missing values. */
50 union value arg[2]; /* Arguments. */
52 /* Accumulated during AGGREGATE execution. */
59 /* Aggregation functions. */
62 NONE, SUM, MEAN, SD, MAX, MIN, PGT, PLT, PIN, POUT, FGT, FLT, FIN,
63 FOUT, N, NU, NMISS, NUMISS, FIRST, LAST,
64 N_AGR_FUNCS, N_NO_VARS, NU_NO_VARS,
65 FUNC = 0x1f, /* Function mask. */
66 FSTRING = 1<<5, /* String function bit. */
69 /* Attributes of an aggregation function. */
72 const char *name; /* Aggregation function name. */
73 int n_args; /* Number of arguments. */
74 int alpha_type; /* When given ALPHA arguments, output type. */
75 struct fmt_spec format; /* Format spec if alpha_type != ALPHA. */
78 /* Attributes of aggregation functions. */
79 static struct agr_func agr_func_tab[] =
81 {"<NONE>", 0, -1, {0, 0, 0}},
82 {"SUM", 0, -1, {FMT_F, 8, 2}},
83 {"MEAN", 0, -1, {FMT_F, 8, 2}},
84 {"SD", 0, -1, {FMT_F, 8, 2}},
85 {"MAX", 0, ALPHA, {-1, -1, -1}},
86 {"MIN", 0, ALPHA, {-1, -1, -1}},
87 {"PGT", 1, NUMERIC, {FMT_F, 5, 1}},
88 {"PLT", 1, NUMERIC, {FMT_F, 5, 1}},
89 {"PIN", 2, NUMERIC, {FMT_F, 5, 1}},
90 {"POUT", 2, NUMERIC, {FMT_F, 5, 1}},
91 {"FGT", 1, NUMERIC, {FMT_F, 5, 3}},
92 {"FLT", 1, NUMERIC, {FMT_F, 5, 3}},
93 {"FIN", 2, NUMERIC, {FMT_F, 5, 3}},
94 {"FOUT", 2, NUMERIC, {FMT_F, 5, 3}},
95 {"N", 0, NUMERIC, {FMT_F, 7, 0}},
96 {"NU", 0, NUMERIC, {FMT_F, 7, 0}},
97 {"NMISS", 0, NUMERIC, {FMT_F, 7, 0}},
98 {"NUMISS", 0, NUMERIC, {FMT_F, 7, 0}},
99 {"FIRST", 0, ALPHA, {-1, -1, -1}},
100 {"LAST", 0, ALPHA, {-1, -1, -1}},
101 {NULL, 0, -1, {-1, -1, -1}},
102 {"N", 0, NUMERIC, {FMT_F, 7, 0}},
103 {"NU", 0, NUMERIC, {FMT_F, 7, 0}},
106 /* Output file, or NULL for the active file. */
107 static struct file_handle *outfile;
109 /* Missing value types. */
112 ITEMWISE, /* Missing values item by item. */
113 COLUMNWISE /* Missing values column by column. */
116 /* ITEMWISE or COLUMNWISE. */
119 /* Aggregate variables. */
120 static struct agr_var *agr_first, *agr_next;
122 /* Aggregate dictionary. */
123 static struct dictionary *agr_dict;
125 /* Number of cases passed through aggregation. */
126 static int case_count;
128 /* Last values of the break variables. */
129 static union value *prev_case;
131 /* Buffers for use by the 10x transformation. */
132 static flt64 *buf64_1xx;
133 static struct ccase *buf_1xx;
135 static void initialize_aggregate_info (void);
138 static int parse_aggregate_functions (void);
139 static void free_aggregate_functions (void);
140 static int aggregate_single_case (struct ccase *input, struct ccase *output);
141 static int create_sysfile (void);
143 static int agr_00x_trns_proc (struct trns_header *, struct ccase *);
144 static void agr_00x_end_func (void *);
145 static int agr_10x_trns_proc (struct trns_header *, struct ccase *);
146 static void agr_10x_trns_free (struct trns_header *);
147 static void agr_10x_end_func (void *);
148 static int agr_11x_func (write_case_data);
151 static void debug_print (int flags);
156 /* Parses and executes the AGGREGATE procedure. */
161 int parse_sort_variables (void);
163 /* Have we seen these subcommands? */
171 agr_dict = dict_create ();
172 dict_set_label (agr_dict, dict_get_label (default_dict));
173 dict_set_documents (agr_dict, dict_get_documents (default_dict));
175 lex_match_id ("AGGREGATE");
177 /* Read most of the subcommands. */
182 if (lex_match_id ("OUTFILE"))
187 dict_destroy (agr_dict);
188 msg (SE, _("%s subcommand given multiple times."),"OUTFILE");
198 outfile = fh_parse_file_handle ();
202 dict_destroy (agr_dict);
207 else if (lex_match_id ("MISSING"))
210 if (!lex_match_id ("COLUMNWISE"))
213 dict_destroy (agr_dict);
214 lex_error (_("while expecting COLUMNWISE"));
217 missing = COLUMNWISE;
219 else if (lex_match_id ("DOCUMENT"))
221 else if (lex_match_id ("PRESORTED"))
223 else if (lex_match_id ("BREAK"))
228 dict_destroy (agr_dict);
229 msg (SE, _("%s subcommand given multiple times."),"BREAK");
235 if (!parse_sort_variables ())
237 dict_destroy (agr_dict);
244 for (i = 0; i < nv_sort; i++)
248 v = dict_clone_var (agr_dict, v_sort[i], v_sort[i]->name);
256 /* Check for proper syntax. */
258 msg (SW, _("BREAK subcommand not specified."));
260 /* Read in the aggregate functions. */
261 if (!parse_aggregate_functions ())
263 free_aggregate_functions ();
268 /* Delete documents. */
270 dict_set_documents (agr_dict, NULL);
272 /* Cancel SPLIT FILE. */
273 dict_set_split_vars (agr_dict, NULL, 0);
281 initialize_aggregate_info ();
283 /* How to implement all this... There are three important variables:
284 whether output is going to the active file (0) or a separate file
285 (1); whether the input data is presorted (0) or needs sorting
286 (1); whether there is a temporary transformation (1) or not (0).
287 The eight cases are as follows:
289 000 (0): Pass it through an aggregate transformation that
292 001 (1): Cancel the temporary transformation and handle as 000.
294 010 (2): Set up a SORT CASES and aggregate the output, writing
295 the results to the active file.
297 011 (3): Cancel the temporary transformation and handle as 010.
299 100 (4): Pass it through an aggregate transformation that doesn't
300 modify the data but merely writes it to the output file.
302 101 (5): Handled as 100.
304 110 (6): Set up a SORT CASES and capture the output, aggregate
305 it, write it to the output file without modifying the active
308 111 (7): Handled as 110. */
315 if (nv_sort != 0 && (seen & 4) == 0)
335 struct trns_header *t = xmalloc (sizeof *t);
336 t->proc = agr_00x_trns_proc;
338 add_transformation (t);
341 temp_dict = agr_dict;
346 procedure (NULL, NULL, agr_00x_end_func, NULL);
353 if (!create_sysfile ())
357 struct trns_header *t = xmalloc (sizeof *t);
358 t->proc = agr_10x_trns_proc;
359 t->free = agr_10x_trns_free;
360 add_transformation (t);
362 procedure (NULL, NULL, agr_10x_end_func, NULL);
372 if (!create_sysfile ())
374 read_sort_output (agr_11x_func, NULL);
377 struct ccase *save_temp_case = temp_case;
380 temp_case = save_temp_case;
395 free_aggregate_functions ();
403 free_aggregate_functions ();
409 /* Create a system file for use in aggregation to an external file,
410 and allocate temporary buffers for writing out cases. */
412 create_sysfile (void)
414 struct sfm_write_info w;
417 w.compress = set_scompression;
418 if (!sfm_write_dictionary (&w))
420 free_aggregate_functions ();
422 dict_destroy (agr_dict);
426 buf64_1xx = xmalloc (sizeof *buf64_1xx * w.case_size);
427 buf_1xx = xmalloc (dict_get_case_size (agr_dict));
432 /* Parse all the aggregate functions. */
434 parse_aggregate_functions (void)
436 agr_first = agr_next = NULL;
438 /* Parse everything. */
446 struct agr_func *function;
451 struct variable **src;
464 /* Parse the list of target variables. */
465 while (!lex_match ('='))
467 int n_dest_prev = n_dest;
469 if (!parse_DATA_LIST_vars (&dest, &n_dest, PV_APPEND | PV_SINGLE | PV_NO_SCRATCH))
472 /* Assign empty labels. */
476 dest_label = xrealloc (dest_label, sizeof *dest_label * n_dest);
477 for (j = n_dest_prev; j < n_dest; j++)
478 dest_label[j] = NULL;
481 if (token == T_STRING)
483 ds_truncate (&tokstr, 120);
484 dest_label[n_dest - 1] = xstrdup (ds_value (&tokstr));
489 /* Get the name of the aggregation function. */
492 lex_error (_("expecting aggregation function"));
497 if (tokid[strlen (tokid) - 1] == '.')
500 tokid[strlen (tokid) - 1] = 0;
503 for (function = agr_func_tab; function->name; function++)
504 if (!strcmp (function->name, tokid))
506 if (NULL == function->name)
508 msg (SE, _("Unknown aggregation function %s."), tokid);
511 func_index = function - agr_func_tab;
514 /* Check for leading lparen. */
515 if (!lex_match ('('))
518 func_index = N_NO_VARS;
519 else if (func_index == NU)
520 func_index = NU_NO_VARS;
523 lex_error (_("expecting `('"));
527 /* Parse list of source variables. */
529 int pv_opts = PV_NO_SCRATCH;
531 if (func_index == SUM || func_index == MEAN || func_index == SD)
532 pv_opts |= PV_NUMERIC;
533 else if (function->n_args)
534 pv_opts |= PV_SAME_TYPE;
536 if (!parse_variables (default_dict, &src, &n_src, pv_opts))
540 /* Parse function arguments, for those functions that
541 require arguments. */
542 if (function->n_args != 0)
543 for (i = 0; i < function->n_args; i++)
548 if (token == T_STRING)
550 arg[i].c = xstrdup (ds_value (&tokstr));
553 else if (token == T_NUM)
558 msg (SE, _("Missing argument %d to %s."), i + 1, function->name);
564 if (type != src[0]->type)
566 msg (SE, _("Arguments to %s must be of same type as "
567 "source variables."),
573 /* Trailing rparen. */
576 lex_error (_("expecting `)'"));
580 /* Now check that the number of source variables match the
581 number of target variables. Do this here because if we
582 do it earlier then the user can get very misleading error
583 messages; i.e., `AGGREGATE x=SUM(y t).' will get this
584 error message when a proper message would be more like
585 `unknown variable t'. */
588 msg (SE, _("Number of source variables (%d) does not match "
589 "number of target variables (%d)."),
595 /* Finally add these to the linked list of aggregation
597 for (i = 0; i < n_dest; i++)
599 struct agr_var *v = xmalloc (sizeof *v);
601 /* Add variable to chain. */
603 agr_next = agr_next->next = v;
605 agr_first = agr_next = v;
606 agr_next->next = NULL;
608 /* Create the target variable in the aggregate
611 struct variable *destvar;
613 agr_next->function = func_index;
619 agr_next->src = src[i];
621 if (src[i]->type == ALPHA)
623 agr_next->function |= FSTRING;
624 agr_next->string = xmalloc (src[i]->width);
627 if (agr_next->src->type == NUMERIC || function->alpha_type == NUMERIC)
630 output_width = agr_next->src->width;
632 if (function->alpha_type == ALPHA)
633 destvar = dict_clone_var (agr_dict, agr_next->src, dest[i]);
636 destvar = dict_create_var (agr_dict, dest[i], output_width);
637 if (output_width == 0)
638 destvar->print = destvar->write = function->format;
639 if (output_width == 0 && dict_get_weight (default_dict) != NULL
640 && (func_index == N || func_index == N_NO_VARS
641 || func_index == NU || func_index == NU_NO_VARS))
643 struct fmt_spec f = {FMT_F, 8, 2};
645 destvar->print = destvar->write = f;
649 agr_next->src = NULL;
650 destvar = dict_create_var (agr_dict, dest[i], 0);
655 msg (SE, _("Variable name %s is not unique within the "
656 "aggregate file dictionary, which contains "
657 "the aggregate variables and the break "
668 destvar->label = dest_label[i];
669 dest_label[i] = NULL;
671 else if (function->alpha_type == ALPHA)
672 destvar->print = destvar->write = function->format;
674 agr_next->dest = destvar;
677 agr_next->include_missing = include_missing;
679 if (agr_next->src != NULL)
683 if (agr_next->src->type == NUMERIC)
684 for (j = 0; j < function->n_args; j++)
685 agr_next->arg[j].f = arg[j].f;
687 for (j = 0; j < function->n_args; j++)
688 agr_next->arg[j].c = xstrdup (arg[j].c);
692 if (src != NULL && src[0]->type == ALPHA)
693 for (i = 0; i < function->n_args; i++)
703 if (!lex_match ('/'))
708 lex_error ("expecting end of command");
714 for (i = 0; i < n_dest; i++)
717 free (dest_label[i]);
723 if (src && n_src && src[0]->type == ALPHA)
724 for (i = 0; i < function->n_args; i++)
735 /* Frees all the state for the AGGREGATE procedure. */
737 free_aggregate_functions (void)
739 struct agr_var *iter, *next;
742 dict_destroy (agr_dict);
743 for (iter = agr_first; iter; iter = next)
747 if (iter->function & FSTRING)
752 n_args = agr_func_tab[iter->function & FUNC].n_args;
753 for (i = 0; i < n_args; i++)
754 free (iter->arg[i].c);
763 static void accumulate_aggregate_info (struct ccase *input);
764 static void dump_aggregate_info (struct ccase *output);
766 /* Processes a single case INPUT for aggregation. If output is
767 warranted, it is written to case OUTPUT, which may be (but need not
768 be) an alias to INPUT. Returns -1 when output is performed, -2
770 /* The code in this function has an eerie similarity to
771 vfm.c:SPLIT_FILE_procfunc()... */
773 aggregate_single_case (struct ccase *input, struct ccase *output)
775 /* The first case always begins a new break group. We also need to
776 preserve the values of the case for later comparison. */
777 if (case_count++ == 0)
784 for (i = 0; i < nv_sort; i++)
785 n_elem += v_sort[i]->nv;
788 prev_case = xmalloc (sizeof *prev_case * n_elem);
790 /* Copy INPUT into prev_case. */
792 union value *iter = prev_case;
795 for (i = 0; i < nv_sort; i++)
797 struct variable *v = v_sort[i];
799 if (v->type == NUMERIC)
800 (iter++)->f = input->data[v->fv].f;
803 memcpy (iter->s, input->data[v->fv].s, v->width);
809 accumulate_aggregate_info (input);
814 /* Compare the value of each break variable to the values on the
817 union value *iter = prev_case;
820 for (i = 0; i < nv_sort; i++)
822 struct variable *v = v_sort[i];
827 if (input->data[v->fv].f != iter->f)
832 if (memcmp (input->data[v->fv].s, iter->s, v->width))
842 accumulate_aggregate_info (input);
847 /* The values of the break variable are different from the values on
848 the previous case. That means that it's time to dump aggregate
850 dump_aggregate_info (output);
851 initialize_aggregate_info ();
852 accumulate_aggregate_info (input);
854 /* Copy INPUT into prev_case. */
856 union value *iter = prev_case;
859 for (i = 0; i < nv_sort; i++)
861 struct variable *v = v_sort[i];
863 if (v->type == NUMERIC)
864 (iter++)->f = input->data[v->fv].f;
867 memcpy (iter->s, input->data[v->fv].s, v->width);
876 /* Accumulates aggregation data from the case INPUT. */
878 accumulate_aggregate_info (struct ccase *input)
880 struct agr_var *iter;
883 weight = dict_get_case_weight (default_dict, input);
885 for (iter = agr_first; iter; iter = iter->next)
888 union value *v = &input->data[iter->src->fv];
890 if ((!iter->include_missing && is_missing (v, iter->src))
891 || (iter->include_missing && iter->src->type == NUMERIC
894 switch (iter->function)
897 iter->dbl[0] += weight;
907 /* This is horrible. There are too many possibilities. */
908 switch (iter->function)
911 iter->dbl[0] += v->f;
914 iter->dbl[0] += v->f * weight;
915 iter->dbl[1] += weight;
919 double product = v->f * weight;
920 iter->dbl[0] += product;
921 iter->dbl[1] += product * v->f;
922 iter->dbl[2] += weight;
926 iter->dbl[0] = max (iter->dbl[0], v->f);
930 if (memcmp (iter->string, v->s, iter->src->width) < 0)
931 memcpy (iter->string, v->s, iter->src->width);
935 iter->dbl[0] = min (iter->dbl[0], v->f);
939 if (memcmp (iter->string, v->s, iter->src->width) > 0)
940 memcpy (iter->string, v->s, iter->src->width);
945 if (v->f > iter->arg[0].f)
946 iter->dbl[0] += weight;
947 iter->dbl[1] += weight;
951 if (memcmp (iter->arg[0].c, v->s, iter->src->width) < 0)
952 iter->dbl[0] += weight;
953 iter->dbl[1] += weight;
957 if (v->f < iter->arg[0].f)
958 iter->dbl[0] += weight;
959 iter->dbl[1] += weight;
963 if (memcmp (iter->arg[0].c, v->s, iter->src->width) > 0)
964 iter->dbl[0] += weight;
965 iter->dbl[1] += weight;
969 if (iter->arg[0].f <= v->f && v->f <= iter->arg[1].f)
970 iter->dbl[0] += weight;
971 iter->dbl[1] += weight;
975 if (memcmp (iter->arg[0].c, v->s, iter->src->width) <= 0
976 && memcmp (iter->arg[1].c, v->s, iter->src->width) >= 0)
977 iter->dbl[0] += weight;
978 iter->dbl[1] += weight;
982 if (iter->arg[0].f > v->f || v->f > iter->arg[1].f)
983 iter->dbl[0] += weight;
984 iter->dbl[1] += weight;
988 if (memcmp (iter->arg[0].c, v->s, iter->src->width) > 0
989 && memcmp (iter->arg[1].c, v->s, iter->src->width) < 0)
990 iter->dbl[0] += weight;
991 iter->dbl[1] += weight;
994 iter->dbl[0] += weight;
1000 if (iter->int1 == 0)
1002 iter->dbl[0] = v->f;
1006 case FIRST | FSTRING:
1007 if (iter->int1 == 0)
1009 memcpy (iter->string, v->s, iter->src->width);
1014 iter->dbl[0] = v->f;
1017 case LAST | FSTRING:
1018 memcpy (iter->string, v->s, iter->src->width);
1025 switch (iter->function)
1028 iter->dbl[0] += weight;
1039 /* We've come to a record that differs from the previous in one or
1040 more of the break variables. Make an output record from the
1041 accumulated statistics in the OUTPUT case. */
1043 dump_aggregate_info (struct ccase *output)
1045 debug_printf (("(dumping "));
1053 for (i = 0; i < nv_sort; i++)
1054 n_elem += v_sort[i]->nv;
1056 debug_printf (("n_elem=%d:", n_elem));
1057 memcpy (output->data, prev_case, sizeof (union value) * n_elem);
1063 for (i = agr_first; i; i = i->next)
1065 union value *v = &output->data[i->dest->fv];
1067 debug_printf ((" %d,%d", i->dest->fv, i->dest->nv));
1069 if (missing == COLUMNWISE && i->missing != 0
1070 && (i->function & FUNC) != N && (i->function & FUNC) != NU
1071 && (i->function & FUNC) != NMISS && (i->function & FUNC) != NUMISS)
1073 if (i->function & FSTRING)
1074 memset (v->s, ' ', i->dest->width);
1080 switch (i->function)
1086 v->f = i->dbl[1] != 0.0 ? i->dbl[0] / i->dbl[1] : SYSMIS;
1089 v->f = ((i->dbl[2] > 1.0)
1090 ? calc_stddev (calc_variance (i->dbl, i->dbl[2]))
1095 v->f = i->int1 ? i->dbl[0] : SYSMIS;
1100 memcpy (v->s, i->string, i->dest->width);
1102 memset (v->s, ' ', i->dest->width);
1107 case FOUT | FSTRING:
1108 v->f = i->int2 ? (double) i->int1 / (double) i->int2 : SYSMIS;
1114 v->f = i->dbl[1] ? i->dbl[0] / i->dbl[1] : SYSMIS;
1123 case POUT | FSTRING:
1124 v->f = i->dbl[1] ? i->dbl[0] / i->dbl[1] * 100.0 : SYSMIS;
1134 v->f = i->int1 ? i->dbl[0] : SYSMIS;
1136 case FIRST | FSTRING:
1137 case LAST | FSTRING:
1139 memcpy (v->s, i->string, i->dest->width);
1141 memset (v->s, ' ', i->dest->width);
1160 debug_printf ((") "));
1163 /* Resets the state for all the aggregate functions. */
1165 initialize_aggregate_info (void)
1167 struct agr_var *iter;
1169 for (iter = agr_first; iter; iter = iter->next)
1172 switch (iter->function)
1175 iter->dbl[0] = DBL_MAX;
1178 memset (iter->string, 255, iter->src->width);
1181 iter->dbl[0] = -DBL_MAX;
1184 memset (iter->string, 0, iter->src->width);
1187 iter->dbl[0] = iter->dbl[1] = iter->dbl[2] = 0.0;
1188 iter->int1 = iter->int2 = 0;
1194 /* Aggregate each case as it comes through. Cases which aren't needed
1197 agr_00x_trns_proc (struct trns_header *h UNUSED, struct ccase *c)
1199 int code = aggregate_single_case (c, compaction_case);
1200 debug_printf (("%d ", code));
1204 /* Output the last aggregate case. It's okay to call the vfm_sink's
1205 write() method here because end_func is called so soon after all
1206 the cases have been output; very little has been cleaned up at this
1209 agr_00x_end_func (void *aux UNUSED)
1211 /* Ensure that info for the last break group gets written to the
1213 dump_aggregate_info (compaction_case);
1214 vfm_sink_info.ncases++;
1218 /* Transform the aggregate case buf_1xx, in internal format, to system
1219 file format, in buf64_1xx, and write the resultant case to the
1222 write_case_to_sfm (void)
1224 flt64 *p = buf64_1xx;
1227 for (i = 0; i < dict_get_var_cnt (agr_dict); i++)
1229 struct variable *v = dict_get_var (agr_dict, i);
1231 if (v->type == NUMERIC)
1233 double src = buf_1xx->data[v->fv].f;
1241 memcpy (p, buf_1xx->data[v->fv].s, v->width);
1242 memset (&((char *) p)[v->width], ' ',
1243 REM_RND_UP (v->width, sizeof (flt64)));
1244 p += DIV_RND_UP (v->width, sizeof (flt64));
1248 sfm_write_case (outfile, buf64_1xx, p - buf64_1xx);
1251 /* Aggregate the current case and output it if we passed a
1254 agr_10x_trns_proc (struct trns_header *h UNUSED, struct ccase *c)
1256 int code = aggregate_single_case (c, buf_1xx);
1258 assert (code == -2 || code == -1);
1260 write_case_to_sfm ();
1264 /* Close the system file now that we're done with it. */
1266 agr_10x_trns_free (struct trns_header *h UNUSED)
1268 fh_close_handle (outfile);
1271 /* Ensure that info for the last break group gets written to the
1274 agr_10x_end_func (void *aux UNUSED)
1276 dump_aggregate_info (buf_1xx);
1277 write_case_to_sfm ();
1280 /* When called with temp_case non-NULL (the normal case), runs the
1281 case through the aggregater and outputs it to the system file if
1282 appropriate. If temp_case is NULL, finishes up writing the last
1283 case if necessary. */
1285 agr_11x_func (write_case_data wc_data UNUSED)
1287 if (temp_case != NULL)
1289 int code = aggregate_single_case (temp_case, buf_1xx);
1291 assert (code == -2 || code == -1);
1293 write_case_to_sfm ();
1299 dump_aggregate_info (buf_1xx);
1300 write_case_to_sfm ();
1302 fh_close_handle (outfile);
1309 /* Print out useful debugging information. */
1311 debug_print (int flags)
1313 printf ("AGGREGATE\n /OUTFILE=%s\n",
1314 outfile ? fh_handle_filename (outfile) : "*");
1316 if (missing == COLUMNWISE)
1317 puts (" /MISSING=COLUMNWISE");
1320 puts (" /DOCUMENT");
1322 puts (" /PRESORTED");
1327 printf (" /BREAK=");
1328 for (i = 0; i < nv_sort; i++)
1329 printf ("%s(%c) ", v_sort[i]->name,
1330 v_sort[i]->p.srt.order == SRT_ASCEND ? 'A' : 'D');
1331 putc ('\n', stdout);
1335 struct agr_var *iter;
1337 for (iter = agr_first; iter; iter = iter->next)
1339 struct agr_func *f = &agr_func_tab[iter->function & FUNC];
1341 printf (" /%s", iter->dest->name);
1342 if (iter->dest->label)
1343 printf ("'%s'", iter->dest->label);
1344 printf ("=%s(%s", f->name, iter->src->name);
1349 for (i = 0; i < f->n_args; i++)
1352 if (iter->src->type == NUMERIC)
1353 printf ("%g", iter->arg[i].f);
1355 printf ("%.*s", iter->src->width, iter->arg[i].c);
1363 #endif /* DEBUGGING */