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 (void);
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, _("OUTFILE specified multiple times."));
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, _("BREAK specified multiple times."));
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);
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);
372 if (!create_sysfile ())
374 read_sort_output (agr_11x_func);
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 (sizeof (struct ccase)
428 + (sizeof (union value)
429 * (dict_get_value_cnt (agr_dict) - 1)));
434 /* Parse all the aggregate functions. */
436 parse_aggregate_functions (void)
438 agr_first = agr_next = NULL;
440 /* Parse everything. */
448 struct agr_func *function;
453 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 "
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 < nv_sort; i++)
786 n_elem += v_sort[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 < nv_sort; i++)
798 struct variable *v = v_sort[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 < nv_sort; i++)
823 struct variable *v = v_sort[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 < nv_sort; i++)
862 struct variable *v = v_sort[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 < nv_sort; i++)
1055 n_elem += v_sort[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)
1200 int code = aggregate_single_case (c, compaction_case);
1201 debug_printf (("%d ", code));
1205 /* Output the last aggregate case. It's okay to call the vfm_sink's
1206 write() method here because end_func is called so soon after all
1207 the cases have been output; very little has been cleaned up at this
1210 agr_00x_end_func (void)
1212 /* Ensure that info for the last break group gets written to the
1214 dump_aggregate_info (compaction_case);
1215 vfm_sink_info.ncases++;
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)
1257 int code = aggregate_single_case (c, buf_1xx);
1259 assert (code == -2 || code == -1);
1261 write_case_to_sfm ();
1265 /* Close the system file now that we're done with it. */
1267 agr_10x_trns_free (struct trns_header *h unused)
1269 fh_close_handle (outfile);
1272 /* Ensure that info for the last break group gets written to the
1275 agr_10x_end_func (void)
1277 dump_aggregate_info (buf_1xx);
1278 write_case_to_sfm ();
1281 /* When called with temp_case non-NULL (the normal case), runs the
1282 case through the aggregater and outputs it to the system file if
1283 appropriate. If temp_case is NULL, finishes up writing the last
1284 case if necessary. */
1288 if (temp_case != NULL)
1290 int code = aggregate_single_case (temp_case, buf_1xx);
1292 assert (code == -2 || code == -1);
1294 write_case_to_sfm ();
1300 dump_aggregate_info (buf_1xx);
1301 write_case_to_sfm ();
1303 fh_close_handle (outfile);
1310 /* Print out useful debugging information. */
1312 debug_print (int flags)
1314 printf ("AGGREGATE\n /OUTFILE=%s\n",
1315 outfile ? fh_handle_filename (outfile) : "*");
1317 if (missing == COLUMNWISE)
1318 puts (" /MISSING=COLUMNWISE");
1321 puts (" /DOCUMENT");
1323 puts (" /PRESORTED");
1328 printf (" /BREAK=");
1329 for (i = 0; i < nv_sort; i++)
1330 printf ("%s(%c) ", v_sort[i]->name,
1331 v_sort[i]->p.srt.order == SRT_ASCEND ? 'A' : 'D');
1332 putc ('\n', stdout);
1336 struct agr_var *iter;
1338 for (iter = agr_first; iter; iter = iter->next)
1340 struct agr_func *f = &agr_func_tab[iter->function & FUNC];
1342 printf (" /%s", iter->dest->name);
1343 if (iter->dest->label)
1344 printf ("'%s'", iter->dest->label);
1345 printf ("=%s(%s", f->name, iter->src->name);
1350 for (i = 0; i < f->n_args; i++)
1353 if (iter->src->type == NUMERIC)
1354 printf ("%g", iter->arg[i].f);
1356 printf ("%.*s", iter->src->width, iter->arg[i].c);
1364 #endif /* DEBUGGING */