Adopt use of gnulib for portability.
[pspp-builds.git] / src / autorecode.c
index 5ae1e7f6ef79784695593bf689c22cc555ba98cd..1f0fd8a3d6943581c3a91b56d619cf609b0b53a5 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 "case.h"
 #include "command.h"
+#include "dictionary.h"
 #include "error.h"
 #include "hash.h"
 #include "lexer.h"
 #include "var.h"
 #include "vfm.h"
 
-#undef DEBUGGING
-/*#define DEBUGGING 1 */
-#include "debug-print.h"
-
-/* FIXME: This module is less than ideally efficient, both in space
-   and time.  If anyone cares, it would be a good project. */
+#include "gettext.h"
+#define _(msgid) gettext (msgid)
 
 /* FIXME: Implement PRINT subcommand. */
 
@@ -59,205 +57,234 @@ struct autorecode_trns
   {
     struct trns_header h;
     struct pool *owner;                /* Contains AUTORECODE specs. */
-    struct arc_spec *arc;      /* AUTORECODE specifications. */
-    int n_arc;                 /* Number of specifications. */
+    struct arc_spec *specs;    /* AUTORECODE specifications. */
+    int spec_cnt;              /* Number of specifications. */
   };
 
-/* Source and target variables, hash table translator. */
-static struct variable **v_src;
-static struct variable **v_dest;
-static struct hsh_table **h_trans;
-static int nv_src;
+/* Descending or ascending sort order. */
+enum direction 
+  {
+    ASCENDING,
+    DESCENDING
+  };
 
-/* Pool for allocation of hash table entries. */
-static struct pool *hash_pool;
+/* AUTORECODE data. */
+struct autorecode_pgm 
+  {
+    struct variable **src_vars;    /* Source variables. */
+    char **dst_names;              /* Target variable names. */
+    struct variable **dst_vars;    /* Target variables. */
+    struct hsh_table **src_values; /* `union value's of source vars. */
+    int var_cnt;                   /* Number of variables. */
+    struct pool *src_values_pool;  /* Pool used by src_values. */
+    enum direction direction;      /* Sort order. */
+    int print;                     /* Print mapping table if nonzero. */
+  };
 
-/* Options. */
-static int descend;
-static int print;
+static trns_proc_func autorecode_trns_proc;
+static trns_free_func autorecode_trns_free;
+static int autorecode_proc_func (struct ccase *, void *);
+static hsh_compare_func compare_alpha_value, compare_numeric_value;
+static hsh_hash_func hash_alpha_value, hash_numeric_value;
 
-static int autorecode_trns_proc (struct trns_header *, struct ccase *);
-static void autorecode_trns_free (struct trns_header *);
-static int autorecode_proc_func (struct ccase *);
-static int compare_alpha_value (const void *, const void *, void *);
-static unsigned hash_alpha_value (const void *, void *);
-static int compare_numeric_value (const void *, const void *, void *);
-static unsigned hash_numeric_value (const void *, void *);
-static void recode (void);
+static void recode (const struct autorecode_pgm *);
+static void arc_free (struct autorecode_pgm *);
 
 /* Performs the AUTORECODE procedure. */
 int
 cmd_autorecode (void)
 {
-  /* Dest var names. */
-  char **n_dest = NULL;
-  int nv_dest = 0;
-
+  struct autorecode_pgm arc;
+  int dst_cnt;
   int i;
 
-  v_src = NULL;
-  descend = print = 0;
-  h_trans = NULL;
+  arc.src_vars = NULL;
+  arc.dst_names = NULL;
+  arc.dst_vars = NULL;
+  arc.src_values = NULL;
+  arc.var_cnt = 0;
+  arc.src_values_pool = NULL;
+  arc.direction = ASCENDING;
+  arc.print = 0;
+  dst_cnt = 0;
 
-  lex_match_id ("AUTORECODE");
   lex_match_id ("VARIABLES");
   lex_match ('=');
-  if (!parse_variables (&default_dict, &v_src, &nv_src, PV_NO_DUPLICATE))
-    return CMD_FAILURE;
+  if (!parse_variables (default_dict, &arc.src_vars, &arc.var_cnt,
+                        PV_NO_DUPLICATE))
+    goto lossage;
   if (!lex_force_match_id ("INTO"))
-    return CMD_FAILURE;
+    goto lossage;
   lex_match ('=');
-  if (!parse_DATA_LIST_vars (&n_dest, &nv_dest, PV_NONE))
+  if (!parse_DATA_LIST_vars (&arc.dst_names, &dst_cnt, PV_NONE))
     goto lossage;
-  if (nv_dest != nv_src)
+  if (dst_cnt != arc.var_cnt)
     {
-      msg (SE, _("Number of source variables (%d) does not match number "
-          "of target variables (%d)."), nv_src, nv_dest);
+      int i;
+
+      msg (SE, _("Source variable count (%d) does not match "
+                 "target variable count (%d)."), arc.var_cnt, dst_cnt);
+
+      for (i = 0; i < dst_cnt; i++)
+        free (arc.dst_names[i]);
+      free (arc.dst_names);
+      arc.dst_names = NULL;
+
       goto lossage;
     }
   while (lex_match ('/'))
     if (lex_match_id ("DESCENDING"))
-      descend = 1;
+      arc.direction = DESCENDING;
     else if (lex_match_id ("PRINT"))
-      print = 1;
+      arc.print = 1;
   if (token != '.')
     {
       lex_error (_("expecting end of command"));
       goto lossage;
     }
 
-  for (i = 0; i < nv_dest; i++)
+  for (i = 0; i < arc.var_cnt; i++)
     {
       int j;
 
-      if (is_varname (n_dest[i]))
+      if (dict_lookup_var (default_dict, arc.dst_names[i]) != NULL)
        {
          msg (SE, _("Target variable %s duplicates existing variable %s."),
-              n_dest[i], n_dest[i]);
+              arc.dst_names[i], arc.dst_names[i]);
          goto lossage;
        }
       for (j = 0; j < i; j++)
-       if (!strcmp (n_dest[i], n_dest[j]))
+       if (!strcasecmp (arc.dst_names[i], arc.dst_names[j]))
          {
            msg (SE, _("Duplicate variable name %s among target variables."),
-                n_dest[i]);
+                arc.dst_names[i]);
            goto lossage;
          }
     }
 
-  hash_pool = pool_create ();
-
-  v_dest = xmalloc (sizeof *v_dest * nv_dest);
-  h_trans = xmalloc (sizeof *h_trans * nv_dest);
-  for (i = 0; i < nv_dest; i++)
-    if (v_src[i]->type == ALPHA)
-      h_trans[i] = hsh_create (10, compare_alpha_value,
-                              hash_alpha_value, NULL,
-                              (void *) v_src[i]->width);
+  arc.src_values_pool = pool_create ();
+  arc.dst_vars = xmalloc (sizeof *arc.dst_vars * arc.var_cnt);
+  arc.src_values = xmalloc (sizeof *arc.src_values * arc.var_cnt);
+  for (i = 0; i < dst_cnt; i++)
+    if (arc.src_vars[i]->type == ALPHA)
+      arc.src_values[i] = hsh_create (10, compare_alpha_value,
+                                      hash_alpha_value, NULL, arc.src_vars[i]);
     else
-      h_trans[i] = hsh_create (10, compare_numeric_value,
-                              hash_numeric_value, NULL, NULL);
+      arc.src_values[i] = hsh_create (10, compare_numeric_value,
+                                      hash_numeric_value, NULL, NULL);
 
-  procedure (NULL, autorecode_proc_func, NULL);
+  procedure (autorecode_proc_func, &arc);
 
-  for (i = 0; i < nv_dest; i++)
+  for (i = 0; i < arc.var_cnt; i++)
     {
-      v_dest[i] = force_create_variable (&default_dict, n_dest[i], NUMERIC, 0);
-      free (n_dest[i]);
+      arc.dst_vars[i] = dict_create_var_assert (default_dict,
+                                                arc.dst_names[i], 0);
+      arc.dst_vars[i]->init = 0;
     }
-  free (n_dest);
-
-  recode ();
-  
-  free (v_src);
-  free (v_dest);
 
+  recode (&arc);
+  arc_free (&arc);
   return CMD_SUCCESS;
 
 lossage:
-  if (h_trans != NULL)
-    for (i = 0; i < nv_src; i++)
-      hsh_destroy (h_trans[i]);
-  for (i = 0; i < nv_dest; i++)
-    free (n_dest[i]);
-  free (n_dest);
-  free (v_src);
+  arc_free (&arc);
   return CMD_FAILURE;
 }
+
+static void
+arc_free (struct autorecode_pgm *arc) 
+{
+  free (arc->src_vars);
+  if (arc->dst_names != NULL) 
+    {
+      int i;
+      
+      for (i = 0; i < arc->var_cnt; i++)
+        free (arc->dst_names[i]);
+      free (arc->dst_names);
+    }
+  free (arc->dst_vars);
+  if (arc->src_values != NULL) 
+    {
+      int i;
+
+      for (i = 0; i < arc->var_cnt; i++)
+        hsh_destroy (arc->src_values[i]);
+      free (arc->src_values);
+    }
+  pool_destroy (arc->src_values_pool);
+}
+
 \f
 /* AUTORECODE transformation. */
 
 static void
-recode (void)
+recode (const struct autorecode_pgm *arc)
 {
   struct autorecode_trns *t;
-  struct pool *arc_pool;
+  struct pool *pool;
   int i;
 
-  arc_pool = pool_create ();
+  pool = pool_create ();
   t = xmalloc (sizeof *t);
   t->h.proc = autorecode_trns_proc;
   t->h.free = autorecode_trns_free;
-  t->owner = arc_pool;
-  t->arc = pool_alloc (arc_pool, sizeof *t->arc * nv_src);
-  t->n_arc = nv_src;
-  for (i = 0; i < nv_src; i++)
+  t->owner = pool;
+  t->specs = pool_alloc (t->owner, sizeof *t->specs * arc->var_cnt);
+  t->spec_cnt = arc->var_cnt;
+  for (i = 0; i < arc->var_cnt; i++)
     {
-      struct arc_spec *spec = &t->arc[i];
-      void **p = hsh_sort (h_trans[i], NULL);
-      int count = hsh_count (h_trans[i]);
+      struct arc_spec *spec = &t->specs[i];
+      void *const *p = hsh_sort (arc->src_values[i]);
+      int count = hsh_count (arc->src_values[i]);
       int j;
 
-      spec->src = v_src[i];
-      spec->dest = v_dest[i];
+      spec->src = arc->src_vars[i];
+      spec->dest = arc->dst_vars[i];
 
-      if (v_src[i]->type == ALPHA)
+      if (arc->src_vars[i]->type == ALPHA)
        spec->items = hsh_create (2 * count, compare_alpha_value,
-                                 hash_alpha_value, NULL,
-                                 (void *) v_src[i]->width);
+                                 hash_alpha_value, NULL, arc->src_vars[i]);
       else
        spec->items = hsh_create (2 * count, compare_numeric_value,
                                  hash_numeric_value, NULL, NULL);
 
       for (j = 0; *p; p++, j++)
        {
-         struct arc_item *item = pool_alloc (arc_pool, sizeof *item);
-
-         memcpy (&item->from, *p, sizeof (union value));
-         if (v_src[i]->type == ALPHA)
-           item->from.c = pool_strdup (arc_pool, item->from.c);
-         item->to = !descend ? j + 1 : count - j;
-         force_hsh_insert (spec->items, item);
+         struct arc_item *item = pool_alloc (t->owner, sizeof *item);
+          union value *vp = *p;
+          
+         if (arc->src_vars[i]->type == NUMERIC)
+            item->from.f = vp->f;
+          else
+           item->from.c = pool_strdup (t->owner, vp->c);
+         item->to = arc->direction == ASCENDING ? j + 1 : count - j;
+         hsh_force_insert (spec->items, item);
        }
-      
-      hsh_destroy (h_trans[i]);
     }
-  free (h_trans);
-  pool_destroy (hash_pool);
-  add_transformation ((struct trns_header *) t);
+  add_transformation (&t->h);
 }
 
 static int
-autorecode_trns_proc (struct trns_header * trns, struct ccase * c)
+autorecode_trns_proc (struct trns_header * trns, struct ccase * c,
+                      int case_idx UNUSED)
 {
   struct autorecode_trns *t = (struct autorecode_trns *) trns;
   int i;
 
-  for (i = 0; i < t->n_arc; i++)
+  for (i = 0; i < t->spec_cnt; i++)
     {
-      struct arc_spec *spec = &t->arc[i];
+      struct arc_spec *spec = &t->specs[i];
       struct arc_item *item;
+      union value v;
 
       if (spec->src->type == NUMERIC)
-       item = force_hsh_find (spec->items, &c->data[spec->src->fv].f);
+        v.f = case_num (c, spec->src->fv);
       else
-       {
-         union value v;
-         v.c = c->data[spec->src->fv].s;
-         item = force_hsh_find (spec->items, &v);
-       }
+        v.c = (char *) case_str (c, spec->src->fv);
+      item = hsh_force_find (spec->items, &v);
 
-      c->data[spec->dest->fv].f = item->to;
+      case_data_rw (c, spec->dest->fv)->f = item->to;
     }
   return -1;
 }
@@ -268,75 +295,75 @@ autorecode_trns_free (struct trns_header * trns)
   struct autorecode_trns *t = (struct autorecode_trns *) trns;
   int i;
 
-  for (i = 0; i < t->n_arc; i++)
-    hsh_destroy (t->arc[i].items);
+  for (i = 0; i < t->spec_cnt; i++)
+    hsh_destroy (t->specs[i].items);
   pool_destroy (t->owner);
 }
 \f
 /* AUTORECODE procedure. */
 
 static int
-compare_alpha_value (const void *a, const void *b, void *len)
+compare_alpha_value (const void *a_, const void *b_, void *v_)
 {
-  return memcmp (((union value *) a)->c, ((union value *) b)->c, (int) len);
+  const union value *a = a_;
+  const union value *b = b_;
+  const struct variable *v = v_;
+
+  return memcmp (a->c, b->c, v->width);
 }
 
 static unsigned
-hash_alpha_value (const void *a, void *len)
+hash_alpha_value (const void *a_, void *v_)
 {
-  return hashpjw_d (((union value *) a)->c, &((union value *) a)->c[(int) len]);
+  const union value *a = a_;
+  const struct variable *v = v_;
+  
+  return hsh_hash_bytes (a->c, v->width);
 }
 
 static int
-compare_numeric_value (const void *pa, const void *pb, void *foobar unused)
+compare_numeric_value (const void *a_, const void *b_, void *foo UNUSED)
 {
-  double a = ((union value *) pa)->f, b = ((union value *) pb)->f;
-  return a > b ? 1 : (a < b ? -1 : 0);
+  const union value *a = a_;
+  const union value *b = b_;
+
+  return a->f < b->f ? -1 : a->f > b->f;
 }
 
 static unsigned
-hash_numeric_value (const void *a, void *len unused)
+hash_numeric_value (const void *a_, void *foo UNUSED)
 {
-  return hashpjw_d ((char *) &((union value *) a)->f,
-                   (char *) &(&((union value *) a)->f)[1]);
+  const union value *a = a_;
+
+  return hsh_hash_double (a->f);
 }
 
 static int
-autorecode_proc_func (struct ccase * c)
+autorecode_proc_func (struct ccase *c, void *arc_)
 {
+  struct autorecode_pgm *arc = arc_;
   int i;
 
-  for (i = 0; i < nv_src; i++)
+  for (i = 0; i < arc->var_cnt; i++)
     {
-      union value v;
-      union value *vp;
-      union value **vpp;
+      union value v, *vp, **vpp;
 
-      if (v_src[i]->type == NUMERIC)
-       {
-         v.f = c->data[v_src[i]->fv].f;
-         vpp = (union value **) hsh_probe (h_trans[i], &v);
-         if (NULL == *vpp)
-           {
-             vp = pool_alloc (hash_pool, sizeof (union value));
-             vp->f = v.f;
-             *vpp = vp;
-           }
-       }
+      if (arc->src_vars[i]->type == NUMERIC)
+        v.f = case_num (c, arc->src_vars[i]->fv);
       else
-       {
-         v.c = c->data[v_src[i]->fv].s;
-         vpp = (union value **) hsh_probe (h_trans[i], &v);
-         if (NULL == *vpp)
-           {
-             vp = pool_alloc (hash_pool, sizeof (union value));
-#if __CHECKER__
-             memset (vp, 0, sizeof (union value));
-#endif
-             vp->c = pool_strdup (hash_pool, v.c);
-             *vpp = vp;
-           }
-       }
+        v.c = (char *) case_str (c, arc->src_vars[i]->fv);
+
+      vpp = (union value **) hsh_probe (arc->src_values[i], &v);
+      if (*vpp == NULL)
+        {
+          vp = pool_alloc (arc->src_values_pool, sizeof (union value));
+          if (arc->src_vars[i]->type == NUMERIC)
+            vp->f = v.f;
+          else
+            vp->c = pool_strndup (arc->src_values_pool,
+                                  v.c, arc->src_vars[i]->width);
+          *vpp = vp;
+        }
     }
   return 1;
 }