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"
39 #include "debug-print.h"
41 /* Specifies how to make an aggregate variable. */
44 struct agr_var *next; /* Next in list. */
46 /* Collected during parsing. */
47 struct variable *src; /* Source variable. */
48 struct variable *dest; /* Target variable. */
49 int function; /* Function. */
50 int include_missing; /* 1=Include user-missing values. */
51 union value arg[2]; /* Arguments. */
53 /* Accumulated during AGGREGATE execution. */
60 /* Aggregation functions. */
63 NONE, SUM, MEAN, SD, MAX, MIN, PGT, PLT, PIN, POUT, FGT, FLT, FIN,
64 FOUT, N, NU, NMISS, NUMISS, FIRST, LAST,
65 N_AGR_FUNCS, N_NO_VARS, NU_NO_VARS,
66 FUNC = 0x1f, /* Function mask. */
67 FSTRING = 1<<5, /* String function bit. */
70 /* Attributes of an aggregation function. */
73 const char *name; /* Aggregation function name. */
74 int n_args; /* Number of arguments. */
75 int alpha_type; /* When given ALPHA arguments, output type. */
76 struct fmt_spec format; /* Format spec if alpha_type != ALPHA. */
79 /* Attributes of aggregation functions. */
80 static const struct agr_func agr_func_tab[] =
82 {"<NONE>", 0, -1, {0, 0, 0}},
83 {"SUM", 0, -1, {FMT_F, 8, 2}},
84 {"MEAN", 0, -1, {FMT_F, 8, 2}},
85 {"SD", 0, -1, {FMT_F, 8, 2}},
86 {"MAX", 0, ALPHA, {-1, -1, -1}},
87 {"MIN", 0, ALPHA, {-1, -1, -1}},
88 {"PGT", 1, NUMERIC, {FMT_F, 5, 1}},
89 {"PLT", 1, NUMERIC, {FMT_F, 5, 1}},
90 {"PIN", 2, NUMERIC, {FMT_F, 5, 1}},
91 {"POUT", 2, NUMERIC, {FMT_F, 5, 1}},
92 {"FGT", 1, NUMERIC, {FMT_F, 5, 3}},
93 {"FLT", 1, NUMERIC, {FMT_F, 5, 3}},
94 {"FIN", 2, NUMERIC, {FMT_F, 5, 3}},
95 {"FOUT", 2, NUMERIC, {FMT_F, 5, 3}},
96 {"N", 0, NUMERIC, {FMT_F, 7, 0}},
97 {"NU", 0, NUMERIC, {FMT_F, 7, 0}},
98 {"NMISS", 0, NUMERIC, {FMT_F, 7, 0}},
99 {"NUMISS", 0, NUMERIC, {FMT_F, 7, 0}},
100 {"FIRST", 0, ALPHA, {-1, -1, -1}},
101 {"LAST", 0, ALPHA, {-1, -1, -1}},
102 {NULL, 0, -1, {-1, -1, -1}},
103 {"N", 0, NUMERIC, {FMT_F, 7, 0}},
104 {"NU", 0, NUMERIC, {FMT_F, 7, 0}},
107 /* Output file, or NULL for the active file. */
108 static struct file_handle *outfile;
110 /* Missing value types. */
113 ITEMWISE, /* Missing values item by item. */
114 COLUMNWISE /* Missing values column by column. */
117 /* ITEMWISE or COLUMNWISE. */
121 static struct sort_cases_pgm *sort;
123 /* Aggregate variables. */
124 static struct agr_var *agr_first, *agr_next;
126 /* Aggregate dictionary. */
127 static struct dictionary *agr_dict;
129 /* Number of cases passed through aggregation. */
130 static int case_count;
132 /* Last values of the break variables. */
133 static union value *prev_case;
135 /* Buffers for use by the 10x transformation. */
136 static flt64 *buf64_1xx;
137 static struct ccase *buf_1xx;
139 static void initialize_aggregate_info (void);
142 static int parse_aggregate_functions (void);
143 static void free_aggregate_functions (void);
144 static int aggregate_single_case (struct ccase *input, struct ccase *output);
145 static int create_sysfile (void);
147 static trns_proc_func agr_00x_trns_proc, agr_10x_trns_proc;
148 static trns_free_func agr_10x_trns_free;
149 static void agr_00x_end_func (void *aux);
150 static void agr_10x_end_func (void *);
151 static int agr_11x_func (write_case_data);
154 static void debug_print (int flags);
159 /* Parses and executes the AGGREGATE procedure. */
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"))
186 destroy_sort_cases_pgm (sort);
187 dict_destroy (agr_dict);
188 msg (SE, _("%s subcommand given multiple times."),"OUTFILE");
198 outfile = fh_parse_file_handle ();
201 destroy_sort_cases_pgm (sort);
202 dict_destroy (agr_dict);
207 else if (lex_match_id ("MISSING"))
210 if (!lex_match_id ("COLUMNWISE"))
212 destroy_sort_cases_pgm (sort);
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"))
227 destroy_sort_cases_pgm (sort);
228 dict_destroy (agr_dict);
229 msg (SE, _("%s subcommand given multiple times."),"BREAK");
235 sort = parse_sort ();
238 dict_destroy (agr_dict);
245 for (i = 0; i < sort->var_cnt; i++)
249 v = dict_clone_var (agr_dict, sort->vars[i], sort->vars[i]->name);
257 /* Check for proper syntax. */
259 msg (SW, _("BREAK subcommand not specified."));
261 /* Read in the aggregate functions. */
262 if (!parse_aggregate_functions ())
264 free_aggregate_functions ();
265 destroy_sort_cases_pgm (sort);
269 /* Delete documents. */
271 dict_set_documents (agr_dict, NULL);
273 /* Cancel SPLIT FILE. */
274 dict_set_split_vars (agr_dict, NULL, 0);
282 initialize_aggregate_info ();
284 /* How to implement all this... There are three important variables:
285 whether output is going to the active file (0) or a separate file
286 (1); whether the input data is presorted (0) or needs sorting
287 (1); whether there is a temporary transformation (1) or not (0).
288 The eight cases are as follows:
290 000 (0): Pass it through an aggregate transformation that
293 001 (1): Cancel the temporary transformation and handle as 000.
295 010 (2): Set up a SORT CASES and aggregate the output, writing
296 the results to the active file.
298 011 (3): Cancel the temporary transformation and handle as 010.
300 100 (4): Pass it through an aggregate transformation that doesn't
301 modify the data but merely writes it to the output file.
303 101 (5): Handled as 100.
305 110 (6): Set up a SORT CASES and capture the output, aggregate
306 it, write it to the output file without modifying the active
309 111 (7): Handled as 110. */
316 if (sort != NULL && (seen & 4) == 0)
327 sort_cases (sort, 0);
336 struct trns_header *t = xmalloc (sizeof *t);
337 t->proc = agr_00x_trns_proc;
339 add_transformation (t);
342 temp_dict = agr_dict;
347 procedure (NULL, NULL, agr_00x_end_func, NULL);
354 if (!create_sysfile ())
358 struct trns_header *t = xmalloc (sizeof *t);
359 t->proc = agr_10x_trns_proc;
360 t->free = agr_10x_trns_free;
361 add_transformation (t);
363 procedure (NULL, NULL, agr_10x_end_func, NULL);
371 sort_cases (sort, 1);
373 if (!create_sysfile ())
375 read_sort_output (sort, agr_11x_func, NULL);
378 struct ccase *save_temp_case = temp_case;
381 temp_case = save_temp_case;
395 destroy_sort_cases_pgm (sort);
396 free_aggregate_functions ();
403 destroy_sort_cases_pgm (sort);
404 free_aggregate_functions ();
410 /* Create a system file for use in aggregation to an external file,
411 and allocate temporary buffers for writing out cases. */
413 create_sysfile (void)
415 struct sfm_write_info w;
418 w.compress = set_scompression;
419 if (!sfm_write_dictionary (&w))
421 free_aggregate_functions ();
422 destroy_sort_cases_pgm (sort);
423 dict_destroy (agr_dict);
427 buf64_1xx = xmalloc (sizeof *buf64_1xx * w.case_size);
428 buf_1xx = xmalloc (dict_get_case_size (agr_dict));
433 /* Parse all the aggregate functions. */
435 parse_aggregate_functions (void)
437 agr_first = agr_next = NULL;
439 /* Parse everything. */
447 const struct agr_func *function;
452 struct variable **src;
466 /* Parse the list of target variables. */
467 while (!lex_match ('='))
469 int n_dest_prev = n_dest;
471 if (!parse_DATA_LIST_vars (&dest, &n_dest, PV_APPEND | PV_SINGLE | PV_NO_SCRATCH))
474 /* Assign empty labels. */
478 dest_label = xrealloc (dest_label, sizeof *dest_label * n_dest);
479 for (j = n_dest_prev; j < n_dest; j++)
480 dest_label[j] = NULL;
483 if (token == T_STRING)
485 ds_truncate (&tokstr, 120);
486 dest_label[n_dest - 1] = xstrdup (ds_value (&tokstr));
491 /* Get the name of the aggregation function. */
494 lex_error (_("expecting aggregation function"));
499 if (tokid[strlen (tokid) - 1] == '.')
502 tokid[strlen (tokid) - 1] = 0;
505 for (function = agr_func_tab; function->name; function++)
506 if (!strcmp (function->name, tokid))
508 if (NULL == function->name)
510 msg (SE, _("Unknown aggregation function %s."), tokid);
513 func_index = function - agr_func_tab;
516 /* Check for leading lparen. */
517 if (!lex_match ('('))
520 func_index = N_NO_VARS;
521 else if (func_index == NU)
522 func_index = NU_NO_VARS;
525 lex_error (_("expecting `('"));
529 /* Parse list of source variables. */
531 int pv_opts = PV_NO_SCRATCH;
533 if (func_index == SUM || func_index == MEAN || func_index == SD)
534 pv_opts |= PV_NUMERIC;
535 else if (function->n_args)
536 pv_opts |= PV_SAME_TYPE;
538 if (!parse_variables (default_dict, &src, &n_src, pv_opts))
542 /* Parse function arguments, for those functions that
543 require arguments. */
544 if (function->n_args != 0)
545 for (i = 0; i < function->n_args; i++)
550 if (token == T_STRING)
552 arg[i].c = xstrdup (ds_value (&tokstr));
555 else if (token == T_NUM)
560 msg (SE, _("Missing argument %d to %s."), i + 1, function->name);
566 if (type != src[0]->type)
568 msg (SE, _("Arguments to %s must be of same type as "
569 "source variables."),
575 /* Trailing rparen. */
578 lex_error (_("expecting `)'"));
582 /* Now check that the number of source variables match the
583 number of target variables. Do this here because if we
584 do it earlier then the user can get very misleading error
585 messages; i.e., `AGGREGATE x=SUM(y t).' will get this
586 error message when a proper message would be more like
587 `unknown variable t'. */
590 msg (SE, _("Number of source variables (%d) does not match "
591 "number of target variables (%d)."),
597 /* Finally add these to the linked list of aggregation
599 for (i = 0; i < n_dest; i++)
601 struct agr_var *v = xmalloc (sizeof *v);
603 /* Add variable to chain. */
605 agr_next = agr_next->next = v;
607 agr_first = agr_next = v;
608 agr_next->next = NULL;
610 /* Create the target variable in the aggregate
613 struct variable *destvar;
615 agr_next->function = func_index;
621 agr_next->src = src[i];
623 if (src[i]->type == ALPHA)
625 agr_next->function |= FSTRING;
626 agr_next->string = xmalloc (src[i]->width);
629 if (agr_next->src->type == NUMERIC || function->alpha_type == NUMERIC)
632 output_width = agr_next->src->width;
634 if (function->alpha_type == ALPHA)
635 destvar = dict_clone_var (agr_dict, agr_next->src, dest[i]);
638 destvar = dict_create_var (agr_dict, dest[i], output_width);
639 if (output_width == 0)
640 destvar->print = destvar->write = function->format;
641 if (output_width == 0 && dict_get_weight (default_dict) != NULL
642 && (func_index == N || func_index == N_NO_VARS
643 || func_index == NU || func_index == NU_NO_VARS))
645 struct fmt_spec f = {FMT_F, 8, 2};
647 destvar->print = destvar->write = f;
651 agr_next->src = NULL;
652 destvar = dict_create_var (agr_dict, dest[i], 0);
657 msg (SE, _("Variable name %s is not unique within the "
658 "aggregate file dictionary, which contains "
659 "the aggregate variables and the break "
670 destvar->label = dest_label[i];
671 dest_label[i] = NULL;
673 else if (function->alpha_type == ALPHA)
674 destvar->print = destvar->write = function->format;
676 agr_next->dest = destvar;
679 agr_next->include_missing = include_missing;
681 if (agr_next->src != NULL)
685 if (agr_next->src->type == NUMERIC)
686 for (j = 0; j < function->n_args; j++)
687 agr_next->arg[j].f = arg[j].f;
689 for (j = 0; j < function->n_args; j++)
690 agr_next->arg[j].c = xstrdup (arg[j].c);
694 if (src != NULL && src[0]->type == ALPHA)
695 for (i = 0; i < function->n_args; i++)
705 if (!lex_match ('/'))
710 lex_error ("expecting end of command");
716 for (i = 0; i < n_dest; i++)
719 free (dest_label[i]);
725 if (src && n_src && src[0]->type == ALPHA)
726 for (i = 0; i < function->n_args; i++)
737 /* Frees all the state for the AGGREGATE procedure. */
739 free_aggregate_functions (void)
741 struct agr_var *iter, *next;
744 dict_destroy (agr_dict);
745 for (iter = agr_first; iter; iter = next)
749 if (iter->function & FSTRING)
754 n_args = agr_func_tab[iter->function & FUNC].n_args;
755 for (i = 0; i < n_args; i++)
756 free (iter->arg[i].c);
765 static void accumulate_aggregate_info (struct ccase *input);
766 static void dump_aggregate_info (struct ccase *output);
768 /* Processes a single case INPUT for aggregation. If output is
769 warranted, it is written to case OUTPUT, which may be (but need not
770 be) an alias to INPUT. Returns -1 when output is performed, -2
772 /* The code in this function has an eerie similarity to
773 vfm.c:SPLIT_FILE_procfunc()... */
775 aggregate_single_case (struct ccase *input, struct ccase *output)
777 /* The first case always begins a new break group. We also need to
778 preserve the values of the case for later comparison. */
779 if (case_count++ == 0)
786 for (i = 0; i < sort->var_cnt; i++)
787 n_elem += sort->vars[i]->nv;
790 prev_case = xmalloc (sizeof *prev_case * n_elem);
792 /* Copy INPUT into prev_case. */
794 union value *iter = prev_case;
797 for (i = 0; i < sort->var_cnt; i++)
799 struct variable *v = sort->vars[i];
801 if (v->type == NUMERIC)
802 (iter++)->f = input->data[v->fv].f;
805 memcpy (iter->s, input->data[v->fv].s, v->width);
811 accumulate_aggregate_info (input);
816 /* Compare the value of each break variable to the values on the
819 union value *iter = prev_case;
822 for (i = 0; i < sort->var_cnt; i++)
824 struct variable *v = sort->vars[i];
829 if (input->data[v->fv].f != iter->f)
834 if (memcmp (input->data[v->fv].s, iter->s, v->width))
844 accumulate_aggregate_info (input);
849 /* The values of the break variable are different from the values on
850 the previous case. That means that it's time to dump aggregate
852 dump_aggregate_info (output);
853 initialize_aggregate_info ();
854 accumulate_aggregate_info (input);
856 /* Copy INPUT into prev_case. */
858 union value *iter = prev_case;
861 for (i = 0; i < sort->var_cnt; i++)
863 struct variable *v = sort->vars[i];
865 if (v->type == NUMERIC)
866 (iter++)->f = input->data[v->fv].f;
869 memcpy (iter->s, input->data[v->fv].s, v->width);
878 /* Accumulates aggregation data from the case INPUT. */
880 accumulate_aggregate_info (struct ccase *input)
882 struct agr_var *iter;
885 weight = dict_get_case_weight (default_dict, input);
887 for (iter = agr_first; iter; iter = iter->next)
890 union value *v = &input->data[iter->src->fv];
892 if ((!iter->include_missing && is_missing (v, iter->src))
893 || (iter->include_missing && iter->src->type == NUMERIC
896 switch (iter->function)
899 iter->dbl[0] += weight;
909 /* This is horrible. There are too many possibilities. */
910 switch (iter->function)
913 iter->dbl[0] += v->f;
916 iter->dbl[0] += v->f * weight;
917 iter->dbl[1] += weight;
921 double product = v->f * weight;
922 iter->dbl[0] += product;
923 iter->dbl[1] += product * v->f;
924 iter->dbl[2] += weight;
928 iter->dbl[0] = max (iter->dbl[0], v->f);
932 if (memcmp (iter->string, v->s, iter->src->width) < 0)
933 memcpy (iter->string, v->s, iter->src->width);
937 iter->dbl[0] = min (iter->dbl[0], v->f);
941 if (memcmp (iter->string, v->s, iter->src->width) > 0)
942 memcpy (iter->string, v->s, iter->src->width);
947 if (v->f > iter->arg[0].f)
948 iter->dbl[0] += weight;
949 iter->dbl[1] += weight;
953 if (memcmp (iter->arg[0].c, v->s, iter->src->width) < 0)
954 iter->dbl[0] += weight;
955 iter->dbl[1] += weight;
959 if (v->f < iter->arg[0].f)
960 iter->dbl[0] += weight;
961 iter->dbl[1] += weight;
965 if (memcmp (iter->arg[0].c, v->s, iter->src->width) > 0)
966 iter->dbl[0] += weight;
967 iter->dbl[1] += weight;
971 if (iter->arg[0].f <= v->f && v->f <= iter->arg[1].f)
972 iter->dbl[0] += weight;
973 iter->dbl[1] += weight;
977 if (memcmp (iter->arg[0].c, v->s, iter->src->width) <= 0
978 && memcmp (iter->arg[1].c, v->s, iter->src->width) >= 0)
979 iter->dbl[0] += weight;
980 iter->dbl[1] += weight;
984 if (iter->arg[0].f > v->f || v->f > iter->arg[1].f)
985 iter->dbl[0] += weight;
986 iter->dbl[1] += weight;
990 if (memcmp (iter->arg[0].c, v->s, iter->src->width) > 0
991 && memcmp (iter->arg[1].c, v->s, iter->src->width) < 0)
992 iter->dbl[0] += weight;
993 iter->dbl[1] += weight;
996 iter->dbl[0] += weight;
1002 if (iter->int1 == 0)
1004 iter->dbl[0] = v->f;
1008 case FIRST | FSTRING:
1009 if (iter->int1 == 0)
1011 memcpy (iter->string, v->s, iter->src->width);
1016 iter->dbl[0] = v->f;
1019 case LAST | FSTRING:
1020 memcpy (iter->string, v->s, iter->src->width);
1027 switch (iter->function)
1030 iter->dbl[0] += weight;
1041 /* We've come to a record that differs from the previous in one or
1042 more of the break variables. Make an output record from the
1043 accumulated statistics in the OUTPUT case. */
1045 dump_aggregate_info (struct ccase *output)
1047 debug_printf (("(dumping "));
1055 for (i = 0; i < sort->var_cnt; i++)
1056 n_elem += sort->vars[i]->nv;
1058 debug_printf (("n_elem=%d:", n_elem));
1059 memcpy (output->data, prev_case, sizeof (union value) * n_elem);
1065 for (i = agr_first; i; i = i->next)
1067 union value *v = &output->data[i->dest->fv];
1069 debug_printf ((" %d,%d", i->dest->fv, i->dest->nv));
1071 if (missing == COLUMNWISE && i->missing != 0
1072 && (i->function & FUNC) != N && (i->function & FUNC) != NU
1073 && (i->function & FUNC) != NMISS && (i->function & FUNC) != NUMISS)
1075 if (i->function & FSTRING)
1076 memset (v->s, ' ', i->dest->width);
1082 switch (i->function)
1088 v->f = i->dbl[1] != 0.0 ? i->dbl[0] / i->dbl[1] : SYSMIS;
1091 v->f = ((i->dbl[2] > 1.0)
1092 ? calc_stddev (calc_variance (i->dbl, i->dbl[2]))
1097 v->f = i->int1 ? i->dbl[0] : SYSMIS;
1102 memcpy (v->s, i->string, i->dest->width);
1104 memset (v->s, ' ', i->dest->width);
1109 case FOUT | FSTRING:
1110 v->f = i->int2 ? (double) i->int1 / (double) i->int2 : SYSMIS;
1116 v->f = i->dbl[1] ? i->dbl[0] / i->dbl[1] : SYSMIS;
1125 case POUT | FSTRING:
1126 v->f = i->dbl[1] ? i->dbl[0] / i->dbl[1] * 100.0 : SYSMIS;
1136 v->f = i->int1 ? i->dbl[0] : SYSMIS;
1138 case FIRST | FSTRING:
1139 case LAST | FSTRING:
1141 memcpy (v->s, i->string, i->dest->width);
1143 memset (v->s, ' ', i->dest->width);
1162 debug_printf ((") "));
1165 /* Resets the state for all the aggregate functions. */
1167 initialize_aggregate_info (void)
1169 struct agr_var *iter;
1171 for (iter = agr_first; iter; iter = iter->next)
1174 switch (iter->function)
1177 iter->dbl[0] = DBL_MAX;
1180 memset (iter->string, 255, iter->src->width);
1183 iter->dbl[0] = -DBL_MAX;
1186 memset (iter->string, 0, iter->src->width);
1189 iter->dbl[0] = iter->dbl[1] = iter->dbl[2] = 0.0;
1190 iter->int1 = iter->int2 = 0;
1196 /* Aggregate each case as it comes through. Cases which aren't needed
1199 agr_00x_trns_proc (struct trns_header *h UNUSED, struct ccase *c,
1200 int case_num UNUSED)
1202 int code = aggregate_single_case (c, compaction_case);
1203 debug_printf (("%d ", code));
1207 /* Output the last aggregate case. It's okay to call the vfm_sink's
1208 write() method here because end_func is called so soon after all
1209 the cases have been output; very little has been cleaned up at this
1212 agr_00x_end_func (void *aux UNUSED)
1214 /* Ensure that info for the last break group gets written to the
1216 dump_aggregate_info (compaction_case);
1217 vfm_sink->class->write (vfm_sink, temp_case);
1220 /* Transform the aggregate case buf_1xx, in internal format, to system
1221 file format, in buf64_1xx, and write the resultant case to the
1224 write_case_to_sfm (void)
1226 flt64 *p = buf64_1xx;
1229 for (i = 0; i < dict_get_var_cnt (agr_dict); i++)
1231 struct variable *v = dict_get_var (agr_dict, i);
1233 if (v->type == NUMERIC)
1235 double src = buf_1xx->data[v->fv].f;
1243 memcpy (p, buf_1xx->data[v->fv].s, v->width);
1244 memset (&((char *) p)[v->width], ' ',
1245 REM_RND_UP (v->width, sizeof (flt64)));
1246 p += DIV_RND_UP (v->width, sizeof (flt64));
1250 sfm_write_case (outfile, buf64_1xx, p - buf64_1xx);
1253 /* Aggregate the current case and output it if we passed a
1256 agr_10x_trns_proc (struct trns_header *h UNUSED, struct ccase *c,
1257 int case_num UNUSED)
1259 int code = aggregate_single_case (c, buf_1xx);
1261 assert (code == -2 || code == -1);
1263 write_case_to_sfm ();
1267 /* Close the system file now that we're done with it. */
1269 agr_10x_trns_free (struct trns_header *h UNUSED)
1271 fh_close_handle (outfile);
1274 /* Ensure that info for the last break group gets written to the
1277 agr_10x_end_func (void *aux UNUSED)
1279 dump_aggregate_info (buf_1xx);
1280 write_case_to_sfm ();
1283 /* When called with temp_case non-NULL (the normal case), runs the
1284 case through the aggregater and outputs it to the system file if
1285 appropriate. If temp_case is NULL, finishes up writing the last
1286 case if necessary. */
1288 agr_11x_func (write_case_data wc_data UNUSED)
1290 if (temp_case != NULL)
1292 int code = aggregate_single_case (temp_case, buf_1xx);
1294 assert (code == -2 || code == -1);
1296 write_case_to_sfm ();
1302 dump_aggregate_info (buf_1xx);
1303 write_case_to_sfm ();
1305 fh_close_handle (outfile);
1312 /* Print out useful debugging information. */
1314 debug_print (int flags)
1316 printf ("AGGREGATE\n /OUTFILE=%s\n",
1317 outfile ? fh_handle_filename (outfile) : "*");
1319 if (missing == COLUMNWISE)
1320 puts (" /MISSING=COLUMNWISE");
1323 puts (" /DOCUMENT");
1325 puts (" /PRESORTED");
1330 printf (" /BREAK=");
1331 for (i = 0; i < sort->var_cnt; i++)
1332 printf ("%s(%c) ", sort->vars[i]->name,
1333 sort->vars[i]->p.srt.order == SRT_ASCEND ? 'A' : 'D');
1334 putc ('\n', stdout);
1338 struct agr_var *iter;
1340 for (iter = agr_first; iter; iter = iter->next)
1342 struct agr_func *f = &agr_func_tab[iter->function & FUNC];
1344 printf (" /%s", iter->dest->name);
1345 if (iter->dest->label)
1346 printf ("'%s'", iter->dest->label);
1347 printf ("=%s(%s", f->name, iter->src->name);
1352 for (i = 0; i < f->n_args; i++)
1355 if (iter->src->type == NUMERIC)
1356 printf ("%g", iter->arg[i].f);
1358 printf ("%.*s", iter->src->width, iter->arg[i].c);
1366 #endif /* DEBUGGING */