Removed extra free
[pspp-builds.git] / src / count.c
index bbb41e55709828686c15f318331c8002086a653a..26509dcf3ba1e3e1669ff8dada6848144ac41c9a 100644 (file)
 
    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
-   Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
-   02111-1307, USA. */
+   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301, USA. */
 
 #include <config.h>
-#include <assert.h>
+#include "error.h"
 #include <stdlib.h>
 #include "alloc.h"
-#include "approx.h"
+#include "case.h"
 #include "command.h"
+#include "dictionary.h"
 #include "error.h"
 #include "lexer.h"
+#include "pool.h"
+#include "range-prs.h"
 #include "str.h"
 #include "var.h"
 
-/* Implementation details:
-
-   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:
+#include "gettext.h"
+#define _(msgid) gettext (msgid)
 
-   A B
-   1 1
-   1 1
-
-   Perhaps simultaneity could be implemented as an option.  On the
-   other hand, what good are the above commands?  */
-
-#undef DEBUGGING
-/*#define DEBUGGING 1*/
-#include "debug-print.h"
-\f
-/* Definitions. */
-
-enum
+/* Value or range? */
+enum value_type
   {
-    CNT_ERROR,                 /* Invalid value. */
     CNT_SINGLE,                        /* Single value. */
-    CNT_HIGH,                  /* x >= a. */
-    CNT_LOW,                   /* x <= a. */
-    CNT_RANGE,                 /* a <= x <= b. */
-    CNT_ANY,                   /* Count any. */
-    CNT_SENTINEL               /* List terminator. */
+    CNT_RANGE                  /* a <= x <= b. */
   };
 
-struct cnt_num
+/* Numeric count criteria. */
+struct num_value
   {
-    int type;
-    double a, b;
+    enum value_type type;       /* How to interpret a, b. */
+    double a, b;                /* Values to count. */
   };
 
-struct cnt_str
+struct criteria
   {
-    int type;
-    char *s;
-  };
+    struct criteria *next;
 
-struct counting
-  {
-    struct counting *next;
-
-    /* variables to count */
-    struct variable **v;
-    int n;
-
-    /* values to count */
-    int missing;               /* (numeric only)
-                                  0=don't count missing,
-                                  1=count SYSMIS,
-                                  2=count system- and user-missing */
-    union                      /* Criterion values. */
+    /* Variables to count. */
+    struct variable **vars;
+    size_t var_cnt;
+
+    /* Count special values?. */
+    bool count_system_missing;  /* Count system missing? */
+    bool count_user_missing;    /* Count user missing? */
+
+    /* Criterion values. */    
+    size_t value_cnt;
+    union
       {
-       struct cnt_num *n;
-       struct cnt_str *s;
+       struct num_value *num;
+       char **str;
       }
-    crit;
+    values;
   };
 
-struct cnt_var_info
+struct dst_var
   {
-    struct cnt_var_info *next;
-
-    struct variable *d;                /* Destination variable. */
-    char n[9];                 /* Name of dest var. */
-
-    struct counting *c;                /* The counting specifications. */
+    struct dst_var *next;
+    struct variable *var;       /* Destination variable. */
+    char *name;                 /* Name of dest var. */
+    struct criteria *crit;      /* The criteria specifications. */
   };
 
 struct count_trns
   {
-    struct trns_header h;
-    struct cnt_var_info *specs;
+    struct dst_var *dst_vars;
+    struct pool *pool;
   };
 
-#if DEBUGGING
-static void debug_print (void);
-#endif
+static trns_proc_func count_trns_proc;
+static trns_free_func count_trns_free;
 
-/* First counting in chain. */
-static struct cnt_var_info *head;
+static bool parse_numeric_criteria (struct pool *, struct criteria *);
+static bool parse_string_criteria (struct pool *, struct criteria *);
 \f
-/* Parser. */
-
-static int count_trns_proc (struct trns_header *, struct ccase *);
-static void count_trns_free (struct trns_header *);
-
-static int parse_numeric_criteria (struct counting *);
-static int parse_string_criteria (struct counting *);
-
-int cmd_count (void);
-
-int
-internal_cmd_count (void)
-{
-  int code = cmd_count ();
-  if (!code)
-    {
-      struct count_trns c;
-      c.specs = head;
-      count_trns_free ((struct trns_header *) & c);
-    }
-  return code;
-}
-
 int
 cmd_count (void)
 {
-  /* Specification currently being parsed. */
-  struct cnt_var_info *cnt;
-
-  /* Counting currently being parsed. */
-  struct counting *c;
-
-  /* Return value from parsing function. */
-  int ret;
-
-  /* Transformation. */
-  struct count_trns *trns;
-
-  lex_match_id ("COUNT");
+  struct dst_var *dv;           /* Destination var being parsed. */
+  struct count_trns *trns;      /* Transformation. */
 
   /* Parses each slash-delimited specification. */
-  head = cnt = xmalloc (sizeof *cnt);
+  trns = pool_create_container (struct count_trns, pool);
+  trns->dst_vars = dv = pool_alloc (trns->pool, sizeof *dv);
   for (;;)
     {
-      /* Initialize this struct cnt_var_info to ensure proper cleanup. */
-      cnt->next = NULL;
-      cnt->d = NULL;
-      cnt->c = NULL;
+      struct criteria *crit;
+
+      /* Initialize this struct dst_var to ensure proper cleanup. */
+      dv->next = NULL;
+      dv->var = NULL;
+      dv->crit = NULL;
 
-      /* Get destination struct variable, or at least its name. */
+      /* Get destination variable, or at least its name. */
       if (!lex_force_id ())
        goto fail;
-      cnt->d = find_variable (tokid);
-      if (cnt->d)
-       {
-         if (cnt->d->type == ALPHA)
-           {
-             msg (SE, _("Destination cannot be a string variable."));
-             goto fail;
-           }
-       }
+      dv->var = dict_lookup_var (default_dict, tokid);
+      if (dv->var != NULL)
+        {
+          if (dv->var->type == ALPHA)
+            {
+              msg (SE, _("Destination cannot be a string variable."));
+              goto fail;
+            }
+        }
       else
-       strcpy (cnt->n, tokid);
+        dv->name = pool_strdup (trns->pool, tokid);
 
       lex_get ();
       if (!lex_force_match ('='))
        goto fail;
 
-      c = cnt->c = xmalloc (sizeof *c);
+      crit = dv->crit = pool_alloc (trns->pool, sizeof *crit);
       for (;;)
        {
-         c->next = NULL;
-         c->v = NULL;
-         if (!parse_variables (NULL, &c->v, &c->n, PV_DUPLICATE | PV_SAME_TYPE))
+          bool ok;
+          
+         crit->next = NULL;
+         crit->vars = NULL;
+         if (!parse_variables (default_dict, &crit->vars, &crit->var_cnt,
+                                PV_DUPLICATE | PV_SAME_TYPE))
            goto fail;
+          pool_register (trns->pool, free, crit->vars);
 
          if (!lex_force_match ('('))
            goto fail;
 
-         ret = (c->v[0]->type == NUMERIC
-                ? parse_numeric_criteria
-                : parse_string_criteria) (c);
-         if (!ret)
+          crit->value_cnt = 0;
+          if (crit->vars[0]->type == NUMERIC)
+            ok = parse_numeric_criteria (trns->pool, crit);
+          else
+            ok = parse_string_criteria (trns->pool, crit);
+         if (!ok)
            goto fail;
 
          if (token == '/' || token == '.')
            break;
 
-         c = c->next = xmalloc (sizeof *c);
+         crit = crit->next = pool_alloc (trns->pool, sizeof *crit);
        }
 
       if (token == '.')
@@ -225,167 +161,95 @@ cmd_count (void)
 
       if (!lex_force_match ('/'))
        goto fail;
-      cnt = cnt->next = xmalloc (sizeof *cnt);
+      dv = dv->next = pool_alloc (trns->pool, sizeof *dv);
     }
 
   /* Create all the nonexistent destination variables. */
-  for (cnt = head; cnt; cnt = cnt->next)
-    if (!cnt->d)
+  for (dv = trns->dst_vars; dv; dv = dv->next)
+    if (dv->var == NULL)
       {
-       /* It's legal, though motivationally questionable, to count to
+       /* It's valid, though motivationally questionable, to count to
           the same dest var more than once. */
-       cnt->d = find_variable (cnt->n);
+       dv->var = dict_lookup_var (default_dict, dv->name);
 
-       if (!cnt->d)
-         cnt->d = force_create_variable (&default_dict, cnt->n, NUMERIC, 0);
+       if (dv->var == NULL) 
+          dv->var = dict_create_var_assert (default_dict, dv->name, 0);
       }
 
-#if DEBUGGING
-  debug_print ();
-#endif
-
-  trns = xmalloc (sizeof *trns);
-  trns->h.proc = count_trns_proc;
-  trns->h.free = count_trns_free;
-  trns->specs = head;
-  add_transformation ((struct trns_header *) trns);
-
+  add_transformation (count_trns_proc, count_trns_free, trns);
   return CMD_SUCCESS;
 
 fail:
-  {
-    struct count_trns t;
-    t.specs = head;
-    count_trns_free ((struct trns_header *) & t);
-    return CMD_FAILURE;
-  }
+  count_trns_free (trns);
+  return CMD_FAILURE;
 }
 
-/* Parses a set of numeric criterion values. */
-static int
-parse_numeric_criteria (struct counting * c)
+/* Parses a set of numeric criterion values.  Returns success. */
+static bool
+parse_numeric_criteria (struct pool *pool, struct criteria *crit)
 {
-  int n = 0;
-  int m = 0;
+  size_t allocated = 0;
 
-  c->crit.n = 0;
-  c->missing = 0;
+  crit->values.num = NULL;
+  crit->count_system_missing = false;
+  crit->count_user_missing = false;
   for (;;)
     {
-      struct cnt_num *cur;
-      if (n >= m - 1)
-       {
-         m += 16;
-         c->crit.n = xrealloc (c->crit.n, m * sizeof (struct cnt_num));
-       }
-
-      cur = &c->crit.n[n++];
-      if (token == T_NUM)
-       {
-         cur->a = tokval;
-         lex_get ();
-         if (lex_match_id ("THRU"))
-           {
-             if (token == T_NUM)
-               {
-                 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 (token == T_NUM)
-           {
-             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;
-       }
+      double low, high;
+      
+      if (lex_match_id ("SYSMIS"))
+        crit->count_system_missing = true;
       else if (lex_match_id ("MISSING"))
-       c->missing = 2;
+       crit->count_user_missing = true;
+      else if (parse_num_range (&low, &high, NULL)) 
+        {
+          struct num_value *cur;
+
+          if (crit->value_cnt >= allocated)
+            crit->values.num = pool_2nrealloc (pool, crit->values.num,
+                                               &allocated,
+                                               sizeof *crit->values.num);
+          cur = &crit->values.num[crit->value_cnt++];
+          cur->type = low == high ? CNT_SINGLE : CNT_RANGE;
+          cur->a = low;
+          cur->b = high;
+        }
       else
-       {
-         lex_error (NULL);
-         return 0;
-       }
+        return false;
 
       lex_match (',');
       if (lex_match (')'))
        break;
     }
-
-  c->crit.n[n].type = CNT_SENTINEL;
-  return 1;
+  return true;
 }
 
-/* Parses a set of string criteria values.  The skeleton is the same
-   as parse_numeric_criteria(). */
-static int
-parse_string_criteria (struct counting * c)
+/* Parses a set of string criteria values.  Returns success. */
+static bool
+parse_string_criteria (struct pool *pool, struct criteria *crit)
 {
   int len = 0;
+  size_t allocated = 0;
+  size_t i;
 
-  int n = 0;
-  int m = 0;
-
-  int i;
+  for (i = 0; i < crit->var_cnt; i++)
+    if (crit->vars[i]->width > len)
+      len = crit->vars[i]->width;
 
-  for (i = 0; i < c->n; i++)
-    if (c->v[i]->width > len)
-      len = c->v[i]->width;
-
-  c->crit.n = 0;
+  crit->values.str = NULL;
   for (;;)
     {
-      struct cnt_str *cur;
-      if (n >= m - 1)
-       {
-         m += 16;
-         c->crit.n = xrealloc (c->crit.n, m * sizeof (struct cnt_str));
-       }
+      char **cur;
+      if (crit->value_cnt >= allocated)
+        crit->values.str = pool_2nrealloc (pool, crit->values.str,
+                                           &allocated,
+                                           sizeof *crit->values.str);
 
       if (!lex_force_string ())
-       return 0;
-      cur = &c->crit.s[n++];
-      cur->type = CNT_SINGLE;
-      cur->s = malloc (len + 1);
-      st_pad_copy (cur->s, ds_value (&tokstr), len + 1);
+       return false;
+      cur = &crit->values.str[crit->value_cnt++];
+      *cur = pool_alloc (pool, len + 1);
+      str_copy_rpad (*cur, len + 1, ds_c_str (&tokstr));
       lex_get ();
 
       lex_match (',');
@@ -393,249 +257,93 @@ parse_string_criteria (struct counting * c)
        break;
     }
 
-  c->crit.s[n].type = CNT_SENTINEL;
-  return 1;
+  return true;
 }
 \f
 /* Transformation. */
 
-/* Counts the number of values in case C matching counting CNT. */
+/* Counts the number of values in case C matching CRIT. */
 static inline int
-count_numeric (struct counting * cnt, struct ccase * c)
+count_numeric (struct criteria *crit, struct ccase *c)
 {
   int counter = 0;
+  size_t i;
 
-  struct cnt_num *num;
-
-  double cmp;
-  int i;
-
-  for (i = 0; i < cnt->n; i++)
+  for (i = 0; i < crit->var_cnt; i++)
     {
-      /* Extract the variable value and eliminate missing values. */
-      cmp = c->data[cnt->v[i]->fv].f;
-      if (cmp == SYSMIS)
-       {
-         if (cnt->missing >= 1)
-           counter++;
-         continue;
-       }
-      if (cnt->missing >= 2 && is_num_user_missing (cmp, cnt->v[i]))
-       {
-         counter++;
-         continue;
-       }
-
-      /* Try to find the value in the list. */
-      for (num = cnt->crit.n;; num++)
-       switch (num->type)
-         {
-         case CNT_ERROR:
-           assert (0);
-           break;
-         case CNT_SINGLE:
-           if (approx_ne (cmp, num->a))
-             break;
-           counter++;
-           goto done;
-         case CNT_HIGH:
-           if (approx_lt (cmp, num->a))
-             break;
-           counter++;
-           goto done;
-         case CNT_LOW:
-           if (approx_gt (cmp, num->a))
-             break;
-           counter++;
-           goto done;
-         case CNT_RANGE:
-           if (approx_lt (cmp, num->a) || approx_gt (cmp, num->b))
-             break;
-           counter++;
-           goto done;
-         case CNT_ANY:
-           counter++;
-           goto done;
-         case CNT_SENTINEL:
-           goto done;
-         default:
-           assert (0);
-         }
-    done: ;
+      double x = case_num (c, crit->vars[i]->fv);
+      if (x == SYSMIS)
+        counter += crit->count_system_missing;
+      else if (crit->count_user_missing
+               && mv_is_num_user_missing (&crit->vars[i]->miss, x))
+        counter++;
+      else 
+        {
+          struct num_value *v;
+          
+          for (v = crit->values.num; v < crit->values.num + crit->value_cnt;
+               v++) 
+            if (v->type == CNT_SINGLE ? x == v->a : x >= v->a && x <= v->b) 
+              {
+                counter++;
+                break;
+              } 
+        }
     }
+  
   return counter;
 }
 
-/* Counts the number of values in case C matching counting CNT. */
+/* Counts the number of values in case C matching CRIT. */
 static inline int
-count_string (struct counting * cnt, struct ccase * c)
+count_string (struct criteria *crit, struct ccase *c)
 {
   int counter = 0;
+  size_t i;
 
-  struct cnt_str *str;
-
-  char *cmp;
-  int len;
-
-  int i;
-
-  for (i = 0; i < cnt->n; i++)
+  for (i = 0; i < crit->var_cnt; i++)
     {
-      /* Extract the variable value, variable width. */
-      cmp = c->data[cnt->v[i]->fv].s;
-      len = cnt->v[i]->width;
-
-      for (str = cnt->crit.s;; str++)
-       switch (str->type)
-         {
-         case CNT_ERROR:
-           assert (0);
-         case CNT_SINGLE:
-           if (memcmp (cmp, str->s, len))
-             break;
+      char **v;
+      for (v = crit->values.str; v < crit->values.str + crit->value_cnt; v++)
+        if (!memcmp (case_str (c, crit->vars[i]->fv), *v,
+                     crit->vars[i]->width))
+          {
            counter++;
-           goto done;
-         case CNT_SENTINEL:
-           goto done;
-         default:
-           assert (0);
-         }
-    done: ;
+            break;
+          }
     }
+
   return counter;
 }
 
 /* Performs the COUNT transformation T on case C. */
 static int
-count_trns_proc (struct trns_header * trns, struct ccase * c)
+count_trns_proc (void *trns_, struct ccase *c,
+                 int case_num UNUSED)
 {
-  struct cnt_var_info *info;
-  struct counting *cnt;
-  int counter;
+  struct count_trns *trns = trns_;
+  struct dst_var *dv;
 
-  for (info = ((struct count_trns *) trns)->specs; info; info = info->next)
+  for (dv = trns->dst_vars; dv; dv = dv->next)
     {
+      struct criteria *crit;
+      int counter;
+
       counter = 0;
-      for (cnt = info->c; cnt; cnt = cnt->next)
-       if (cnt->v[0]->type == NUMERIC)
-         counter += count_numeric (cnt, c);
+      for (crit = dv->crit; crit; crit = crit->next)
+       if (crit->vars[0]->type == NUMERIC)
+         counter += count_numeric (crit, c);
        else
-         counter += count_string (cnt, c);
-      c->data[info->d->fv].f = counter;
+         counter += count_string (crit, c);
+      case_data_rw (c, dv->var->fv)->f = counter;
     }
   return -1;
 }
 
-/* Destroys all dynamic data structures associated with T. */
-static void
-count_trns_free (struct trns_header * t)
-{
-  struct cnt_var_info *iter, *next;
-
-  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);
-    }
-}
-\f
-/* Debugging. */
-
-#if DEBUGGING
+/* Destroys all dynamic data structures associated with TRNS. */
 static void
-debug_print (void)
+count_trns_free (void *trns_)
 {
-  struct cnt_var_info *iter;
-  struct counting *i;
-  int j;
-
-  printf ("COUNT\n");
-  for (iter = head; iter; iter = iter->next)
-    {
-      printf ("  %s=", iter->d->name);
-      for (i = iter->c; i; i = i->next)
-       {
-         for (j = 0; j < i->n; j++)
-           printf ("%s%s", j ? " " : "", i->v[j]->name);
-         printf (" (");
-         if (i->v[0]->type == NUMERIC)
-           {
-             struct cnt_num *n;
-
-             if (i->missing == 2)
-               printf ("MISSING");
-             else if (i->missing == 1)
-               printf ("SYSMIS");
-             else
-               assert (i->missing == 0);
-
-             for (n = i->crit.n; n->type != CNT_SENTINEL; n++)
-               {
-                 if (i->missing && n != i->crit.n)
-                   printf (",");
-                 switch (n->type)
-                   {
-                   case CNT_SINGLE:
-                     printf ("%g", n->a);
-                     break;
-                   case CNT_HIGH:
-                     printf ("%g THRU HIGH", n->a);
-                     break;
-                   case CNT_LOW:
-                     printf ("LOW THRU %g", n->a);
-                     break;
-                   case CNT_RANGE:
-                     printf ("%g THRU %g", n->a, n->b);
-                     break;
-                   case CNT_ANY:
-                     printf ("LOW THRU HIGH");
-                     break;
-                   default:
-                     printf ("<ERROR %d>", n->type);
-                     break;
-                   }
-               }
-           }
-         else
-           {
-             struct cnt_str *s;
-
-             for (s = i->crit.s; s->type != CNT_SENTINEL; s++)
-               {
-                 if (s != i->crit.s)
-                   printf (",");
-                 if (s->type == CNT_SINGLE)
-                   printf ("'%s'", s->s);
-                 else
-                   printf ("<ERROR %d>", s->type);
-               }
-           }
-         printf (")  ");
-       }
-      printf ("\n");
-    }
+  struct count_trns *trns = (struct count_trns *) trns_;
+  pool_destroy (trns->pool);
 }
-#endif /* DEBUGGING */