27fe0db2ded776f463442aa0fe8c6eaaf235409d
[pspp-builds.git] / src / autorecode.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., 59 Temple Place - Suite 330, Boston, MA
18    02111-1307, USA. */
19
20 #include <config.h>
21 #include <assert.h>
22 #include <stdlib.h>
23 #include "alloc.h"
24 #include "command.h"
25 #include "error.h"
26 #include "hash.h"
27 #include "lexer.h"
28 #include "pool.h"
29 #include "str.h"
30 #include "var.h"
31 #include "vfm.h"
32
33 #include "debug-print.h"
34
35 /* FIXME: This module is less than ideally efficient, both in space
36    and time.  If anyone cares, it would be a good project. */
37
38 /* FIXME: Implement PRINT subcommand. */
39
40 /* Explains how to recode one value.  `from' must be first element.  */
41 struct arc_item
42   {
43     union value from;           /* Original value. */
44     double to;                  /* Recoded value. */
45   };
46
47 /* Explains how to recode an AUTORECODE variable. */
48 struct arc_spec
49   {
50     struct variable *src;       /* Source variable. */
51     struct variable *dest;      /* Target variable. */
52     struct hsh_table *items;    /* Hash table of `freq's. */
53   };
54
55 /* AUTORECODE transformation. */
56 struct autorecode_trns
57   {
58     struct trns_header h;
59     struct pool *owner;         /* Contains AUTORECODE specs. */
60     struct arc_spec *arc;       /* AUTORECODE specifications. */
61     int n_arc;                  /* Number of specifications. */
62   };
63
64 /* Source and target variables, hash table translator. */
65 static struct variable **v_src;
66 static struct variable **v_dest;
67 static struct hsh_table **h_trans;
68 static int nv_src;
69
70 /* Pool for allocation of hash table entries. */
71 static struct pool *hash_pool;
72
73 /* Options. */
74 static int descend;
75 static int print;
76
77 static trns_proc_func autorecode_trns_proc;
78 static trns_free_func autorecode_trns_free;
79 static int autorecode_proc_func (struct ccase *, void *);
80 static hsh_compare_func compare_alpha_value, compare_numeric_value;
81 static hsh_hash_func hash_alpha_value, hash_numeric_value;
82 static void recode (void);
83
84 /* Performs the AUTORECODE procedure. */
85 int
86 cmd_autorecode (void)
87 {
88   /* Dest var names. */
89   char **n_dest = NULL;
90   int nv_dest = 0;
91
92   int i;
93
94   v_src = NULL;
95   descend = print = 0;
96   h_trans = NULL;
97
98   lex_match_id ("AUTORECODE");
99   lex_match_id ("VARIABLES");
100   lex_match ('=');
101   if (!parse_variables (default_dict, &v_src, &nv_src, PV_NO_DUPLICATE))
102     return CMD_FAILURE;
103   if (!lex_force_match_id ("INTO"))
104     return CMD_FAILURE;
105   lex_match ('=');
106   if (!parse_DATA_LIST_vars (&n_dest, &nv_dest, PV_NONE))
107     goto lossage;
108   if (nv_dest != nv_src)
109     {
110       msg (SE, _("Number of source variables (%d) does not match number "
111            "of target variables (%d)."), nv_src, nv_dest);
112       goto lossage;
113     }
114   while (lex_match ('/'))
115     if (lex_match_id ("DESCENDING"))
116       descend = 1;
117     else if (lex_match_id ("PRINT"))
118       print = 1;
119   if (token != '.')
120     {
121       lex_error (_("expecting end of command"));
122       goto lossage;
123     }
124
125   for (i = 0; i < nv_dest; i++)
126     {
127       int j;
128
129       if (dict_lookup_var (default_dict, n_dest[i]) != NULL)
130         {
131           msg (SE, _("Target variable %s duplicates existing variable %s."),
132                n_dest[i], n_dest[i]);
133           goto lossage;
134         }
135       for (j = 0; j < i; j++)
136         if (!strcmp (n_dest[i], n_dest[j]))
137           {
138             msg (SE, _("Duplicate variable name %s among target variables."),
139                  n_dest[i]);
140             goto lossage;
141           }
142     }
143
144   hash_pool = pool_create ();
145
146   v_dest = xmalloc (sizeof *v_dest * nv_dest);
147   h_trans = xmalloc (sizeof *h_trans * nv_dest);
148   for (i = 0; i < nv_dest; i++)
149     if (v_src[i]->type == ALPHA)
150       h_trans[i] = hsh_create (10, compare_alpha_value,
151                                hash_alpha_value, NULL, v_src[i]);
152     else
153       h_trans[i] = hsh_create (10, compare_numeric_value,
154                                hash_numeric_value, NULL, NULL);
155
156   procedure (NULL, autorecode_proc_func, NULL, NULL);
157
158   for (i = 0; i < nv_dest; i++)
159     {
160       v_dest[i] = dict_create_var_assert (default_dict, n_dest[i], 0);
161       v_dest[i]->init = 0;
162       free (n_dest[i]);
163     }
164   free (n_dest);
165
166   recode ();
167   
168   free (v_src);
169   free (v_dest);
170
171   return CMD_SUCCESS;
172
173 lossage:
174   if (h_trans != NULL)
175     for (i = 0; i < nv_src; i++)
176       hsh_destroy (h_trans[i]);
177   for (i = 0; i < nv_dest; i++)
178     free (n_dest[i]);
179   free (n_dest);
180   free (v_src);
181   return CMD_FAILURE;
182 }
183 \f
184 /* AUTORECODE transformation. */
185
186 static void
187 recode (void)
188 {
189   struct autorecode_trns *t;
190   struct pool *arc_pool;
191   int i;
192
193   arc_pool = pool_create ();
194   t = xmalloc (sizeof *t);
195   t->h.proc = autorecode_trns_proc;
196   t->h.free = autorecode_trns_free;
197   t->owner = arc_pool;
198   t->arc = pool_alloc (arc_pool, sizeof *t->arc * nv_src);
199   t->n_arc = nv_src;
200   for (i = 0; i < nv_src; i++)
201     {
202       struct arc_spec *spec = &t->arc[i];
203       void **p = hsh_sort (h_trans[i]);
204       int count = hsh_count (h_trans[i]);
205       int j;
206
207       spec->src = v_src[i];
208       spec->dest = v_dest[i];
209
210       if (v_src[i]->type == ALPHA)
211         spec->items = hsh_create (2 * count, compare_alpha_value,
212                                   hash_alpha_value, NULL, v_src[i]);
213       else
214         spec->items = hsh_create (2 * count, compare_numeric_value,
215                                   hash_numeric_value, NULL, NULL);
216
217       for (j = 0; *p; p++, j++)
218         {
219           struct arc_item *item = pool_alloc (arc_pool, sizeof *item);
220           union value *vp = *p;
221           
222           if (v_src[i]->type == NUMERIC)
223             item->from.f = vp->f;
224           else
225             item->from.c = pool_strdup (arc_pool, vp->c);
226           item->to = !descend ? j + 1 : count - j;
227           hsh_force_insert (spec->items, item);
228         }
229       
230       hsh_destroy (h_trans[i]);
231     }
232   free (h_trans);
233   pool_destroy (hash_pool);
234   add_transformation ((struct trns_header *) t);
235 }
236
237 static int
238 autorecode_trns_proc (struct trns_header * trns, struct ccase * c,
239                       int case_num UNUSED)
240 {
241   struct autorecode_trns *t = (struct autorecode_trns *) trns;
242   int i;
243
244   for (i = 0; i < t->n_arc; i++)
245     {
246       struct arc_spec *spec = &t->arc[i];
247       struct arc_item *item;
248
249       if (spec->src->type == NUMERIC)
250         item = hsh_force_find (spec->items, &c->data[spec->src->fv].f);
251       else
252         {
253           union value v;
254           v.c = c->data[spec->src->fv].s;
255           item = hsh_force_find (spec->items, &v);
256         }
257
258       c->data[spec->dest->fv].f = item->to;
259     }
260   return -1;
261 }
262
263 static void
264 autorecode_trns_free (struct trns_header * trns)
265 {
266   struct autorecode_trns *t = (struct autorecode_trns *) trns;
267   int i;
268
269   for (i = 0; i < t->n_arc; i++)
270     hsh_destroy (t->arc[i].items);
271   pool_destroy (t->owner);
272 }
273 \f
274 /* AUTORECODE procedure. */
275
276 static int
277 compare_alpha_value (const void *a_, const void *b_, void *v_)
278 {
279   const union value *a = a_;
280   const union value *b = b_;
281   const struct variable *v = v_;
282
283   return memcmp (a->c, b->c, v->width);
284 }
285
286 static unsigned
287 hash_alpha_value (const void *a_, void *v_)
288 {
289   const union value *a = a_;
290   const struct variable *v = v_;
291   
292   return hsh_hash_bytes (a->c, v->width);
293 }
294
295 static int
296 compare_numeric_value (const void *a_, const void *b_, void *foo UNUSED)
297 {
298   const union value *a = a_;
299   const union value *b = b_;
300
301   return a->f < b->f ? -1 : a->f > b->f;
302 }
303
304 static unsigned
305 hash_numeric_value (const void *a_, void *foo UNUSED)
306 {
307   const union value *a = a_;
308
309   return hsh_hash_double (a->f);
310 }
311
312 static int
313 autorecode_proc_func (struct ccase *c, void *aux UNUSED)
314 {
315   int i;
316
317   for (i = 0; i < nv_src; i++)
318     {
319       union value v;
320       union value *vp;
321       union value **vpp;
322
323       if (v_src[i]->type == NUMERIC)
324         {
325           v.f = c->data[v_src[i]->fv].f;
326           vpp = (union value **) hsh_probe (h_trans[i], &v);
327           if (*vpp == NULL)
328             {
329               vp = pool_alloc (hash_pool, sizeof (union value));
330               vp->f = v.f;
331               *vpp = vp;
332             }
333         }
334       else
335         {
336           v.c = c->data[v_src[i]->fv].s;
337           vpp = (union value **) hsh_probe (h_trans[i], &v);
338           if (*vpp == NULL)
339             {
340               vp = pool_alloc (hash_pool, sizeof (union value));
341               vp->c = pool_strndup (hash_pool, v.c, v_src[i]->width);
342               *vpp = vp;
343             }
344         }
345     }
346   return 1;
347 }