Continue reforming procedure execution. In this phase, get rid of
[pspp-builds.git] / src / language / xforms / 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., 51 Franklin Street, Fifth Floor, Boston, MA
18    02110-1301, USA. */
19
20 #include <config.h>
21
22 #include <stdlib.h>
23
24 #include <data/case.h>
25 #include <data/dictionary.h>
26 #include <procedure.h>
27 #include <data/transformations.h>
28 #include <data/variable.h>
29 #include <language/command.h>
30 #include <language/lexer/lexer.h>
31 #include <language/lexer/range-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 pool *, struct criteria *);
96 static bool parse_string_criteria (struct pool *, struct criteria *);
97 \f
98 int
99 cmd_count (void)
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 ())
118         goto fail;
119       dv->var = dict_lookup_var (default_dict, tokid);
120       if (dv->var != NULL)
121         {
122           if (dv->var->type == ALPHA)
123             {
124               msg (SE, _("Destination cannot be a string variable."));
125               goto fail;
126             }
127         }
128       else
129         dv->name = pool_strdup (trns->pool, tokid);
130
131       lex_get ();
132       if (!lex_force_match ('='))
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 (default_dict, &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 ('('))
148             goto fail;
149
150           crit->value_cnt = 0;
151           if (crit->vars[0]->type == NUMERIC)
152             ok = parse_numeric_criteria (trns->pool, crit);
153           else
154             ok = parse_string_criteria (trns->pool, crit);
155           if (!ok)
156             goto fail;
157
158           if (token == '/' || token == '.')
159             break;
160
161           crit = crit->next = pool_alloc (trns->pool, sizeof *crit);
162         }
163
164       if (token == '.')
165         break;
166
167       if (!lex_force_match ('/'))
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 (default_dict, dv->name);
179
180         if (dv->var == NULL) 
181           dv->var = dict_create_var_assert (default_dict, dv->name, 0);
182       }
183
184   add_transformation (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 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 ("SYSMIS"))
206         crit->count_system_missing = true;
207       else if (lex_match_id ("MISSING"))
208         crit->count_user_missing = true;
209       else if (parse_num_range (&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 (',');
226       if (lex_match (')'))
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 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 (crit->vars[i]->width > len)
242       len = crit->vars[i]->width;
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 ())
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_c_str (&tokstr));
258       lex_get ();
259
260       lex_match (',');
261       if (lex_match (')'))
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]->fv);
280       if (x == SYSMIS)
281         counter += crit->count_system_missing;
282       else if (crit->count_user_missing
283                && mv_is_num_user_missing (&crit->vars[i]->miss, 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]->fv), *v,
314                      crit->vars[i]->width))
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                  int 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 (crit->vars[0]->type == NUMERIC)
340           counter += count_numeric (crit, c);
341         else
342           counter += count_string (crit, c);
343       case_data_rw (c, dv->var->fv)->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 }