/[pspp]/pspp/src/count.c
ViewVC logotype

Diff of /pspp/src/count.c

Parent Directory Parent Directory | Revision Log Revision Log | View Patch Patch

revision 1.22 by blp, Wed Oct 26 05:06:14 2005 UTC revision 1.23 by blp, Sat Oct 29 23:35:55 2005 UTC
# Line 26  Line 26 
26  #include "dictionary.h"  #include "dictionary.h"
27  #include "error.h"  #include "error.h"
28  #include "lexer.h"  #include "lexer.h"
29    #include "pool.h"
30    #include "range-prs.h"
31  #include "str.h"  #include "str.h"
32  #include "var.h"  #include "var.h"
33    
34  #include "gettext.h"  #include "gettext.h"
35  #define _(msgid) gettext (msgid)  #define _(msgid) gettext (msgid)
36    
37  /* Implementation details:  /* Value or range? */
38    enum value_type
    The S?SS manuals do not specify the order that COUNT subcommands are  
    performed in.  Experiments, however, have shown that they are performed  
    in the order that they are specified in, rather than simultaneously.  
    So, with the two variables A and B, and the two cases,  
   
    A B  
    1 2  
    2 1  
   
    the command COUNT A=A B (1) / B=A B (2) will produce the following  
    results,  
   
    A B  
    1 1  
    1 0  
   
    rather than the results that would be produced if subcommands were  
    simultaneous:  
   
    A B  
    1 1  
    1 1  
   
    Perhaps simultaneity could be implemented as an option.  On the  
    other hand, what good are the above commands?  */  
   
 /* Definitions. */  
   
 enum  
39    {    {
     CNT_ERROR,                  /* Invalid value. */  
40      CNT_SINGLE,                 /* Single value. */      CNT_SINGLE,                 /* Single value. */
41      CNT_HIGH,                   /* x >= a. */      CNT_RANGE                   /* a <= x <= b. */
     CNT_LOW,                    /* x <= a. */  
     CNT_RANGE,                  /* a <= x <= b. */  
     CNT_ANY,                    /* Count any. */  
     CNT_SENTINEL                /* List terminator. */  
42    };    };
43    
44  struct cnt_num  /* Numeric count criteria. */
45    struct num_value
46    {    {
47      int type;      enum value_type type;       /* How to interpret a, b. */
48      double a, b;      double a, b;                /* Values to count. */
49    };    };
50    
51  struct cnt_str  struct criteria
52    {    {
53      int type;      struct criteria *next;
     char *s;  
   };  
   
 struct counting  
   {  
     struct counting *next;  
54    
55      /* variables to count */      /* Variables to count. */
56      struct variable **v;      struct variable **vars;
57      size_t n;      size_t var_cnt;
58    
59      /* values to count */      /* Count special values?. */
60      int missing;                /* (numeric only)      bool count_system_missing;  /* Count system missing? */
61                                     0=don't count missing,      bool count_user_missing;    /* Count user missing? */
62                                     1=count SYSMIS,  
63                                     2=count system- and user-missing */      /* Criterion values. */    
64      union                       /* Criterion values. */      size_t value_cnt;
65        union
66        {        {
67          struct cnt_num *n;          struct num_value *num;
68          struct cnt_str *s;          char **str;
69        }        }
70      crit;      values;
71    };    };
72    
73  struct cnt_var_info  struct dst_var
74    {    {
75      struct cnt_var_info *next;      struct dst_var *next;
76        struct variable *var;       /* Destination variable. */
77      struct variable *d;         /* Destination variable. */      char *name;                 /* Name of dest var. */
78      char n[LONG_NAME_LEN + 1];  /* Name of dest var. */      struct criteria *crit;      /* The criteria specifications. */
   
     struct counting *c;         /* The counting specifications. */  
79    };    };
80    
81  struct count_trns  struct count_trns
82    {    {
83      struct trns_header h;      struct trns_header h;
84      struct cnt_var_info *specs;      struct dst_var *dst_vars;
85        struct pool *pool;
86    };    };
87    
88  /* Parser. */  /* Parser. */
# Line 127  struct count_trns Line 90  struct count_trns
90  static trns_proc_func count_trns_proc;  static trns_proc_func count_trns_proc;
91  static trns_free_func count_trns_free;  static trns_free_func count_trns_free;
92    
93  static int parse_numeric_criteria (struct counting *);  static bool parse_numeric_criteria (struct pool *, struct criteria *);
94  static int parse_string_criteria (struct counting *);  static bool parse_string_criteria (struct pool *, struct criteria *);
95    
96  int  int
97  cmd_count (void)  cmd_count (void)
98  {  {
99    struct cnt_var_info *cnt;     /* Specification currently being parsed. */    struct dst_var *dv;           /* Destination var being parsed. */
   struct counting *c;           /* Counting currently being parsed. */  
   int ret;                      /* Return value from parsing function. */  
100    struct count_trns *trns;      /* Transformation. */    struct count_trns *trns;      /* Transformation. */
   struct cnt_var_info *head;    /* First counting in chain. */  
101    
102    /* Parses each slash-delimited specification. */    /* Parses each slash-delimited specification. */
103    head = cnt = xmalloc (sizeof *cnt);    trns = xmalloc (sizeof *trns);
104      trns->h.proc = count_trns_proc;
105      trns->h.free = count_trns_free;
106      trns->pool = pool_create ();
107      trns->dst_vars = dv = pool_alloc (trns->pool, sizeof *dv);
108    for (;;)    for (;;)
109      {      {
110        /* Initialize this struct cnt_var_info to ensure proper cleanup. */        struct criteria *crit;
111        cnt->next = NULL;  
112        cnt->d = NULL;        /* Initialize this struct dst_var to ensure proper cleanup. */
113        cnt->c = NULL;        dv->next = NULL;
114          dv->var = NULL;
115          dv->crit = NULL;
116    
117        /* Get destination variable, or at least its name. */        /* Get destination variable, or at least its name. */
118        if (!lex_force_id ())        if (!lex_force_id ())
119          goto fail;          goto fail;
120        cnt->d = dict_lookup_var (default_dict, tokid);        dv->var = dict_lookup_var (default_dict, tokid);
121        if (cnt->d)        if (dv->var != NULL)
122          {          {
123            if (cnt->d->type == ALPHA)            if (dv->var->type == ALPHA)
124              {              {
125                msg (SE, _("Destination cannot be a string variable."));                msg (SE, _("Destination cannot be a string variable."));
126                goto fail;                goto fail;
127              }              }
128          }          }
129        else        else
130          str_copy_trunc (cnt->n, sizeof cnt->n, tokid);          dv->name = pool_strdup (trns->pool, tokid);
131    
132        lex_get ();        lex_get ();
133        if (!lex_force_match ('='))        if (!lex_force_match ('='))
134          goto fail;          goto fail;
135    
136        c = cnt->c = xmalloc (sizeof *c);        crit = dv->crit = pool_alloc (trns->pool, sizeof *crit);
137        for (;;)        for (;;)
138          {          {
139            c->next = NULL;            bool ok;
140            c->v = NULL;            
141            if (!parse_variables (default_dict, &c->v, &c->n,            crit->next = NULL;
142              crit->vars = NULL;
143              if (!parse_variables (default_dict, &crit->vars, &crit->var_cnt,
144                                  PV_DUPLICATE | PV_SAME_TYPE))                                  PV_DUPLICATE | PV_SAME_TYPE))
145              goto fail;              goto fail;
146              pool_register (trns->pool, free, crit->vars);
147    
148            if (!lex_force_match ('('))            if (!lex_force_match ('('))
149              goto fail;              goto fail;
150    
151            ret = (c->v[0]->type == NUMERIC            crit->value_cnt = 0;
152                   ? parse_numeric_criteria            if (crit->vars[0]->type == NUMERIC)
153                   : parse_string_criteria) (c);              ok = parse_numeric_criteria (trns->pool, crit);
154            if (!ret)            else
155                ok = parse_string_criteria (trns->pool, crit);
156              if (!ok)
157              goto fail;              goto fail;
158    
159            if (token == '/' || token == '.')            if (token == '/' || token == '.')
160              break;              break;
161    
162            c = c->next = xmalloc (sizeof *c);            crit = crit->next = pool_alloc (trns->pool, sizeof *crit);
163          }          }
164    
165        if (token == '.')        if (token == '.')
# Line 196  cmd_count (void) Line 167  cmd_count (void)
167    
168        if (!lex_force_match ('/'))        if (!lex_force_match ('/'))
169          goto fail;          goto fail;
170        cnt = cnt->next = xmalloc (sizeof *cnt);        dv = dv->next = pool_alloc (trns->pool, sizeof *dv);
171      }      }
172    
173    /* Create all the nonexistent destination variables. */    /* Create all the nonexistent destination variables. */
174    for (cnt = head; cnt; cnt = cnt->next)    for (dv = trns->dst_vars; dv; dv = dv->next)
175      if (!cnt->d)      if (dv->var == NULL)
176        {        {
177          /* It's valid, though motivationally questionable, to count to          /* It's valid, though motivationally questionable, to count to
178             the same dest var more than once. */             the same dest var more than once. */
179          cnt->d = dict_lookup_var (default_dict, cnt->n);          dv->var = dict_lookup_var (default_dict, dv->name);
180    
181          if (cnt->d == NULL)          if (dv->var == NULL)
182            cnt->d = dict_create_var_assert (default_dict, cnt->n, 0);            dv->var = dict_create_var_assert (default_dict, dv->name, 0);
183        }        }
184    
185    trns = xmalloc (sizeof *trns);    add_transformation (&trns->h);
   trns->h.proc = count_trns_proc;  
   trns->h.free = count_trns_free;  
   trns->specs = head;  
   add_transformation ((struct trns_header *) trns);  
   
186    return CMD_SUCCESS;    return CMD_SUCCESS;
187    
188  fail:  fail:
189    {    count_trns_free (&trns->h);
190      struct count_trns t;    return CMD_FAILURE;
     t.specs = head;  
     count_trns_free ((struct trns_header *) & t);  
     return CMD_FAILURE;  
   }  
191  }  }
192    
193  /* Parses a set of numeric criterion values. */  /* Parses a set of numeric criterion values.  Returns success. */
194  static int  static bool
195  parse_numeric_criteria (struct counting *c)  parse_numeric_criteria (struct pool *pool, struct criteria *crit)
196  {  {
197    size_t n = 0;    size_t allocated = 0;
   size_t m = 0;  
198    
199    c->crit.n = 0;    crit->values.num = NULL;
200    c->missing = 0;    crit->count_system_missing = false;
201      crit->count_user_missing = false;
202    for (;;)    for (;;)
203      {      {
204        struct cnt_num *cur;        double low, high;
205        if (n + 1 >= m)        
206          {        if (lex_match_id ("SYSMIS"))
207            m += 16;          crit->count_system_missing = true;
           c->crit.n = xnrealloc (c->crit.n, m, sizeof *c->crit.n);  
         }  
   
       cur = &c->crit.n[n++];  
       if (lex_is_number ())  
         {  
           cur->a = tokval;  
           lex_get ();  
           if (lex_match_id ("THRU"))  
             {  
               if (lex_is_number ())  
                 {  
                   if (!lex_force_num ())  
                     return 0;  
                   cur->b = tokval;  
                   cur->type = CNT_RANGE;  
                   lex_get ();  
   
                   if (cur->a > cur->b)  
                     {  
                       msg (SE, _("%g THRU %g is not a valid range.  The "  
                                  "number following THRU must be at least "  
                                  "as big as the number preceding THRU."),  
                            cur->a, cur->b);  
                       return 0;  
                     }  
                 }  
               else if (lex_match_id ("HI") || lex_match_id ("HIGHEST"))  
                 cur->type = CNT_HIGH;  
               else  
                 {  
                   lex_error (NULL);  
                   return 0;  
                 }  
             }  
           else  
             cur->type = CNT_SINGLE;  
         }  
       else if (lex_match_id ("LO") || lex_match_id ("LOWEST"))  
         {  
           if (!lex_force_match_id ("THRU"))  
             return 0;  
           if (lex_is_number ())  
             {  
               cur->type = CNT_LOW;  
               cur->a = tokval;  
               lex_get ();  
             }  
           else if (lex_match_id ("HI") || lex_match_id ("HIGHEST"))  
             cur->type = CNT_ANY;  
           else  
             {  
               lex_error (NULL);  
               return 0;  
             }  
         }  
       else if (lex_match_id ("SYSMIS"))  
         {  
           if (c->missing < 1)  
             c->missing = 1;  
         }  
208        else if (lex_match_id ("MISSING"))        else if (lex_match_id ("MISSING"))
209          c->missing = 2;          crit->count_user_missing = true;
210          else if (parse_num_range (&low, &high, NULL))
211            {
212              struct num_value *cur;
213    
214              if (crit->value_cnt >= allocated)
215                crit->values.num = pool_2nrealloc (pool, crit->values.num,
216                                                   &allocated,
217                                                   sizeof *crit->values.num);
218              cur = &crit->values.num[crit->value_cnt++];
219              cur->type = low == high ? CNT_SINGLE : CNT_RANGE;
220              cur->a = low;
221              cur->b = high;
222            }
223        else        else
224          {          return false;
           lex_error (NULL);  
           return 0;  
         }  
225    
226        lex_match (',');        lex_match (',');
227        if (lex_match (')'))        if (lex_match (')'))
228          break;          break;
229      }      }
230      return true;
   c->crit.n[n].type = CNT_SENTINEL;  
   return 1;  
231  }  }
232    
233  /* Parses a set of string criteria values.  The skeleton is the same  /* Parses a set of string criteria values.  Returns success. */
234     as parse_numeric_criteria(). */  static bool
235  static int  parse_string_criteria (struct pool *pool, struct criteria *crit)
 parse_string_criteria (struct counting *c)  
236  {  {
237    int len = 0;    int len = 0;
238      size_t allocated = 0;
   size_t n = 0;  
   size_t m = 0;  
   
239    size_t i;    size_t i;
240    
241    for (i = 0; i < c->n; i++)    for (i = 0; i < crit->var_cnt; i++)
242      if (c->v[i]->width > len)      if (crit->vars[i]->width > len)
243        len = c->v[i]->width;        len = crit->vars[i]->width;
244    
245    c->crit.n = 0;    crit->values.str = NULL;
246    for (;;)    for (;;)
247      {      {
248        struct cnt_str *cur;        char **cur;
249        if (n + 1 >= m)        if (crit->value_cnt >= allocated)
250          {          crit->values.str = pool_2nrealloc (pool, crit->values.str,
251            m += 16;                                             &allocated,
252            c->crit.s = xnrealloc (c->crit.s, m, sizeof *c->crit.s);                                             sizeof *crit->values.str);
         }  
253    
254        if (!lex_force_string ())        if (!lex_force_string ())
255          return 0;          return false;
256        cur = &c->crit.s[n++];        cur = &crit->values.str[crit->value_cnt++];
257        cur->type = CNT_SINGLE;        *cur = pool_alloc (pool, len + 1);
258        cur->s = malloc (len + 1);        str_copy_rpad (*cur, len + 1, ds_c_str (&tokstr));
       str_copy_rpad (cur->s, len + 1, ds_c_str (&tokstr));  
259        lex_get ();        lex_get ();
260    
261        lex_match (',');        lex_match (',');
# Line 360  parse_string_criteria (struct counting * Line 263  parse_string_criteria (struct counting *
263          break;          break;
264      }      }
265    
266    c->crit.s[n].type = CNT_SENTINEL;    return true;
   return 1;  
267  }  }
268    
269  /* Transformation. */  /* Transformation. */
270    
271  /* Counts the number of values in case C matching counting CNT. */  /* Counts the number of values in case C matching CRIT. */
272  static inline int  static inline int
273  count_numeric (struct counting *cnt, struct ccase *c)  count_numeric (struct criteria *crit, struct ccase *c)
274  {  {
275    int counter = 0;    int counter = 0;
276    size_t i;    size_t i;
277    
278    for (i = 0; i < cnt->n; i++)    for (i = 0; i < crit->var_cnt; i++)
279      {      {
280        struct cnt_num *num;        double x = case_num (c, crit->vars[i]->fv);
281          if (x == SYSMIS)
282        /* Extract the variable value and eliminate missing values. */          counter += crit->count_system_missing;
283        double cmp = case_num (c, cnt->v[i]->fv);        else if (crit->count_user_missing
284        if (cmp == SYSMIS)                 && mv_is_num_user_missing (&crit->vars[i]->miss, x))
285          {          counter++;
286            if (cnt->missing >= 1)        else
287              counter++;          {
288            continue;            struct num_value *v;
289          }            
290        if (cnt->missing >= 2 && mv_is_num_user_missing (&cnt->v[i]->miss, cmp))            for (v = crit->values.num; v < crit->values.num + crit->value_cnt;
291          {                 v++)
292            counter++;              if (v->type == CNT_SINGLE ? x == v->a : x >= v->a && x <= v->b)
293            continue;                {
294          }                  counter++;
295                    break;
296        /* Try to find the value in the list. */                }
297        for (num = cnt->crit.n;; num++)          }
         switch (num->type)  
           {  
           case CNT_ERROR:  
             assert (0);  
             break;  
           case CNT_SINGLE:  
             if (cmp != num->a)  
               break;  
             counter++;  
             goto done;  
           case CNT_HIGH:  
             if (cmp < num->a)  
               break;  
             counter++;  
             goto done;  
           case CNT_LOW:  
             if (cmp > num->a)  
               break;  
             counter++;  
             goto done;  
           case CNT_RANGE:  
             if (cmp < num->a || cmp > num->b)  
               break;  
             counter++;  
             goto done;  
           case CNT_ANY:  
             counter++;  
             goto done;  
           case CNT_SENTINEL:  
             goto done;  
           default:  
             assert (0);  
           }  
     done: ;  
298      }      }
299      
300    return counter;    return counter;
301  }  }
302    
303  /* Counts the number of values in case C matching counting CNT. */  /* Counts the number of values in case C matching CRIT. */
304  static inline int  static inline int
305  count_string (struct counting *cnt, struct ccase *c)  count_string (struct criteria *crit, struct ccase *c)
306  {  {
307    int counter = 0;    int counter = 0;
308    size_t i;    size_t i;
309    
310    for (i = 0; i < cnt->n; i++)    for (i = 0; i < crit->var_cnt; i++)
311      {      {
312        struct cnt_str *str;        char **v;
313          for (v = crit->values.str; v < crit->values.str + crit->value_cnt; v++)
314        /* Extract the variable value, variable width. */          if (!memcmp (case_str (c, crit->vars[i]->fv), *v,
315        for (str = cnt->crit.s;; str++)                       crit->vars[i]->width))
316          switch (str->type)            {
           {  
           case CNT_ERROR:  
             assert (0);  
           case CNT_SINGLE:  
             if (memcmp (case_str (c, cnt->v[i]->fv), str->s,  
                         cnt->v[i]->width))  
               break;  
317              counter++;              counter++;
318              goto done;              break;
319            case CNT_SENTINEL:            }
             goto done;  
           default:  
             assert (0);  
           }  
     done: ;  
320      }      }
321    
322    return counter;    return counter;
323  }  }
324    
325  /* Performs the COUNT transformation T on case C. */  /* Performs the COUNT transformation T on case C. */
326  static int  static int
327  count_trns_proc (struct trns_header *trns, struct ccase *c,  count_trns_proc (struct trns_header *trns_, struct ccase *c,
328                   int case_num UNUSED)                   int case_num UNUSED)
329  {  {
330    struct cnt_var_info *info;    struct count_trns *trns = (struct count_trns *) trns_;
331    struct counting *cnt;    struct dst_var *dv;
   int counter;  
332    
333    for (info = ((struct count_trns *) trns)->specs; info; info = info->next)    for (dv = trns->dst_vars; dv; dv = dv->next)
334      {      {
335          struct criteria *crit;
336          int counter;
337    
338        counter = 0;        counter = 0;
339        for (cnt = info->c; cnt; cnt = cnt->next)        for (crit = dv->crit; crit; crit = crit->next)
340          if (cnt->v[0]->type == NUMERIC)          if (crit->vars[0]->type == NUMERIC)
341            counter += count_numeric (cnt, c);            counter += count_numeric (crit, c);
342          else          else
343            counter += count_string (cnt, c);            counter += count_string (crit, c);
344        case_data_rw (c, info->d->fv)->f = counter;        case_data_rw (c, dv->var->fv)->f = counter;
345      }      }
346    return -1;    return -1;
347  }  }
348    
349  /* Destroys all dynamic data structures associated with T. */  /* Destroys all dynamic data structures associated with TRNS. */
350  static void  static void
351  count_trns_free (struct trns_header *t)  count_trns_free (struct trns_header *trns_)
352  {  {
353    struct cnt_var_info *iter, *next;    struct count_trns *trns = (struct count_trns *) trns_;
354      pool_destroy (trns->pool);
   for (iter = ((struct count_trns *) t)->specs; iter; iter = next)  
     {  
       struct counting *i, *n;  
   
       for (i = iter->c; i; i = n)  
         {  
           if (i->n && i->v)  
             {  
               if (i->v[0]->type == NUMERIC)  
                 free (i->crit.n);  
               else  
                 {  
                   struct cnt_str *s;  
   
                   for (s = i->crit.s; s->type != CNT_SENTINEL; s++)  
                     free (s->s);  
                   free (i->crit.s);  
                 }  
             }  
           free (i->v);  
   
           n = i->next;  
           free (i);  
         }  
   
       next = iter->next;  
       free (iter);  
     }  
355  }  }

Legend:
Removed from v.1.22  
changed lines
  Added in v.1.23

savannah-hackers-public@gnu.org
ViewVC Help
Powered by ViewVC 1.1.26