Remove "Written by Ben Pfaff <blp@gnu.org>" lines everywhere.
[pspp-builds.git] / src / language / xforms / count.c
1 /* PSPP - computes sample statistics.
2    Copyright (C) 1997-9, 2000 Free Software Foundation, Inc.
3
4    This program is free software; you can redistribute it and/or
5    modify it under the terms of the GNU General Public License as
6    published by the Free Software Foundation; either version 2 of the
7    License, or (at your option) any later version.
8
9    This program is distributed in the hope that it will be useful, but
10    WITHOUT ANY WARRANTY; without even the implied warranty of
11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12    General Public License for more details.
13
14    You should have received a copy of the GNU General Public License
15    along with this program; if not, write to the Free Software
16    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
17    02110-1301, USA. */
18
19 #include <config.h>
20
21 #include <stdlib.h>
22
23 #include <data/case.h>
24 #include <data/dictionary.h>
25 #include <data/procedure.h>
26 #include <data/transformations.h>
27 #include <data/variable.h>
28 #include <language/command.h>
29 #include <language/lexer/lexer.h>
30 #include <language/lexer/range-parser.h>
31 #include <language/lexer/variable-parser.h>
32 #include <libpspp/alloc.h>
33 #include <libpspp/compiler.h>
34 #include <libpspp/message.h>
35 #include <libpspp/message.h>
36 #include <libpspp/pool.h>
37 #include <libpspp/str.h>
38
39 #include "gettext.h"
40 #define _(msgid) gettext (msgid)
41
42 /* Value or range? */
43 enum value_type
44   {
45     CNT_SINGLE,                 /* Single value. */
46     CNT_RANGE                   /* a <= x <= b. */
47   };
48
49 /* Numeric count criteria. */
50 struct num_value
51   {
52     enum value_type type;       /* How to interpret a, b. */
53     double a, b;                /* Values to count. */
54   };
55
56 struct criteria
57   {
58     struct criteria *next;
59
60     /* Variables to count. */
61     struct variable **vars;
62     size_t var_cnt;
63
64     /* Count special values?. */
65     bool count_system_missing;  /* Count system missing? */
66     bool count_user_missing;    /* Count user missing? */
67
68     /* Criterion values. */    
69     size_t value_cnt;
70     union
71       {
72         struct num_value *num;
73         char **str;
74       }
75     values;
76   };
77
78 struct dst_var
79   {
80     struct dst_var *next;
81     struct variable *var;       /* Destination variable. */
82     char *name;                 /* Name of dest var. */
83     struct criteria *crit;      /* The criteria specifications. */
84   };
85
86 struct count_trns
87   {
88     struct dst_var *dst_vars;
89     struct pool *pool;
90   };
91
92 static trns_proc_func count_trns_proc;
93 static trns_free_func count_trns_free;
94
95 static bool parse_numeric_criteria (struct lexer *, struct pool *, struct criteria *);
96 static bool parse_string_criteria (struct lexer *, struct pool *, struct criteria *);
97 \f
98 int
99 cmd_count (struct lexer *lexer, struct dataset *ds)
100 {
101   struct dst_var *dv;           /* Destination var being parsed. */
102   struct count_trns *trns;      /* Transformation. */
103
104   /* Parses each slash-delimited specification. */
105   trns = pool_create_container (struct count_trns, pool);
106   trns->dst_vars = dv = pool_alloc (trns->pool, sizeof *dv);
107   for (;;)
108     {
109       struct criteria *crit;
110
111       /* Initialize this struct dst_var to ensure proper cleanup. */
112       dv->next = NULL;
113       dv->var = NULL;
114       dv->crit = NULL;
115
116       /* Get destination variable, or at least its name. */
117       if (!lex_force_id (lexer))
118         goto fail;
119       dv->var = dict_lookup_var (dataset_dict (ds), lex_tokid (lexer));
120       if (dv->var != NULL)
121         {
122           if (var_is_alpha (dv->var))
123             {
124               msg (SE, _("Destination cannot be a string variable."));
125               goto fail;
126             }
127         }
128       else
129         dv->name = pool_strdup (trns->pool, lex_tokid (lexer));
130
131       lex_get (lexer);
132       if (!lex_force_match (lexer, '='))
133         goto fail;
134
135       crit = dv->crit = pool_alloc (trns->pool, sizeof *crit);
136       for (;;)
137         {
138           bool ok;
139           
140           crit->next = NULL;
141           crit->vars = NULL;
142           if (!parse_variables (lexer, dataset_dict (ds), &crit->vars, &crit->var_cnt,
143                                 PV_DUPLICATE | PV_SAME_TYPE))
144             goto fail;
145           pool_register (trns->pool, free, crit->vars);
146
147           if (!lex_force_match (lexer, '('))
148             goto fail;
149
150           crit->value_cnt = 0;
151           if (var_is_numeric (crit->vars[0]))
152             ok = parse_numeric_criteria (lexer, trns->pool, crit);
153           else
154             ok = parse_string_criteria (lexer, trns->pool, crit);
155           if (!ok)
156             goto fail;
157
158           if (lex_token (lexer) == '/' || lex_token (lexer) == '.')
159             break;
160
161           crit = crit->next = pool_alloc (trns->pool, sizeof *crit);
162         }
163
164       if (lex_token (lexer) == '.')
165         break;
166
167       if (!lex_force_match (lexer, '/'))
168         goto fail;
169       dv = dv->next = pool_alloc (trns->pool, sizeof *dv);
170     }
171
172   /* Create all the nonexistent destination variables. */
173   for (dv = trns->dst_vars; dv; dv = dv->next)
174     if (dv->var == NULL)
175       {
176         /* It's valid, though motivationally questionable, to count to
177            the same dest var more than once. */
178         dv->var = dict_lookup_var (dataset_dict (ds), dv->name);
179
180         if (dv->var == NULL) 
181           dv->var = dict_create_var_assert (dataset_dict (ds), dv->name, 0);
182       }
183
184   add_transformation (ds, count_trns_proc, count_trns_free, trns);
185   return CMD_SUCCESS;
186
187 fail:
188   count_trns_free (trns);
189   return CMD_FAILURE;
190 }
191
192 /* Parses a set of numeric criterion values.  Returns success. */
193 static bool
194 parse_numeric_criteria (struct lexer *lexer, struct pool *pool, struct criteria *crit)
195 {
196   size_t allocated = 0;
197
198   crit->values.num = NULL;
199   crit->count_system_missing = false;
200   crit->count_user_missing = false;
201   for (;;)
202     {
203       double low, high;
204       
205       if (lex_match_id (lexer, "SYSMIS"))
206         crit->count_system_missing = true;
207       else if (lex_match_id (lexer, "MISSING"))
208         crit->count_user_missing = true;
209       else if (parse_num_range (lexer, &low, &high, NULL)) 
210         {
211           struct num_value *cur;
212
213           if (crit->value_cnt >= allocated)
214             crit->values.num = pool_2nrealloc (pool, crit->values.num,
215                                                &allocated,
216                                                sizeof *crit->values.num);
217           cur = &crit->values.num[crit->value_cnt++];
218           cur->type = low == high ? CNT_SINGLE : CNT_RANGE;
219           cur->a = low;
220           cur->b = high;
221         }
222       else
223         return false;
224
225       lex_match (lexer, ',');
226       if (lex_match (lexer, ')'))
227         break;
228     }
229   return true;
230 }
231
232 /* Parses a set of string criteria values.  Returns success. */
233 static bool
234 parse_string_criteria (struct lexer *lexer, struct pool *pool, struct criteria *crit)
235 {
236   int len = 0;
237   size_t allocated = 0;
238   size_t i;
239
240   for (i = 0; i < crit->var_cnt; i++)
241     if (var_get_width (crit->vars[i]) > len)
242       len = var_get_width (crit->vars[i]);
243
244   crit->values.str = NULL;
245   for (;;)
246     {
247       char **cur;
248       if (crit->value_cnt >= allocated)
249         crit->values.str = pool_2nrealloc (pool, crit->values.str,
250                                            &allocated,
251                                            sizeof *crit->values.str);
252
253       if (!lex_force_string (lexer))
254         return false;
255       cur = &crit->values.str[crit->value_cnt++];
256       *cur = pool_alloc (pool, len + 1);
257       str_copy_rpad (*cur, len + 1, ds_cstr (lex_tokstr (lexer)));
258       lex_get (lexer);
259
260       lex_match (lexer, ',');
261       if (lex_match (lexer, ')'))
262         break;
263     }
264
265   return true;
266 }
267 \f
268 /* Transformation. */
269
270 /* Counts the number of values in case C matching CRIT. */
271 static inline int
272 count_numeric (struct criteria *crit, struct ccase *c)
273 {
274   int counter = 0;
275   size_t i;
276
277   for (i = 0; i < crit->var_cnt; i++)
278     {
279       double x = case_num (c, crit->vars[i]);
280       if (x == SYSMIS)
281         counter += crit->count_system_missing;
282       else if (crit->count_user_missing
283                && var_is_num_user_missing (crit->vars[i], x))
284         counter++;
285       else 
286         {
287           struct num_value *v;
288           
289           for (v = crit->values.num; v < crit->values.num + crit->value_cnt;
290                v++) 
291             if (v->type == CNT_SINGLE ? x == v->a : x >= v->a && x <= v->b) 
292               {
293                 counter++;
294                 break;
295               } 
296         }
297     }
298   
299   return counter;
300 }
301
302 /* Counts the number of values in case C matching CRIT. */
303 static inline int
304 count_string (struct criteria *crit, struct ccase *c)
305 {
306   int counter = 0;
307   size_t i;
308
309   for (i = 0; i < crit->var_cnt; i++)
310     {
311       char **v;
312       for (v = crit->values.str; v < crit->values.str + crit->value_cnt; v++)
313         if (!memcmp (case_str (c, crit->vars[i]), *v,
314                      var_get_width (crit->vars[i])))
315           {
316             counter++;
317             break;
318           }
319     }
320
321   return counter;
322 }
323
324 /* Performs the COUNT transformation T on case C. */
325 static int
326 count_trns_proc (void *trns_, struct ccase *c,
327                  casenumber case_num UNUSED)
328 {
329   struct count_trns *trns = trns_;
330   struct dst_var *dv;
331
332   for (dv = trns->dst_vars; dv; dv = dv->next)
333     {
334       struct criteria *crit;
335       int counter;
336
337       counter = 0;
338       for (crit = dv->crit; crit; crit = crit->next)
339         if (var_is_numeric (crit->vars[0]))
340           counter += count_numeric (crit, c);
341         else
342           counter += count_string (crit, c);
343       case_data_rw (c, dv->var)->f = counter;
344     }
345   return TRNS_CONTINUE;
346 }
347
348 /* Destroys all dynamic data structures associated with TRNS. */
349 static bool
350 count_trns_free (void *trns_)
351 {
352   struct count_trns *trns = (struct count_trns *) trns_;
353   pool_destroy (trns->pool);
354   return true;
355 }