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