058d60d539d81f71c7880b703fc9f1a1c62e06ad
[pspp-builds.git] / src / count.c
1 /* PSPP - computes sample statistics.
2    Copyright (C) 1997-9, 2000 Free Software Foundation, Inc.
3    Written by Ben Pfaff <blp@gnu.org>.
4
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.
9
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.
14
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
18    02111-1307, USA. */
19
20 #include <config.h>
21 #include "error.h"
22 #include <stdlib.h>
23 #include "alloc.h"
24 #include "command.h"
25 #include "error.h"
26 #include "lexer.h"
27 #include "str.h"
28 #include "var.h"
29
30 /* Implementation details:
31
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,
36
37    A B
38    1 2
39    2 1
40
41    the command COUNT A=A B (1) / B=A B (2) will produce the following
42    results,
43
44    A B
45    1 1
46    1 0
47
48    rather than the results that would be produced if subcommands were
49    simultaneous:
50
51    A B
52    1 1
53    1 1
54
55    Perhaps simultaneity could be implemented as an option.  On the
56    other hand, what good are the above commands?  */
57 \f
58 /* Definitions. */
59
60 enum
61   {
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. */
69   };
70
71 struct cnt_num
72   {
73     int type;
74     double a, b;
75   };
76
77 struct cnt_str
78   {
79     int type;
80     char *s;
81   };
82
83 struct counting
84   {
85     struct counting *next;
86
87     /* variables to count */
88     struct variable **v;
89     int n;
90
91     /* values to count */
92     int missing;                /* (numeric only)
93                                    0=don't count missing,
94                                    1=count SYSMIS,
95                                    2=count system- and user-missing */
96     union                       /* Criterion values. */
97       {
98         struct cnt_num *n;
99         struct cnt_str *s;
100       }
101     crit;
102   };
103
104 struct cnt_var_info
105   {
106     struct cnt_var_info *next;
107
108     struct variable *d;         /* Destination variable. */
109     char n[9];                  /* Name of dest var. */
110
111     struct counting *c;         /* The counting specifications. */
112   };
113
114 struct count_trns
115   {
116     struct trns_header h;
117     struct cnt_var_info *specs;
118   };
119 \f
120 /* Parser. */
121
122 static trns_proc_func count_trns_proc;
123 static trns_free_func count_trns_free;
124
125 static int parse_numeric_criteria (struct counting *);
126 static int parse_string_criteria (struct counting *);
127
128 int
129 cmd_count (void)
130 {
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. */
136
137   /* Parses each slash-delimited specification. */
138   head = cnt = xmalloc (sizeof *cnt);
139   for (;;)
140     {
141       /* Initialize this struct cnt_var_info to ensure proper cleanup. */
142       cnt->next = NULL;
143       cnt->d = NULL;
144       cnt->c = NULL;
145
146       /* Get destination struct variable, or at least its name. */
147       if (!lex_force_id ())
148         goto fail;
149       cnt->d = dict_lookup_var (default_dict, tokid);
150       if (cnt->d)
151         {
152           if (cnt->d->type == ALPHA)
153             {
154               msg (SE, _("Destination cannot be a string variable."));
155               goto fail;
156             }
157         }
158       else
159         strcpy (cnt->n, tokid);
160
161       lex_get ();
162       if (!lex_force_match ('='))
163         goto fail;
164
165       c = cnt->c = xmalloc (sizeof *c);
166       for (;;)
167         {
168           c->next = NULL;
169           c->v = NULL;
170           if (!parse_variables (default_dict, &c->v, &c->n,
171                                 PV_DUPLICATE | PV_SAME_TYPE))
172             goto fail;
173
174           if (!lex_force_match ('('))
175             goto fail;
176
177           ret = (c->v[0]->type == NUMERIC
178                  ? parse_numeric_criteria
179                  : parse_string_criteria) (c);
180           if (!ret)
181             goto fail;
182
183           if (token == '/' || token == '.')
184             break;
185
186           c = c->next = xmalloc (sizeof *c);
187         }
188
189       if (token == '.')
190         break;
191
192       if (!lex_force_match ('/'))
193         goto fail;
194       cnt = cnt->next = xmalloc (sizeof *cnt);
195     }
196
197   /* Create all the nonexistent destination variables. */
198   for (cnt = head; cnt; cnt = cnt->next)
199     if (!cnt->d)
200       {
201         /* It's valid, though motivationally questionable, to count to
202            the same dest var more than once. */
203         cnt->d = dict_lookup_var (default_dict, cnt->n);
204
205         if (cnt->d == NULL) 
206           cnt->d = dict_create_var_assert (default_dict, cnt->n, 0);
207       }
208
209   trns = xmalloc (sizeof *trns);
210   trns->h.proc = count_trns_proc;
211   trns->h.free = count_trns_free;
212   trns->specs = head;
213   add_transformation ((struct trns_header *) trns);
214
215   return CMD_SUCCESS;
216
217 fail:
218   {
219     struct count_trns t;
220     t.specs = head;
221     count_trns_free ((struct trns_header *) & t);
222     return CMD_FAILURE;
223   }
224 }
225
226 /* Parses a set of numeric criterion values. */
227 static int
228 parse_numeric_criteria (struct counting * c)
229 {
230   int n = 0;
231   int m = 0;
232
233   c->crit.n = 0;
234   c->missing = 0;
235   for (;;)
236     {
237       struct cnt_num *cur;
238       if (n >= m - 1)
239         {
240           m += 16;
241           c->crit.n = xrealloc (c->crit.n, m * sizeof (struct cnt_num));
242         }
243
244       cur = &c->crit.n[n++];
245       if (token == T_NUM)
246         {
247           cur->a = tokval;
248           lex_get ();
249           if (lex_match_id ("THRU"))
250             {
251               if (token == T_NUM)
252                 {
253                   if (!lex_force_num ())
254                     return 0;
255                   cur->b = tokval;
256                   cur->type = CNT_RANGE;
257                   lex_get ();
258
259                   if (cur->a > cur->b)
260                     {
261                       msg (SE, _("%g THRU %g is not a valid range.  The "
262                                  "number following THRU must be at least "
263                                  "as big as the number preceding THRU."),
264                            cur->a, cur->b);
265                       return 0;
266                     }
267                 }
268               else if (lex_match_id ("HI") || lex_match_id ("HIGHEST"))
269                 cur->type = CNT_HIGH;
270               else
271                 {
272                   lex_error (NULL);
273                   return 0;
274                 }
275             }
276           else
277             cur->type = CNT_SINGLE;
278         }
279       else if (lex_match_id ("LO") || lex_match_id ("LOWEST"))
280         {
281           if (!lex_force_match_id ("THRU"))
282             return 0;
283           if (token == T_NUM)
284             {
285               cur->type = CNT_LOW;
286               cur->a = tokval;
287               lex_get ();
288             }
289           else if (lex_match_id ("HI") || lex_match_id ("HIGHEST"))
290             cur->type = CNT_ANY;
291           else
292             {
293               lex_error (NULL);
294               return 0;
295             }
296         }
297       else if (lex_match_id ("SYSMIS"))
298         {
299           if (c->missing < 1)
300             c->missing = 1;
301         }
302       else if (lex_match_id ("MISSING"))
303         c->missing = 2;
304       else
305         {
306           lex_error (NULL);
307           return 0;
308         }
309
310       lex_match (',');
311       if (lex_match (')'))
312         break;
313     }
314
315   c->crit.n[n].type = CNT_SENTINEL;
316   return 1;
317 }
318
319 /* Parses a set of string criteria values.  The skeleton is the same
320    as parse_numeric_criteria(). */
321 static int
322 parse_string_criteria (struct counting * c)
323 {
324   int len = 0;
325
326   int n = 0;
327   int m = 0;
328
329   int i;
330
331   for (i = 0; i < c->n; i++)
332     if (c->v[i]->width > len)
333       len = c->v[i]->width;
334
335   c->crit.n = 0;
336   for (;;)
337     {
338       struct cnt_str *cur;
339       if (n >= m - 1)
340         {
341           m += 16;
342           c->crit.n = xrealloc (c->crit.n, m * sizeof (struct cnt_str));
343         }
344
345       if (!lex_force_string ())
346         return 0;
347       cur = &c->crit.s[n++];
348       cur->type = CNT_SINGLE;
349       cur->s = malloc (len + 1);
350       st_pad_copy (cur->s, ds_value (&tokstr), len + 1);
351       lex_get ();
352
353       lex_match (',');
354       if (lex_match (')'))
355         break;
356     }
357
358   c->crit.s[n].type = CNT_SENTINEL;
359   return 1;
360 }
361 \f
362 /* Transformation. */
363
364 /* Counts the number of values in case C matching counting CNT. */
365 static inline int
366 count_numeric (struct counting * cnt, struct ccase * c)
367 {
368   int counter = 0;
369
370   struct cnt_num *num;
371
372   double cmp;
373   int i;
374
375   for (i = 0; i < cnt->n; i++)
376     {
377       /* Extract the variable value and eliminate missing values. */
378       cmp = c->data[cnt->v[i]->fv].f;
379       if (cmp == SYSMIS)
380         {
381           if (cnt->missing >= 1)
382             counter++;
383           continue;
384         }
385       if (cnt->missing >= 2 && is_num_user_missing (cmp, cnt->v[i]))
386         {
387           counter++;
388           continue;
389         }
390
391       /* Try to find the value in the list. */
392       for (num = cnt->crit.n;; num++)
393         switch (num->type)
394           {
395           case CNT_ERROR:
396             assert (0);
397             break;
398           case CNT_SINGLE:
399             if (cmp != num->a)
400               break;
401             counter++;
402             goto done;
403           case CNT_HIGH:
404             if (cmp < num->a)
405               break;
406             counter++;
407             goto done;
408           case CNT_LOW:
409             if (cmp > num->a)
410               break;
411             counter++;
412             goto done;
413           case CNT_RANGE:
414             if (cmp < num->a || cmp > num->b)
415               break;
416             counter++;
417             goto done;
418           case CNT_ANY:
419             counter++;
420             goto done;
421           case CNT_SENTINEL:
422             goto done;
423           default:
424             assert (0);
425           }
426     done: ;
427     }
428   return counter;
429 }
430
431 /* Counts the number of values in case C matching counting CNT. */
432 static inline int
433 count_string (struct counting * cnt, struct ccase * c)
434 {
435   int counter = 0;
436
437   struct cnt_str *str;
438
439   char *cmp;
440   int len;
441
442   int i;
443
444   for (i = 0; i < cnt->n; i++)
445     {
446       /* Extract the variable value, variable width. */
447       cmp = c->data[cnt->v[i]->fv].s;
448       len = cnt->v[i]->width;
449
450       for (str = cnt->crit.s;; str++)
451         switch (str->type)
452           {
453           case CNT_ERROR:
454             assert (0);
455           case CNT_SINGLE:
456             if (memcmp (cmp, str->s, len))
457               break;
458             counter++;
459             goto done;
460           case CNT_SENTINEL:
461             goto done;
462           default:
463             assert (0);
464           }
465     done: ;
466     }
467   return counter;
468 }
469
470 /* Performs the COUNT transformation T on case C. */
471 static int
472 count_trns_proc (struct trns_header * trns, struct ccase * c,
473                  int case_num UNUSED)
474 {
475   struct cnt_var_info *info;
476   struct counting *cnt;
477   int counter;
478
479   for (info = ((struct count_trns *) trns)->specs; info; info = info->next)
480     {
481       counter = 0;
482       for (cnt = info->c; cnt; cnt = cnt->next)
483         if (cnt->v[0]->type == NUMERIC)
484           counter += count_numeric (cnt, c);
485         else
486           counter += count_string (cnt, c);
487       c->data[info->d->fv].f = counter;
488     }
489   return -1;
490 }
491
492 /* Destroys all dynamic data structures associated with T. */
493 static void
494 count_trns_free (struct trns_header * t)
495 {
496   struct cnt_var_info *iter, *next;
497
498   for (iter = ((struct count_trns *) t)->specs; iter; iter = next)
499     {
500       struct counting *i, *n;
501
502       for (i = iter->c; i; i = n)
503         {
504           if (i->n && i->v)
505             {
506               if (i->v[0]->type == NUMERIC)
507                 free (i->crit.n);
508               else
509                 {
510                   struct cnt_str *s;
511
512                   for (s = i->crit.s; s->type != CNT_SENTINEL; s++)
513                     free (s->s);
514                   free (i->crit.s);
515                 }
516             }
517           free (i->v);
518
519           n = i->next;
520           free (i);
521         }
522
523       next = iter->next;
524       free (iter);
525     }
526 }