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? */
58 #include "debug-print.h"
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;
123 static void debug_print (void);
126 /* First counting in chain. */
127 static struct cnt_var_info *head;
131 static trns_proc_func count_trns_proc;
132 static trns_free_func count_trns_free;
134 static int parse_numeric_criteria (struct counting *);
135 static int parse_string_criteria (struct counting *);
140 /* Specification currently being parsed. */
141 struct cnt_var_info *cnt;
143 /* Counting currently being parsed. */
146 /* Return value from parsing function. */
149 /* Transformation. */
150 struct count_trns *trns;
152 lex_match_id ("COUNT");
154 /* Parses each slash-delimited specification. */
155 head = cnt = xmalloc (sizeof *cnt);
158 /* Initialize this struct cnt_var_info to ensure proper cleanup. */
163 /* Get destination struct variable, or at least its name. */
164 if (!lex_force_id ())
166 cnt->d = dict_lookup_var (default_dict, tokid);
169 if (cnt->d->type == ALPHA)
171 msg (SE, _("Destination cannot be a string variable."));
176 strcpy (cnt->n, tokid);
179 if (!lex_force_match ('='))
182 c = cnt->c = xmalloc (sizeof *c);
187 if (!parse_variables (default_dict, &c->v, &c->n,
188 PV_DUPLICATE | PV_SAME_TYPE))
191 if (!lex_force_match ('('))
194 ret = (c->v[0]->type == NUMERIC
195 ? parse_numeric_criteria
196 : parse_string_criteria) (c);
200 if (token == '/' || token == '.')
203 c = c->next = xmalloc (sizeof *c);
209 if (!lex_force_match ('/'))
211 cnt = cnt->next = xmalloc (sizeof *cnt);
214 /* Create all the nonexistent destination variables. */
215 for (cnt = head; cnt; cnt = cnt->next)
218 /* It's valid, though motivationally questionable, to count to
219 the same dest var more than once. */
220 cnt->d = dict_lookup_var (default_dict, cnt->n);
223 cnt->d = dict_create_var_assert (default_dict, cnt->n, 0);
230 trns = xmalloc (sizeof *trns);
231 trns->h.proc = count_trns_proc;
232 trns->h.free = count_trns_free;
234 add_transformation ((struct trns_header *) trns);
242 count_trns_free ((struct trns_header *) & t);
247 /* Parses a set of numeric criterion values. */
249 parse_numeric_criteria (struct counting * c)
262 c->crit.n = xrealloc (c->crit.n, m * sizeof (struct cnt_num));
265 cur = &c->crit.n[n++];
270 if (lex_match_id ("THRU"))
274 if (!lex_force_num ())
277 cur->type = CNT_RANGE;
282 msg (SE, _("%g THRU %g is not a valid range. The "
283 "number following THRU must be at least "
284 "as big as the number preceding THRU."),
289 else if (lex_match_id ("HI") || lex_match_id ("HIGHEST"))
290 cur->type = CNT_HIGH;
298 cur->type = CNT_SINGLE;
300 else if (lex_match_id ("LO") || lex_match_id ("LOWEST"))
302 if (!lex_force_match_id ("THRU"))
310 else if (lex_match_id ("HI") || lex_match_id ("HIGHEST"))
318 else if (lex_match_id ("SYSMIS"))
323 else if (lex_match_id ("MISSING"))
336 c->crit.n[n].type = CNT_SENTINEL;
340 /* Parses a set of string criteria values. The skeleton is the same
341 as parse_numeric_criteria(). */
343 parse_string_criteria (struct counting * c)
352 for (i = 0; i < c->n; i++)
353 if (c->v[i]->width > len)
354 len = c->v[i]->width;
363 c->crit.n = xrealloc (c->crit.n, m * sizeof (struct cnt_str));
366 if (!lex_force_string ())
368 cur = &c->crit.s[n++];
369 cur->type = CNT_SINGLE;
370 cur->s = malloc (len + 1);
371 st_pad_copy (cur->s, ds_value (&tokstr), len + 1);
379 c->crit.s[n].type = CNT_SENTINEL;
383 /* Transformation. */
385 /* Counts the number of values in case C matching counting CNT. */
387 count_numeric (struct counting * cnt, struct ccase * c)
396 for (i = 0; i < cnt->n; i++)
398 /* Extract the variable value and eliminate missing values. */
399 cmp = c->data[cnt->v[i]->fv].f;
402 if (cnt->missing >= 1)
406 if (cnt->missing >= 2 && is_num_user_missing (cmp, cnt->v[i]))
412 /* Try to find the value in the list. */
413 for (num = cnt->crit.n;; num++)
435 if (cmp < num->a || cmp > num->b)
452 /* Counts the number of values in case C matching counting CNT. */
454 count_string (struct counting * cnt, struct ccase * c)
465 for (i = 0; i < cnt->n; i++)
467 /* Extract the variable value, variable width. */
468 cmp = c->data[cnt->v[i]->fv].s;
469 len = cnt->v[i]->width;
471 for (str = cnt->crit.s;; str++)
477 if (memcmp (cmp, str->s, len))
491 /* Performs the COUNT transformation T on case C. */
493 count_trns_proc (struct trns_header * trns, struct ccase * c,
496 struct cnt_var_info *info;
497 struct counting *cnt;
500 for (info = ((struct count_trns *) trns)->specs; info; info = info->next)
503 for (cnt = info->c; cnt; cnt = cnt->next)
504 if (cnt->v[0]->type == NUMERIC)
505 counter += count_numeric (cnt, c);
507 counter += count_string (cnt, c);
508 c->data[info->d->fv].f = counter;
513 /* Destroys all dynamic data structures associated with T. */
515 count_trns_free (struct trns_header * t)
517 struct cnt_var_info *iter, *next;
519 for (iter = ((struct count_trns *) t)->specs; iter; iter = next)
521 struct counting *i, *n;
523 for (i = iter->c; i; i = n)
527 if (i->v[0]->type == NUMERIC)
533 for (s = i->crit.s; s->type != CNT_SENTINEL; s++)
555 struct cnt_var_info *iter;
560 for (iter = head; iter; iter = iter->next)
562 printf (" %s=", iter->d->name);
563 for (i = iter->c; i; i = i->next)
565 for (j = 0; j < i->n; j++)
566 printf ("%s%s", j ? " " : "", i->v[j]->name);
568 if (i->v[0]->type == NUMERIC)
574 else if (i->missing == 1)
577 assert (i->missing == 0);
579 for (n = i->crit.n; n->type != CNT_SENTINEL; n++)
581 if (i->missing && n != i->crit.n)
589 printf ("%g THRU HIGH", n->a);
592 printf ("LOW THRU %g", n->a);
595 printf ("%g THRU %g", n->a, n->b);
598 printf ("LOW THRU HIGH");
601 printf ("<ERROR %d>", n->type);
610 for (s = i->crit.s; s->type != CNT_SENTINEL; s++)
614 if (s->type == CNT_SINGLE)
615 printf ("'%s'", s->s);
617 printf ("<ERROR %d>", s->type);
625 #endif /* DEBUGGING */