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., 51 Franklin Street, Fifth Floor, Boston, MA
26 #include "dictionary.h"
33 #define _(msgid) gettext (msgid)
35 /* Implementation details:
37 The S?SS manuals do not specify the order that COUNT subcommands are
38 performed in. Experiments, however, have shown that they are performed
39 in the order that they are specified in, rather than simultaneously.
40 So, with the two variables A and B, and the two cases,
46 the command COUNT A=A B (1) / B=A B (2) will produce the following
53 rather than the results that would be produced if subcommands were
60 Perhaps simultaneity could be implemented as an option. On the
61 other hand, what good are the above commands? */
67 CNT_ERROR, /* Invalid value. */
68 CNT_SINGLE, /* Single value. */
69 CNT_HIGH, /* x >= a. */
70 CNT_LOW, /* x <= a. */
71 CNT_RANGE, /* a <= x <= b. */
72 CNT_ANY, /* Count any. */
73 CNT_SENTINEL /* List terminator. */
90 struct counting *next;
92 /* variables to count */
97 int missing; /* (numeric only)
98 0=don't count missing,
100 2=count system- and user-missing */
101 union /* Criterion values. */
111 struct cnt_var_info *next;
113 struct variable *d; /* Destination variable. */
114 char n[LONG_NAME_LEN + 1]; /* Name of dest var. */
116 struct counting *c; /* The counting specifications. */
121 struct trns_header h;
122 struct cnt_var_info *specs;
127 static trns_proc_func count_trns_proc;
128 static trns_free_func count_trns_free;
130 static int parse_numeric_criteria (struct counting *);
131 static int parse_string_criteria (struct counting *);
136 struct cnt_var_info *cnt; /* Specification currently being parsed. */
137 struct counting *c; /* Counting currently being parsed. */
138 int ret; /* Return value from parsing function. */
139 struct count_trns *trns; /* Transformation. */
140 struct cnt_var_info *head; /* First counting in chain. */
142 /* Parses each slash-delimited specification. */
143 head = cnt = xmalloc (sizeof *cnt);
146 /* Initialize this struct cnt_var_info to ensure proper cleanup. */
151 /* Get destination variable, or at least its name. */
152 if (!lex_force_id ())
154 cnt->d = dict_lookup_var (default_dict, tokid);
157 if (cnt->d->type == ALPHA)
159 msg (SE, _("Destination cannot be a string variable."));
164 str_copy_trunc (cnt->n, sizeof cnt->n, tokid);
167 if (!lex_force_match ('='))
170 c = cnt->c = xmalloc (sizeof *c);
175 if (!parse_variables (default_dict, &c->v, &c->n,
176 PV_DUPLICATE | PV_SAME_TYPE))
179 if (!lex_force_match ('('))
182 ret = (c->v[0]->type == NUMERIC
183 ? parse_numeric_criteria
184 : parse_string_criteria) (c);
188 if (token == '/' || token == '.')
191 c = c->next = xmalloc (sizeof *c);
197 if (!lex_force_match ('/'))
199 cnt = cnt->next = xmalloc (sizeof *cnt);
202 /* Create all the nonexistent destination variables. */
203 for (cnt = head; cnt; cnt = cnt->next)
206 /* It's valid, though motivationally questionable, to count to
207 the same dest var more than once. */
208 cnt->d = dict_lookup_var (default_dict, cnt->n);
211 cnt->d = dict_create_var_assert (default_dict, cnt->n, 0);
214 trns = xmalloc (sizeof *trns);
215 trns->h.proc = count_trns_proc;
216 trns->h.free = count_trns_free;
218 add_transformation ((struct trns_header *) trns);
226 count_trns_free ((struct trns_header *) & t);
231 /* Parses a set of numeric criterion values. */
233 parse_numeric_criteria (struct counting * c)
246 c->crit.n = xrealloc (c->crit.n, m * sizeof (struct cnt_num));
249 cur = &c->crit.n[n++];
250 if (lex_is_number ())
254 if (lex_match_id ("THRU"))
256 if (lex_is_number ())
258 if (!lex_force_num ())
261 cur->type = CNT_RANGE;
266 msg (SE, _("%g THRU %g is not a valid range. The "
267 "number following THRU must be at least "
268 "as big as the number preceding THRU."),
273 else if (lex_match_id ("HI") || lex_match_id ("HIGHEST"))
274 cur->type = CNT_HIGH;
282 cur->type = CNT_SINGLE;
284 else if (lex_match_id ("LO") || lex_match_id ("LOWEST"))
286 if (!lex_force_match_id ("THRU"))
288 if (lex_is_number ())
294 else if (lex_match_id ("HI") || lex_match_id ("HIGHEST"))
302 else if (lex_match_id ("SYSMIS"))
307 else if (lex_match_id ("MISSING"))
320 c->crit.n[n].type = CNT_SENTINEL;
324 /* Parses a set of string criteria values. The skeleton is the same
325 as parse_numeric_criteria(). */
327 parse_string_criteria (struct counting * c)
336 for (i = 0; i < c->n; i++)
337 if (c->v[i]->width > len)
338 len = c->v[i]->width;
347 c->crit.n = xrealloc (c->crit.n, m * sizeof (struct cnt_str));
350 if (!lex_force_string ())
352 cur = &c->crit.s[n++];
353 cur->type = CNT_SINGLE;
354 cur->s = malloc (len + 1);
355 str_copy_rpad (cur->s, len + 1, ds_c_str (&tokstr));
363 c->crit.s[n].type = CNT_SENTINEL;
367 /* Transformation. */
369 /* Counts the number of values in case C matching counting CNT. */
371 count_numeric (struct counting * cnt, struct ccase * c)
376 for (i = 0; i < cnt->n; i++)
380 /* Extract the variable value and eliminate missing values. */
381 double cmp = case_num (c, cnt->v[i]->fv);
384 if (cnt->missing >= 1)
388 if (cnt->missing >= 2 && is_num_user_missing (cmp, cnt->v[i]))
394 /* Try to find the value in the list. */
395 for (num = cnt->crit.n;; num++)
417 if (cmp < num->a || cmp > num->b)
434 /* Counts the number of values in case C matching counting CNT. */
436 count_string (struct counting * cnt, struct ccase * c)
441 for (i = 0; i < cnt->n; i++)
445 /* Extract the variable value, variable width. */
446 for (str = cnt->crit.s;; str++)
452 if (memcmp (case_str (c, cnt->v[i]->fv), str->s,
467 /* Performs the COUNT transformation T on case C. */
469 count_trns_proc (struct trns_header * trns, struct ccase * c,
472 struct cnt_var_info *info;
473 struct counting *cnt;
476 for (info = ((struct count_trns *) trns)->specs; info; info = info->next)
479 for (cnt = info->c; cnt; cnt = cnt->next)
480 if (cnt->v[0]->type == NUMERIC)
481 counter += count_numeric (cnt, c);
483 counter += count_string (cnt, c);
484 case_data_rw (c, info->d->fv)->f = counter;
489 /* Destroys all dynamic data structures associated with T. */
491 count_trns_free (struct trns_header * t)
493 struct cnt_var_info *iter, *next;
495 for (iter = ((struct count_trns *) t)->specs; iter; iter = next)
497 struct counting *i, *n;
499 for (i = iter->c; i; i = n)
503 if (i->v[0]->type == NUMERIC)
509 for (s = i->crit.s; s->type != CNT_SENTINEL; s++)