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;
465 /* Parse the list of target variables. */
466 while (!lex_match ('='))
468 int n_dest_prev = n_dest;
470 if (!parse_DATA_LIST_vars (&dest, &n_dest, PV_APPEND | PV_SINGLE | PV_NO_SCRATCH))
473 /* Assign empty labels. */
477 dest_label = xrealloc (dest_label, sizeof *dest_label * n_dest);
478 for (j = n_dest_prev; j < n_dest; j++)
479 dest_label[j] = NULL;
482 if (token == T_STRING)
484 ds_truncate (&tokstr, 120);
485 dest_label[n_dest - 1] = xstrdup (ds_value (&tokstr));
490 /* Get the name of the aggregation function. */
493 lex_error (_("expecting aggregation function"));
498 if (tokid[strlen (tokid) - 1] == '.')
501 tokid[strlen (tokid) - 1] = 0;
504 for (function = agr_func_tab; function->name; function++)
505 if (!strcmp (function->name, tokid))
507 if (NULL == function->name)
509 msg (SE, _("Unknown aggregation function %s."), tokid);
512 func_index = function - agr_func_tab;
515 /* Check for leading lparen. */
516 if (!lex_match ('('))
519 func_index = N_NO_VARS;
520 else if (func_index == NU)
521 func_index = NU_NO_VARS;
524 lex_error (_("expecting `('"));
528 /* Parse list of source variables. */
530 int pv_opts = PV_NO_SCRATCH;
532 if (func_index == SUM || func_index == MEAN || func_index == SD)
533 pv_opts |= PV_NUMERIC;
534 else if (function->n_args)
535 pv_opts |= PV_SAME_TYPE;
537 if (!parse_variables (default_dict, &src, &n_src, pv_opts))
541 /* Parse function arguments, for those functions that
542 require arguments. */
543 if (function->n_args != 0)
544 for (i = 0; i < function->n_args; i++)
549 if (token == T_STRING)
551 arg[i].c = xstrdup (ds_value (&tokstr));
554 else if (token == T_NUM)
559 msg (SE, _("Missing argument %d to %s."), i + 1, function->name);
565 if (type != src[0]->type)
567 msg (SE, _("Arguments to %s must be of same type as "
568 "source variables."),
574 /* Trailing rparen. */
577 lex_error (_("expecting `)'"));
581 /* Now check that the number of source variables match the
582 number of target variables. Do this here because if we
583 do it earlier then the user can get very misleading error
584 messages; i.e., `AGGREGATE x=SUM(y t).' will get this
585 error message when a proper message would be more like
586 `unknown variable t'. */
589 msg (SE, _("Number of source variables (%d) does not match "
590 "number of target variables (%d)."),
596 /* Finally add these to the linked list of aggregation
598 for (i = 0; i < n_dest; i++)
600 struct agr_var *v = xmalloc (sizeof *v);
602 /* Add variable to chain. */
604 agr_next = agr_next->next = v;
606 agr_first = agr_next = v;
607 agr_next->next = NULL;
609 /* Create the target variable in the aggregate
612 struct variable *destvar;
614 agr_next->function = func_index;
620 agr_next->src = src[i];
622 if (src[i]->type == ALPHA)
624 agr_next->function |= FSTRING;
625 agr_next->string = xmalloc (src[i]->width);
628 if (agr_next->src->type == NUMERIC || function->alpha_type == NUMERIC)
631 output_width = agr_next->src->width;
633 if (function->alpha_type == ALPHA)
634 destvar = dict_clone_var (agr_dict, agr_next->src, dest[i]);
637 destvar = dict_create_var (agr_dict, dest[i], output_width);
638 if (output_width == 0)
639 destvar->print = destvar->write = function->format;
640 if (output_width == 0 && dict_get_weight (default_dict) != NULL
641 && (func_index == N || func_index == N_NO_VARS
642 || func_index == NU || func_index == NU_NO_VARS))
644 struct fmt_spec f = {FMT_F, 8, 2};
646 destvar->print = destvar->write = f;
650 agr_next->src = NULL;
651 destvar = dict_create_var (agr_dict, dest[i], 0);
656 msg (SE, _("Variable name %s is not unique within the "
657 "aggregate file dictionary, which contains "
658 "the aggregate variables and the break "
669 destvar->label = dest_label[i];
670 dest_label[i] = NULL;
672 else if (function->alpha_type == ALPHA)
673 destvar->print = destvar->write = function->format;
675 agr_next->dest = destvar;
678 agr_next->include_missing = include_missing;
680 if (agr_next->src != NULL)
684 if (agr_next->src->type == NUMERIC)
685 for (j = 0; j < function->n_args; j++)
686 agr_next->arg[j].f = arg[j].f;
688 for (j = 0; j < function->n_args; j++)
689 agr_next->arg[j].c = xstrdup (arg[j].c);
693 if (src != NULL && src[0]->type == ALPHA)
694 for (i = 0; i < function->n_args; i++)
704 if (!lex_match ('/'))
709 lex_error ("expecting end of command");
715 for (i = 0; i < n_dest; i++)
718 free (dest_label[i]);
724 if (src && n_src && src[0]->type == ALPHA)
725 for (i = 0; i < function->n_args; i++)
736 /* Frees all the state for the AGGREGATE procedure. */
738 free_aggregate_functions (void)
740 struct agr_var *iter, *next;
743 dict_destroy (agr_dict);
744 for (iter = agr_first; iter; iter = next)
748 if (iter->function & FSTRING)
753 n_args = agr_func_tab[iter->function & FUNC].n_args;
754 for (i = 0; i < n_args; i++)
755 free (iter->arg[i].c);
764 static void accumulate_aggregate_info (struct ccase *input);
765 static void dump_aggregate_info (struct ccase *output);
767 /* Processes a single case INPUT for aggregation. If output is
768 warranted, it is written to case OUTPUT, which may be (but need not
769 be) an alias to INPUT. Returns -1 when output is performed, -2
771 /* The code in this function has an eerie similarity to
772 vfm.c:SPLIT_FILE_procfunc()... */
774 aggregate_single_case (struct ccase *input, struct ccase *output)
776 /* The first case always begins a new break group. We also need to
777 preserve the values of the case for later comparison. */
778 if (case_count++ == 0)
785 for (i = 0; i < sort->var_cnt; i++)
786 n_elem += sort->vars[i]->nv;
789 prev_case = xmalloc (sizeof *prev_case * n_elem);
791 /* Copy INPUT into prev_case. */
793 union value *iter = prev_case;
796 for (i = 0; i < sort->var_cnt; i++)
798 struct variable *v = sort->vars[i];
800 if (v->type == NUMERIC)
801 (iter++)->f = input->data[v->fv].f;
804 memcpy (iter->s, input->data[v->fv].s, v->width);
810 accumulate_aggregate_info (input);
815 /* Compare the value of each break variable to the values on the
818 union value *iter = prev_case;
821 for (i = 0; i < sort->var_cnt; i++)
823 struct variable *v = sort->vars[i];
828 if (input->data[v->fv].f != iter->f)
833 if (memcmp (input->data[v->fv].s, iter->s, v->width))
843 accumulate_aggregate_info (input);
848 /* The values of the break variable are different from the values on
849 the previous case. That means that it's time to dump aggregate
851 dump_aggregate_info (output);
852 initialize_aggregate_info ();
853 accumulate_aggregate_info (input);
855 /* Copy INPUT into prev_case. */
857 union value *iter = prev_case;
860 for (i = 0; i < sort->var_cnt; i++)
862 struct variable *v = sort->vars[i];
864 if (v->type == NUMERIC)
865 (iter++)->f = input->data[v->fv].f;
868 memcpy (iter->s, input->data[v->fv].s, v->width);
877 /* Accumulates aggregation data from the case INPUT. */
879 accumulate_aggregate_info (struct ccase *input)
881 struct agr_var *iter;
884 weight = dict_get_case_weight (default_dict, input);
886 for (iter = agr_first; iter; iter = iter->next)
889 union value *v = &input->data[iter->src->fv];
891 if ((!iter->include_missing && is_missing (v, iter->src))
892 || (iter->include_missing && iter->src->type == NUMERIC
895 switch (iter->function)
898 iter->dbl[0] += weight;
908 /* This is horrible. There are too many possibilities. */
909 switch (iter->function)
912 iter->dbl[0] += v->f;
915 iter->dbl[0] += v->f * weight;
916 iter->dbl[1] += weight;
920 double product = v->f * weight;
921 iter->dbl[0] += product;
922 iter->dbl[1] += product * v->f;
923 iter->dbl[2] += weight;
927 iter->dbl[0] = max (iter->dbl[0], v->f);
931 if (memcmp (iter->string, v->s, iter->src->width) < 0)
932 memcpy (iter->string, v->s, iter->src->width);
936 iter->dbl[0] = min (iter->dbl[0], v->f);
940 if (memcmp (iter->string, v->s, iter->src->width) > 0)
941 memcpy (iter->string, v->s, iter->src->width);
946 if (v->f > iter->arg[0].f)
947 iter->dbl[0] += weight;
948 iter->dbl[1] += weight;
952 if (memcmp (iter->arg[0].c, v->s, iter->src->width) < 0)
953 iter->dbl[0] += weight;
954 iter->dbl[1] += weight;
958 if (v->f < iter->arg[0].f)
959 iter->dbl[0] += weight;
960 iter->dbl[1] += weight;
964 if (memcmp (iter->arg[0].c, v->s, iter->src->width) > 0)
965 iter->dbl[0] += weight;
966 iter->dbl[1] += weight;
970 if (iter->arg[0].f <= v->f && v->f <= iter->arg[1].f)
971 iter->dbl[0] += weight;
972 iter->dbl[1] += weight;
976 if (memcmp (iter->arg[0].c, v->s, iter->src->width) <= 0
977 && memcmp (iter->arg[1].c, v->s, iter->src->width) >= 0)
978 iter->dbl[0] += weight;
979 iter->dbl[1] += weight;
983 if (iter->arg[0].f > v->f || v->f > iter->arg[1].f)
984 iter->dbl[0] += weight;
985 iter->dbl[1] += weight;
989 if (memcmp (iter->arg[0].c, v->s, iter->src->width) > 0
990 && memcmp (iter->arg[1].c, v->s, iter->src->width) < 0)
991 iter->dbl[0] += weight;
992 iter->dbl[1] += weight;
995 iter->dbl[0] += weight;
1001 if (iter->int1 == 0)
1003 iter->dbl[0] = v->f;
1007 case FIRST | FSTRING:
1008 if (iter->int1 == 0)
1010 memcpy (iter->string, v->s, iter->src->width);
1015 iter->dbl[0] = v->f;
1018 case LAST | FSTRING:
1019 memcpy (iter->string, v->s, iter->src->width);
1026 switch (iter->function)
1029 iter->dbl[0] += weight;
1040 /* We've come to a record that differs from the previous in one or
1041 more of the break variables. Make an output record from the
1042 accumulated statistics in the OUTPUT case. */
1044 dump_aggregate_info (struct ccase *output)
1046 debug_printf (("(dumping "));
1054 for (i = 0; i < sort->var_cnt; i++)
1055 n_elem += sort->vars[i]->nv;
1057 debug_printf (("n_elem=%d:", n_elem));
1058 memcpy (output->data, prev_case, sizeof (union value) * n_elem);
1064 for (i = agr_first; i; i = i->next)
1066 union value *v = &output->data[i->dest->fv];
1068 debug_printf ((" %d,%d", i->dest->fv, i->dest->nv));
1070 if (missing == COLUMNWISE && i->missing != 0
1071 && (i->function & FUNC) != N && (i->function & FUNC) != NU
1072 && (i->function & FUNC) != NMISS && (i->function & FUNC) != NUMISS)
1074 if (i->function & FSTRING)
1075 memset (v->s, ' ', i->dest->width);
1081 switch (i->function)
1087 v->f = i->dbl[1] != 0.0 ? i->dbl[0] / i->dbl[1] : SYSMIS;
1090 v->f = ((i->dbl[2] > 1.0)
1091 ? calc_stddev (calc_variance (i->dbl, i->dbl[2]))
1096 v->f = i->int1 ? i->dbl[0] : SYSMIS;
1101 memcpy (v->s, i->string, i->dest->width);
1103 memset (v->s, ' ', i->dest->width);
1108 case FOUT | FSTRING:
1109 v->f = i->int2 ? (double) i->int1 / (double) i->int2 : SYSMIS;
1115 v->f = i->dbl[1] ? i->dbl[0] / i->dbl[1] : SYSMIS;
1124 case POUT | FSTRING:
1125 v->f = i->dbl[1] ? i->dbl[0] / i->dbl[1] * 100.0 : SYSMIS;
1135 v->f = i->int1 ? i->dbl[0] : SYSMIS;
1137 case FIRST | FSTRING:
1138 case LAST | FSTRING:
1140 memcpy (v->s, i->string, i->dest->width);
1142 memset (v->s, ' ', i->dest->width);
1161 debug_printf ((") "));
1164 /* Resets the state for all the aggregate functions. */
1166 initialize_aggregate_info (void)
1168 struct agr_var *iter;
1170 for (iter = agr_first; iter; iter = iter->next)
1173 switch (iter->function)
1176 iter->dbl[0] = DBL_MAX;
1179 memset (iter->string, 255, iter->src->width);
1182 iter->dbl[0] = -DBL_MAX;
1185 memset (iter->string, 0, iter->src->width);
1188 iter->dbl[0] = iter->dbl[1] = iter->dbl[2] = 0.0;
1189 iter->int1 = iter->int2 = 0;
1195 /* Aggregate each case as it comes through. Cases which aren't needed
1198 agr_00x_trns_proc (struct trns_header *h UNUSED, struct ccase *c,
1199 int case_num UNUSED)
1201 int code = aggregate_single_case (c, compaction_case);
1202 debug_printf (("%d ", code));
1206 /* Output the last aggregate case. It's okay to call the vfm_sink's
1207 write() method here because end_func is called so soon after all
1208 the cases have been output; very little has been cleaned up at this
1211 agr_00x_end_func (void *aux UNUSED)
1213 /* Ensure that info for the last break group gets written to the
1215 dump_aggregate_info (compaction_case);
1216 vfm_sink->class->write (vfm_sink, temp_case);
1219 /* Transform the aggregate case buf_1xx, in internal format, to system
1220 file format, in buf64_1xx, and write the resultant case to the
1223 write_case_to_sfm (void)
1225 flt64 *p = buf64_1xx;
1228 for (i = 0; i < dict_get_var_cnt (agr_dict); i++)
1230 struct variable *v = dict_get_var (agr_dict, i);
1232 if (v->type == NUMERIC)
1234 double src = buf_1xx->data[v->fv].f;
1242 memcpy (p, buf_1xx->data[v->fv].s, v->width);
1243 memset (&((char *) p)[v->width], ' ',
1244 REM_RND_UP (v->width, sizeof (flt64)));
1245 p += DIV_RND_UP (v->width, sizeof (flt64));
1249 sfm_write_case (outfile, buf64_1xx, p - buf64_1xx);
1252 /* Aggregate the current case and output it if we passed a
1255 agr_10x_trns_proc (struct trns_header *h UNUSED, struct ccase *c,
1256 int case_num UNUSED)
1258 int code = aggregate_single_case (c, buf_1xx);
1260 assert (code == -2 || code == -1);
1262 write_case_to_sfm ();
1266 /* Close the system file now that we're done with it. */
1268 agr_10x_trns_free (struct trns_header *h UNUSED)
1270 fh_close_handle (outfile);
1273 /* Ensure that info for the last break group gets written to the
1276 agr_10x_end_func (void *aux UNUSED)
1278 dump_aggregate_info (buf_1xx);
1279 write_case_to_sfm ();
1282 /* When called with temp_case non-NULL (the normal case), runs the
1283 case through the aggregater and outputs it to the system file if
1284 appropriate. If temp_case is NULL, finishes up writing the last
1285 case if necessary. */
1287 agr_11x_func (write_case_data wc_data UNUSED)
1289 if (temp_case != NULL)
1291 int code = aggregate_single_case (temp_case, buf_1xx);
1293 assert (code == -2 || code == -1);
1295 write_case_to_sfm ();
1301 dump_aggregate_info (buf_1xx);
1302 write_case_to_sfm ();
1304 fh_close_handle (outfile);
1311 /* Print out useful debugging information. */
1313 debug_print (int flags)
1315 printf ("AGGREGATE\n /OUTFILE=%s\n",
1316 outfile ? fh_handle_filename (outfile) : "*");
1318 if (missing == COLUMNWISE)
1319 puts (" /MISSING=COLUMNWISE");
1322 puts (" /DOCUMENT");
1324 puts (" /PRESORTED");
1329 printf (" /BREAK=");
1330 for (i = 0; i < sort->var_cnt; i++)
1331 printf ("%s(%c) ", sort->vars[i]->name,
1332 sort->vars[i]->p.srt.order == SRT_ASCEND ? 'A' : 'D');
1333 putc ('\n', stdout);
1337 struct agr_var *iter;
1339 for (iter = agr_first; iter; iter = iter->next)
1341 struct agr_func *f = &agr_func_tab[iter->function & FUNC];
1343 printf (" /%s", iter->dest->name);
1344 if (iter->dest->label)
1345 printf ("'%s'", iter->dest->label);
1346 printf ("=%s(%s", f->name, iter->src->name);
1351 for (i = 0; i < f->n_args; i++)
1354 if (iter->src->type == NUMERIC)
1355 printf ("%g", iter->arg[i].f);
1357 printf ("%.*s", iter->src->width, iter->arg[i].c);
1365 #endif /* DEBUGGING */