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
30 /* Implementation details:
32 The S?SS manuals do not specify the order that COUNT subcommands are
33 performed in. Experiments, however, have shown that they are performed
34 in the order that they are specified in, rather than simultaneously.
35 So, with the two variables A and B, and the two cases,
41 the command COUNT A=A B (1) / B=A B (2) will produce the following
48 rather than the results that would be produced if subcommands were
55 Perhaps simultaneity could be implemented as an option. On the
56 other hand, what good are the above commands? */
62 CNT_ERROR, /* Invalid value. */
63 CNT_SINGLE, /* Single value. */
64 CNT_HIGH, /* x >= a. */
65 CNT_LOW, /* x <= a. */
66 CNT_RANGE, /* a <= x <= b. */
67 CNT_ANY, /* Count any. */
68 CNT_SENTINEL /* List terminator. */
85 struct counting *next;
87 /* variables to count */
92 int missing; /* (numeric only)
93 0=don't count missing,
95 2=count system- and user-missing */
96 union /* Criterion values. */
106 struct cnt_var_info *next;
108 struct variable *d; /* Destination variable. */
109 char n[9]; /* Name of dest var. */
111 struct counting *c; /* The counting specifications. */
116 struct trns_header h;
117 struct cnt_var_info *specs;
122 static trns_proc_func count_trns_proc;
123 static trns_free_func count_trns_free;
125 static int parse_numeric_criteria (struct counting *);
126 static int parse_string_criteria (struct counting *);
131 struct cnt_var_info *cnt; /* Specification currently being parsed. */
132 struct counting *c; /* Counting currently being parsed. */
133 int ret; /* Return value from parsing function. */
134 struct count_trns *trns; /* Transformation. */
135 struct cnt_var_info *head; /* First counting in chain. */
137 lex_match_id ("COUNT");
139 /* Parses each slash-delimited specification. */
140 head = cnt = xmalloc (sizeof *cnt);
143 /* Initialize this struct cnt_var_info to ensure proper cleanup. */
148 /* Get destination struct variable, or at least its name. */
149 if (!lex_force_id ())
151 cnt->d = dict_lookup_var (default_dict, tokid);
154 if (cnt->d->type == ALPHA)
156 msg (SE, _("Destination cannot be a string variable."));
161 strcpy (cnt->n, tokid);
164 if (!lex_force_match ('='))
167 c = cnt->c = xmalloc (sizeof *c);
172 if (!parse_variables (default_dict, &c->v, &c->n,
173 PV_DUPLICATE | PV_SAME_TYPE))
176 if (!lex_force_match ('('))
179 ret = (c->v[0]->type == NUMERIC
180 ? parse_numeric_criteria
181 : parse_string_criteria) (c);
185 if (token == '/' || token == '.')
188 c = c->next = xmalloc (sizeof *c);
194 if (!lex_force_match ('/'))
196 cnt = cnt->next = xmalloc (sizeof *cnt);
199 /* Create all the nonexistent destination variables. */
200 for (cnt = head; cnt; cnt = cnt->next)
203 /* It's valid, though motivationally questionable, to count to
204 the same dest var more than once. */
205 cnt->d = dict_lookup_var (default_dict, cnt->n);
208 cnt->d = dict_create_var_assert (default_dict, cnt->n, 0);
211 trns = xmalloc (sizeof *trns);
212 trns->h.proc = count_trns_proc;
213 trns->h.free = count_trns_free;
215 add_transformation ((struct trns_header *) trns);
223 count_trns_free ((struct trns_header *) & t);
228 /* Parses a set of numeric criterion values. */
230 parse_numeric_criteria (struct counting * c)
243 c->crit.n = xrealloc (c->crit.n, m * sizeof (struct cnt_num));
246 cur = &c->crit.n[n++];
251 if (lex_match_id ("THRU"))
255 if (!lex_force_num ())
258 cur->type = CNT_RANGE;
263 msg (SE, _("%g THRU %g is not a valid range. The "
264 "number following THRU must be at least "
265 "as big as the number preceding THRU."),
270 else if (lex_match_id ("HI") || lex_match_id ("HIGHEST"))
271 cur->type = CNT_HIGH;
279 cur->type = CNT_SINGLE;
281 else if (lex_match_id ("LO") || lex_match_id ("LOWEST"))
283 if (!lex_force_match_id ("THRU"))
291 else if (lex_match_id ("HI") || lex_match_id ("HIGHEST"))
299 else if (lex_match_id ("SYSMIS"))
304 else if (lex_match_id ("MISSING"))
317 c->crit.n[n].type = CNT_SENTINEL;
321 /* Parses a set of string criteria values. The skeleton is the same
322 as parse_numeric_criteria(). */
324 parse_string_criteria (struct counting * c)
333 for (i = 0; i < c->n; i++)
334 if (c->v[i]->width > len)
335 len = c->v[i]->width;
344 c->crit.n = xrealloc (c->crit.n, m * sizeof (struct cnt_str));
347 if (!lex_force_string ())
349 cur = &c->crit.s[n++];
350 cur->type = CNT_SINGLE;
351 cur->s = malloc (len + 1);
352 st_pad_copy (cur->s, ds_value (&tokstr), len + 1);
360 c->crit.s[n].type = CNT_SENTINEL;
364 /* Transformation. */
366 /* Counts the number of values in case C matching counting CNT. */
368 count_numeric (struct counting * cnt, struct ccase * c)
377 for (i = 0; i < cnt->n; i++)
379 /* Extract the variable value and eliminate missing values. */
380 cmp = c->data[cnt->v[i]->fv].f;
383 if (cnt->missing >= 1)
387 if (cnt->missing >= 2 && is_num_user_missing (cmp, cnt->v[i]))
393 /* Try to find the value in the list. */
394 for (num = cnt->crit.n;; num++)
416 if (cmp < num->a || cmp > num->b)
433 /* Counts the number of values in case C matching counting CNT. */
435 count_string (struct counting * cnt, struct ccase * c)
446 for (i = 0; i < cnt->n; i++)
448 /* Extract the variable value, variable width. */
449 cmp = c->data[cnt->v[i]->fv].s;
450 len = cnt->v[i]->width;
452 for (str = cnt->crit.s;; str++)
458 if (memcmp (cmp, str->s, len))
472 /* Performs the COUNT transformation T on case C. */
474 count_trns_proc (struct trns_header * trns, struct ccase * c,
477 struct cnt_var_info *info;
478 struct counting *cnt;
481 for (info = ((struct count_trns *) trns)->specs; info; info = info->next)
484 for (cnt = info->c; cnt; cnt = cnt->next)
485 if (cnt->v[0]->type == NUMERIC)
486 counter += count_numeric (cnt, c);
488 counter += count_string (cnt, c);
489 c->data[info->d->fv].f = counter;
494 /* Destroys all dynamic data structures associated with T. */
496 count_trns_free (struct trns_header * t)
498 struct cnt_var_info *iter, *next;
500 for (iter = ((struct count_trns *) t)->specs; iter; iter = next)
502 struct counting *i, *n;
504 for (i = iter->c; i; i = n)
508 if (i->v[0]->type == NUMERIC)
514 for (s = i->crit.s; s->type != CNT_SENTINEL; s++)