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