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 "dictionary.h"
32 /* Implementation details:
34 The S?SS manuals do not specify the order that COUNT subcommands are
35 performed in. Experiments, however, have shown that they are performed
36 in the order that they are specified in, rather than simultaneously.
37 So, with the two variables A and B, and the two cases,
43 the command COUNT A=A B (1) / B=A B (2) will produce the following
50 rather than the results that would be produced if subcommands were
57 Perhaps simultaneity could be implemented as an option. On the
58 other hand, what good are the above commands? */
64 CNT_ERROR, /* Invalid value. */
65 CNT_SINGLE, /* Single value. */
66 CNT_HIGH, /* x >= a. */
67 CNT_LOW, /* x <= a. */
68 CNT_RANGE, /* a <= x <= b. */
69 CNT_ANY, /* Count any. */
70 CNT_SENTINEL /* List terminator. */
87 struct counting *next;
89 /* variables to count */
94 int missing; /* (numeric only)
95 0=don't count missing,
97 2=count system- and user-missing */
98 union /* Criterion values. */
108 struct cnt_var_info *next;
110 struct variable *d; /* Destination variable. */
111 char n[9]; /* Name of dest var. */
113 struct counting *c; /* The counting specifications. */
118 struct trns_header h;
119 struct cnt_var_info *specs;
124 static trns_proc_func count_trns_proc;
125 static trns_free_func count_trns_free;
127 static int parse_numeric_criteria (struct counting *);
128 static int parse_string_criteria (struct counting *);
133 struct cnt_var_info *cnt; /* Specification currently being parsed. */
134 struct counting *c; /* Counting currently being parsed. */
135 int ret; /* Return value from parsing function. */
136 struct count_trns *trns; /* Transformation. */
137 struct cnt_var_info *head; /* First counting in chain. */
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++];
247 if (lex_is_number ())
251 if (lex_match_id ("THRU"))
253 if (lex_is_number ())
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"))
285 if (lex_is_number ())
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_c_str (&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)
373 for (i = 0; i < cnt->n; i++)
377 /* Extract the variable value and eliminate missing values. */
378 double cmp = case_num (c, cnt->v[i]->fv);
381 if (cnt->missing >= 1)
385 if (cnt->missing >= 2 && is_num_user_missing (cmp, cnt->v[i]))
391 /* Try to find the value in the list. */
392 for (num = cnt->crit.n;; num++)
414 if (cmp < num->a || cmp > num->b)
431 /* Counts the number of values in case C matching counting CNT. */
433 count_string (struct counting * cnt, struct ccase * c)
438 for (i = 0; i < cnt->n; i++)
442 /* Extract the variable value, variable width. */
443 for (str = cnt->crit.s;; str++)
449 if (memcmp (case_str (c, cnt->v[i]->fv), str->s,
464 /* Performs the COUNT transformation T on case C. */
466 count_trns_proc (struct trns_header * trns, struct ccase * c,
469 struct cnt_var_info *info;
470 struct counting *cnt;
473 for (info = ((struct count_trns *) trns)->specs; info; info = info->next)
476 for (cnt = info->c; cnt; cnt = cnt->next)
477 if (cnt->v[0]->type == NUMERIC)
478 counter += count_numeric (cnt, c);
480 counter += count_string (cnt, c);
481 case_data_rw (c, info->d->fv)->f = counter;
486 /* Destroys all dynamic data structures associated with T. */
488 count_trns_free (struct trns_header * t)
490 struct cnt_var_info *iter, *next;
492 for (iter = ((struct count_trns *) t)->specs; iter; iter = next)
494 struct counting *i, *n;
496 for (i = iter->c; i; i = n)
500 if (i->v[0]->type == NUMERIC)
506 for (s = i->crit.s; s->type != CNT_SENTINEL; s++)