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 (const struct ccase *input,
145 struct ccase *output);
146 static int create_sysfile (void);
148 static trns_proc_func agr_00x_trns_proc, agr_10x_trns_proc;
149 static trns_free_func agr_10x_trns_free;
150 static void agr_00x_end_func (void *aux);
151 static void agr_10x_end_func (void *);
152 static read_sort_output_func agr_11x_read;
153 static void agr_11x_finish (void);
156 static void debug_print (int flags);
161 /* Parses and executes the AGGREGATE procedure. */
165 /* Have we seen these subcommands? */
173 agr_dict = dict_create ();
174 dict_set_label (agr_dict, dict_get_label (default_dict));
175 dict_set_documents (agr_dict, dict_get_documents (default_dict));
177 lex_match_id ("AGGREGATE");
179 /* Read most of the subcommands. */
184 if (lex_match_id ("OUTFILE"))
188 destroy_sort_cases_pgm (sort);
189 dict_destroy (agr_dict);
190 msg (SE, _("%s subcommand given multiple times."),"OUTFILE");
200 outfile = fh_parse_file_handle ();
203 destroy_sort_cases_pgm (sort);
204 dict_destroy (agr_dict);
209 else if (lex_match_id ("MISSING"))
212 if (!lex_match_id ("COLUMNWISE"))
214 destroy_sort_cases_pgm (sort);
215 dict_destroy (agr_dict);
216 lex_error (_("while expecting COLUMNWISE"));
219 missing = COLUMNWISE;
221 else if (lex_match_id ("DOCUMENT"))
223 else if (lex_match_id ("PRESORTED"))
225 else if (lex_match_id ("BREAK"))
229 destroy_sort_cases_pgm (sort);
230 dict_destroy (agr_dict);
231 msg (SE, _("%s subcommand given multiple times."),"BREAK");
237 sort = parse_sort ();
240 dict_destroy (agr_dict);
247 for (i = 0; i < sort->var_cnt; i++)
251 v = dict_clone_var (agr_dict, sort->vars[i], sort->vars[i]->name);
259 /* Check for proper syntax. */
261 msg (SW, _("BREAK subcommand not specified."));
263 /* Read in the aggregate functions. */
264 if (!parse_aggregate_functions ())
266 free_aggregate_functions ();
267 destroy_sort_cases_pgm (sort);
271 /* Delete documents. */
273 dict_set_documents (agr_dict, NULL);
275 /* Cancel SPLIT FILE. */
276 dict_set_split_vars (agr_dict, NULL, 0);
284 initialize_aggregate_info ();
286 /* How to implement all this... There are three important variables:
287 whether output is going to the active file (0) or a separate file
288 (1); whether the input data is presorted (0) or needs sorting
289 (1); whether there is a temporary transformation (1) or not (0).
290 The eight cases are as follows:
292 000 (0): Pass it through an aggregate transformation that
295 001 (1): Cancel the temporary transformation and handle as 000.
297 010 (2): Set up a SORT CASES and aggregate the output, writing
298 the results to the active file.
300 011 (3): Cancel the temporary transformation and handle as 010.
302 100 (4): Pass it through an aggregate transformation that doesn't
303 modify the data but merely writes it to the output file.
305 101 (5): Handled as 100.
307 110 (6): Set up a SORT CASES and capture the output, aggregate
308 it, write it to the output file without modifying the active
311 111 (7): Handled as 110. */
318 if (sort != NULL && (seen & 4) == 0)
329 sort_cases (sort, 0);
338 struct trns_header *t = xmalloc (sizeof *t);
339 t->proc = agr_00x_trns_proc;
341 add_transformation (t);
344 temp_dict = agr_dict;
349 procedure (NULL, NULL, agr_00x_end_func, NULL);
356 if (!create_sysfile ())
360 struct trns_header *t = xmalloc (sizeof *t);
361 t->proc = agr_10x_trns_proc;
362 t->free = agr_10x_trns_free;
363 add_transformation (t);
365 procedure (NULL, NULL, agr_10x_end_func, NULL);
373 sort_cases (sort, 1);
375 if (!create_sysfile ())
377 read_sort_output (sort, agr_11x_read, NULL);
391 destroy_sort_cases_pgm (sort);
392 free_aggregate_functions ();
399 destroy_sort_cases_pgm (sort);
400 free_aggregate_functions ();
406 /* Create a system file for use in aggregation to an external file,
407 and allocate temporary buffers for writing out cases. */
409 create_sysfile (void)
411 struct sfm_write_info w;
414 w.compress = set_scompression;
415 if (!sfm_write_dictionary (&w))
417 free_aggregate_functions ();
418 destroy_sort_cases_pgm (sort);
419 dict_destroy (agr_dict);
423 buf64_1xx = xmalloc (sizeof *buf64_1xx * w.case_size);
424 buf_1xx = xmalloc (dict_get_case_size (agr_dict));
429 /* Parse all the aggregate functions. */
431 parse_aggregate_functions (void)
433 agr_first = agr_next = NULL;
435 /* Parse everything. */
443 const struct agr_func *function;
448 struct variable **src;
462 /* Parse the list of target variables. */
463 while (!lex_match ('='))
465 int n_dest_prev = n_dest;
467 if (!parse_DATA_LIST_vars (&dest, &n_dest, PV_APPEND | PV_SINGLE | PV_NO_SCRATCH))
470 /* Assign empty labels. */
474 dest_label = xrealloc (dest_label, sizeof *dest_label * n_dest);
475 for (j = n_dest_prev; j < n_dest; j++)
476 dest_label[j] = NULL;
479 if (token == T_STRING)
481 ds_truncate (&tokstr, 120);
482 dest_label[n_dest - 1] = xstrdup (ds_value (&tokstr));
487 /* Get the name of the aggregation function. */
490 lex_error (_("expecting aggregation function"));
495 if (tokid[strlen (tokid) - 1] == '.')
498 tokid[strlen (tokid) - 1] = 0;
501 for (function = agr_func_tab; function->name; function++)
502 if (!strcmp (function->name, tokid))
504 if (NULL == function->name)
506 msg (SE, _("Unknown aggregation function %s."), tokid);
509 func_index = function - agr_func_tab;
512 /* Check for leading lparen. */
513 if (!lex_match ('('))
516 func_index = N_NO_VARS;
517 else if (func_index == NU)
518 func_index = NU_NO_VARS;
521 lex_error (_("expecting `('"));
525 /* Parse list of source variables. */
527 int pv_opts = PV_NO_SCRATCH;
529 if (func_index == SUM || func_index == MEAN || func_index == SD)
530 pv_opts |= PV_NUMERIC;
531 else if (function->n_args)
532 pv_opts |= PV_SAME_TYPE;
534 if (!parse_variables (default_dict, &src, &n_src, pv_opts))
538 /* Parse function arguments, for those functions that
539 require arguments. */
540 if (function->n_args != 0)
541 for (i = 0; i < function->n_args; i++)
546 if (token == T_STRING)
548 arg[i].c = xstrdup (ds_value (&tokstr));
551 else if (token == T_NUM)
556 msg (SE, _("Missing argument %d to %s."), i + 1, function->name);
562 if (type != src[0]->type)
564 msg (SE, _("Arguments to %s must be of same type as "
565 "source variables."),
571 /* Trailing rparen. */
574 lex_error (_("expecting `)'"));
578 /* Now check that the number of source variables match the
579 number of target variables. Do this here because if we
580 do it earlier then the user can get very misleading error
581 messages; i.e., `AGGREGATE x=SUM(y t).' will get this
582 error message when a proper message would be more like
583 `unknown variable t'. */
586 msg (SE, _("Number of source variables (%d) does not match "
587 "number of target variables (%d)."),
593 /* Finally add these to the linked list of aggregation
595 for (i = 0; i < n_dest; i++)
597 struct agr_var *v = xmalloc (sizeof *v);
599 /* Add variable to chain. */
601 agr_next = agr_next->next = v;
603 agr_first = agr_next = v;
604 agr_next->next = NULL;
606 /* Create the target variable in the aggregate
609 struct variable *destvar;
611 agr_next->function = func_index;
617 agr_next->src = src[i];
619 if (src[i]->type == ALPHA)
621 agr_next->function |= FSTRING;
622 agr_next->string = xmalloc (src[i]->width);
625 if (agr_next->src->type == NUMERIC || function->alpha_type == NUMERIC)
628 output_width = agr_next->src->width;
630 if (function->alpha_type == ALPHA)
631 destvar = dict_clone_var (agr_dict, agr_next->src, dest[i]);
634 destvar = dict_create_var (agr_dict, dest[i], output_width);
635 if (output_width == 0)
636 destvar->print = destvar->write = function->format;
637 if (output_width == 0 && dict_get_weight (default_dict) != NULL
638 && (func_index == N || func_index == N_NO_VARS
639 || func_index == NU || func_index == NU_NO_VARS))
641 struct fmt_spec f = {FMT_F, 8, 2};
643 destvar->print = destvar->write = f;
647 agr_next->src = NULL;
648 destvar = dict_create_var (agr_dict, dest[i], 0);
653 msg (SE, _("Variable name %s is not unique within the "
654 "aggregate file dictionary, which contains "
655 "the aggregate variables and the break "
666 destvar->label = dest_label[i];
667 dest_label[i] = NULL;
669 else if (function->alpha_type == ALPHA)
670 destvar->print = destvar->write = function->format;
672 agr_next->dest = destvar;
675 agr_next->include_missing = include_missing;
677 if (agr_next->src != NULL)
681 if (agr_next->src->type == NUMERIC)
682 for (j = 0; j < function->n_args; j++)
683 agr_next->arg[j].f = arg[j].f;
685 for (j = 0; j < function->n_args; j++)
686 agr_next->arg[j].c = xstrdup (arg[j].c);
690 if (src != NULL && src[0]->type == ALPHA)
691 for (i = 0; i < function->n_args; i++)
701 if (!lex_match ('/'))
706 lex_error ("expecting end of command");
712 for (i = 0; i < n_dest; i++)
715 free (dest_label[i]);
721 if (src && n_src && src[0]->type == ALPHA)
722 for (i = 0; i < function->n_args; i++)
733 /* Frees all the state for the AGGREGATE procedure. */
735 free_aggregate_functions (void)
737 struct agr_var *iter, *next;
740 dict_destroy (agr_dict);
741 for (iter = agr_first; iter; iter = next)
745 if (iter->function & FSTRING)
750 n_args = agr_func_tab[iter->function & FUNC].n_args;
751 for (i = 0; i < n_args; i++)
752 free (iter->arg[i].c);
761 static void accumulate_aggregate_info (const struct ccase *input);
762 static void dump_aggregate_info (struct ccase *output);
764 /* Processes a single case INPUT for aggregation. If output is
765 warranted, it is written to case OUTPUT, which may be (but need not
766 be) an alias to INPUT. Returns -1 when output is performed, -2
768 /* The code in this function has an eerie similarity to
769 vfm.c:SPLIT_FILE_procfunc()... */
771 aggregate_single_case (const struct ccase *input, struct ccase *output)
773 /* The first case always begins a new break group. We also need to
774 preserve the values of the case for later comparison. */
775 if (case_count++ == 0)
782 for (i = 0; i < sort->var_cnt; i++)
783 n_elem += sort->vars[i]->nv;
786 prev_case = xmalloc (sizeof *prev_case * n_elem);
788 /* Copy INPUT into prev_case. */
790 union value *iter = prev_case;
793 for (i = 0; i < sort->var_cnt; i++)
795 struct variable *v = sort->vars[i];
797 if (v->type == NUMERIC)
798 (iter++)->f = input->data[v->fv].f;
801 memcpy (iter->s, input->data[v->fv].s, v->width);
807 accumulate_aggregate_info (input);
812 /* Compare the value of each break variable to the values on the
815 union value *iter = prev_case;
818 for (i = 0; i < sort->var_cnt; i++)
820 struct variable *v = sort->vars[i];
825 if (input->data[v->fv].f != iter->f)
830 if (memcmp (input->data[v->fv].s, iter->s, v->width))
840 accumulate_aggregate_info (input);
845 /* The values of the break variable are different from the values on
846 the previous case. That means that it's time to dump aggregate
848 dump_aggregate_info (output);
849 initialize_aggregate_info ();
850 accumulate_aggregate_info (input);
852 /* Copy INPUT into prev_case. */
854 union value *iter = prev_case;
857 for (i = 0; i < sort->var_cnt; i++)
859 struct variable *v = sort->vars[i];
861 if (v->type == NUMERIC)
862 (iter++)->f = input->data[v->fv].f;
865 memcpy (iter->s, input->data[v->fv].s, v->width);
874 /* Accumulates aggregation data from the case INPUT. */
876 accumulate_aggregate_info (const struct ccase *input)
878 struct agr_var *iter;
881 weight = dict_get_case_weight (default_dict, input);
883 for (iter = agr_first; iter; iter = iter->next)
886 const union value *v = &input->data[iter->src->fv];
888 if ((!iter->include_missing && is_missing (v, iter->src))
889 || (iter->include_missing && iter->src->type == NUMERIC
892 switch (iter->function)
895 iter->dbl[0] += weight;
905 /* This is horrible. There are too many possibilities. */
906 switch (iter->function)
909 iter->dbl[0] += v->f;
912 iter->dbl[0] += v->f * weight;
913 iter->dbl[1] += weight;
917 double product = v->f * weight;
918 iter->dbl[0] += product;
919 iter->dbl[1] += product * v->f;
920 iter->dbl[2] += weight;
924 iter->dbl[0] = max (iter->dbl[0], v->f);
928 if (memcmp (iter->string, v->s, iter->src->width) < 0)
929 memcpy (iter->string, v->s, iter->src->width);
933 iter->dbl[0] = min (iter->dbl[0], v->f);
937 if (memcmp (iter->string, v->s, iter->src->width) > 0)
938 memcpy (iter->string, v->s, iter->src->width);
943 if (v->f > iter->arg[0].f)
944 iter->dbl[0] += weight;
945 iter->dbl[1] += weight;
949 if (memcmp (iter->arg[0].c, v->s, iter->src->width) < 0)
950 iter->dbl[0] += weight;
951 iter->dbl[1] += weight;
955 if (v->f < iter->arg[0].f)
956 iter->dbl[0] += weight;
957 iter->dbl[1] += weight;
961 if (memcmp (iter->arg[0].c, v->s, iter->src->width) > 0)
962 iter->dbl[0] += weight;
963 iter->dbl[1] += weight;
967 if (iter->arg[0].f <= v->f && v->f <= iter->arg[1].f)
968 iter->dbl[0] += weight;
969 iter->dbl[1] += weight;
973 if (memcmp (iter->arg[0].c, v->s, iter->src->width) <= 0
974 && memcmp (iter->arg[1].c, v->s, iter->src->width) >= 0)
975 iter->dbl[0] += weight;
976 iter->dbl[1] += weight;
980 if (iter->arg[0].f > v->f || v->f > iter->arg[1].f)
981 iter->dbl[0] += weight;
982 iter->dbl[1] += weight;
986 if (memcmp (iter->arg[0].c, v->s, iter->src->width) > 0
987 && memcmp (iter->arg[1].c, v->s, iter->src->width) < 0)
988 iter->dbl[0] += weight;
989 iter->dbl[1] += weight;
992 iter->dbl[0] += weight;
1000 iter->dbl[0] = v->f;
1004 case FIRST | FSTRING:
1005 if (iter->int1 == 0)
1007 memcpy (iter->string, v->s, iter->src->width);
1012 iter->dbl[0] = v->f;
1015 case LAST | FSTRING:
1016 memcpy (iter->string, v->s, iter->src->width);
1023 switch (iter->function)
1026 iter->dbl[0] += weight;
1037 /* We've come to a record that differs from the previous in one or
1038 more of the break variables. Make an output record from the
1039 accumulated statistics in the OUTPUT case. */
1041 dump_aggregate_info (struct ccase *output)
1043 debug_printf (("(dumping "));
1051 for (i = 0; i < sort->var_cnt; i++)
1052 n_elem += sort->vars[i]->nv;
1054 debug_printf (("n_elem=%d:", n_elem));
1055 memcpy (output->data, prev_case, sizeof (union value) * n_elem);
1061 for (i = agr_first; i; i = i->next)
1063 union value *v = &output->data[i->dest->fv];
1065 debug_printf ((" %d,%d", i->dest->fv, i->dest->nv));
1067 if (missing == COLUMNWISE && i->missing != 0
1068 && (i->function & FUNC) != N && (i->function & FUNC) != NU
1069 && (i->function & FUNC) != NMISS && (i->function & FUNC) != NUMISS)
1071 if (i->function & FSTRING)
1072 memset (v->s, ' ', i->dest->width);
1078 switch (i->function)
1084 v->f = i->dbl[1] != 0.0 ? i->dbl[0] / i->dbl[1] : SYSMIS;
1087 v->f = ((i->dbl[2] > 1.0)
1088 ? calc_stddev (calc_variance (i->dbl, i->dbl[2]))
1093 v->f = i->int1 ? i->dbl[0] : SYSMIS;
1098 memcpy (v->s, i->string, i->dest->width);
1100 memset (v->s, ' ', i->dest->width);
1105 case FOUT | FSTRING:
1106 v->f = i->int2 ? (double) i->int1 / (double) i->int2 : SYSMIS;
1112 v->f = i->dbl[1] ? i->dbl[0] / i->dbl[1] : SYSMIS;
1121 case POUT | FSTRING:
1122 v->f = i->dbl[1] ? i->dbl[0] / i->dbl[1] * 100.0 : SYSMIS;
1132 v->f = i->int1 ? i->dbl[0] : SYSMIS;
1134 case FIRST | FSTRING:
1135 case LAST | FSTRING:
1137 memcpy (v->s, i->string, i->dest->width);
1139 memset (v->s, ' ', i->dest->width);
1158 debug_printf ((") "));
1161 /* Resets the state for all the aggregate functions. */
1163 initialize_aggregate_info (void)
1165 struct agr_var *iter;
1167 for (iter = agr_first; iter; iter = iter->next)
1170 switch (iter->function)
1173 iter->dbl[0] = DBL_MAX;
1176 memset (iter->string, 255, iter->src->width);
1179 iter->dbl[0] = -DBL_MAX;
1182 memset (iter->string, 0, iter->src->width);
1185 iter->dbl[0] = iter->dbl[1] = iter->dbl[2] = 0.0;
1186 iter->int1 = iter->int2 = 0;
1192 /* Aggregate each case as it comes through. Cases which aren't needed
1195 agr_00x_trns_proc (struct trns_header *h UNUSED, struct ccase *c,
1196 int case_num UNUSED)
1198 int code = aggregate_single_case (c, compaction_case);
1199 debug_printf (("%d ", code));
1203 /* Output the last aggregate case. It's okay to call the vfm_sink's
1204 write() method here because end_func is called so soon after all
1205 the cases have been output; very little has been cleaned up at this
1208 agr_00x_end_func (void *aux UNUSED)
1210 /* Ensure that info for the last break group gets written to the
1212 dump_aggregate_info (compaction_case);
1213 vfm_sink->class->write (vfm_sink, compaction_case);
1216 /* Transform the aggregate case buf_1xx, in internal format, to system
1217 file format, in buf64_1xx, and write the resultant case to the
1220 write_case_to_sfm (void)
1222 flt64 *p = buf64_1xx;
1225 for (i = 0; i < dict_get_var_cnt (agr_dict); i++)
1227 struct variable *v = dict_get_var (agr_dict, i);
1229 if (v->type == NUMERIC)
1231 double src = buf_1xx->data[v->fv].f;
1239 memcpy (p, buf_1xx->data[v->fv].s, v->width);
1240 memset (&((char *) p)[v->width], ' ',
1241 REM_RND_UP (v->width, sizeof (flt64)));
1242 p += DIV_RND_UP (v->width, sizeof (flt64));
1246 sfm_write_case (outfile, buf64_1xx, p - buf64_1xx);
1249 /* Aggregate the current case and output it if we passed a
1252 agr_10x_trns_proc (struct trns_header *h UNUSED, struct ccase *c,
1253 int case_num UNUSED)
1255 int code = aggregate_single_case (c, buf_1xx);
1257 assert (code == -2 || code == -1);
1259 write_case_to_sfm ();
1263 /* Close the system file now that we're done with it. */
1265 agr_10x_trns_free (struct trns_header *h UNUSED)
1267 fh_close_handle (outfile);
1270 /* Ensure that info for the last break group gets written to the
1273 agr_10x_end_func (void *aux UNUSED)
1275 dump_aggregate_info (buf_1xx);
1276 write_case_to_sfm ();
1279 /* Runs case C through the aggregater and outputs it to the
1280 system file if appropriate. */
1282 agr_11x_read (const struct ccase *c, void *aux UNUSED)
1284 int code = aggregate_single_case (c, buf_1xx);
1286 assert (code == -2 || code == -1);
1288 write_case_to_sfm ();
1293 /* Finishes up writing the last case if necessary. */
1295 agr_11x_finish (void)
1299 dump_aggregate_info (buf_1xx);
1300 write_case_to_sfm ();
1302 fh_close_handle (outfile);
1307 /* Print out useful debugging information. */
1309 debug_print (int flags)
1311 printf ("AGGREGATE\n /OUTFILE=%s\n",
1312 outfile ? fh_handle_filename (outfile) : "*");
1314 if (missing == COLUMNWISE)
1315 puts (" /MISSING=COLUMNWISE");
1318 puts (" /DOCUMENT");
1320 puts (" /PRESORTED");
1325 printf (" /BREAK=");
1326 for (i = 0; i < sort->var_cnt; i++)
1327 printf ("%s(%c) ", sort->vars[i]->name,
1328 sort->vars[i]->p.srt.order == SRT_ASCEND ? 'A' : 'D');
1329 putc ('\n', stdout);
1333 struct agr_var *iter;
1335 for (iter = agr_first; iter; iter = iter->next)
1337 struct agr_func *f = &agr_func_tab[iter->function & FUNC];
1339 printf (" /%s", iter->dest->name);
1340 if (iter->dest->label)
1341 printf ("'%s'", iter->dest->label);
1342 printf ("=%s(%s", f->name, iter->src->name);
1347 for (i = 0; i < f->n_args; i++)
1350 if (iter->src->type == NUMERIC)
1351 printf ("%g", iter->arg[i].f);
1353 printf ("%.*s", iter->src->width, iter->arg[i].c);
1361 #endif /* DEBUGGING */