194f9ca6bb2340ca46f9e91e64ae2ff48a35cec9
[pspp] / src / language / stats / autorecode.c
1 /* PSPP - a program for statistical analysis.
2    Copyright (C) 1997-9, 2000, 2009, 2010 Free Software Foundation, Inc.
3
4    This program is free software: you can redistribute it and/or modify
5    it under the terms of the GNU General Public License as published by
6    the Free Software Foundation, either version 3 of the License, or
7    (at your option) any later version.
8
9    This program is distributed in the hope that it will be useful,
10    but WITHOUT ANY WARRANTY; without even the implied warranty of
11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12    GNU General Public License for more details.
13
14    You should have received a copy of the GNU General Public License
15    along with this program.  If not, see <http://www.gnu.org/licenses/>. */
16
17 #include <config.h>
18 #include <stdlib.h>
19
20 #include <data/case.h>
21 #include <data/casereader.h>
22 #include <data/dictionary.h>
23 #include <data/procedure.h>
24 #include <data/transformations.h>
25 #include <data/variable.h>
26 #include <language/command.h>
27 #include <language/lexer/lexer.h>
28 #include <language/lexer/variable-parser.h>
29 #include <libpspp/compiler.h>
30 #include <libpspp/hash.h>
31 #include <libpspp/message.h>
32 #include <libpspp/pool.h>
33 #include <libpspp/str.h>
34
35 #include "xalloc.h"
36
37 #include "gettext.h"
38 #define _(msgid) gettext (msgid)
39
40 /* FIXME: Implement PRINT subcommand. */
41
42 /* An AUTORECODE variable's original value. */
43 union arc_value
44   {
45     double f;                   /* Numeric. */
46     char *c;                    /* Short or long string. */
47   };
48
49 /* Explains how to recode one value.  `from' must be first element.  */
50 struct arc_item
51   {
52     union arc_value from;       /* Original value. */
53     double to;                  /* Recoded value. */
54   };
55
56 /* Explains how to recode an AUTORECODE variable. */
57 struct arc_spec
58   {
59     const struct variable *src; /* Source variable. */
60     struct variable *dest;      /* Target variable. */
61     struct hsh_table *items;    /* Hash table of `freq's. */
62   };
63
64 /* AUTORECODE transformation. */
65 struct autorecode_trns
66   {
67     struct pool *pool;          /* Contains AUTORECODE specs. */
68     struct arc_spec *specs;     /* AUTORECODE specifications. */
69     size_t spec_cnt;            /* Number of specifications. */
70   };
71
72 /* Descending or ascending sort order. */
73 enum direction
74   {
75     ASCENDING,
76     DESCENDING
77   };
78
79 /* AUTORECODE data. */
80 struct autorecode_pgm
81   {
82     const struct variable **src_vars;    /* Source variables. */
83     char **dst_names;              /* Target variable names. */
84     struct variable **dst_vars;    /* Target variables. */
85     struct hsh_table **src_values; /* `union arc_value's of source vars. */
86     size_t var_cnt;                /* Number of variables. */
87     struct pool *src_values_pool;  /* Pool used by src_values. */
88     enum direction direction;      /* Sort order. */
89     int print;                     /* Print mapping table if nonzero. */
90   };
91
92 static trns_proc_func autorecode_trns_proc;
93 static trns_free_func autorecode_trns_free;
94 static hsh_compare_func compare_alpha_value, compare_numeric_value;
95 static hsh_hash_func hash_alpha_value, hash_numeric_value;
96
97 static void recode (struct dataset *, const struct autorecode_pgm *);
98 static void arc_free (struct autorecode_pgm *);
99
100 /* Performs the AUTORECODE procedure. */
101 int
102 cmd_autorecode (struct lexer *lexer, struct dataset *ds)
103 {
104   struct autorecode_pgm arc;
105   struct casereader *input;
106   struct ccase *c;
107   size_t dst_cnt;
108   size_t i;
109   bool ok;
110
111   arc.src_vars = NULL;
112   arc.dst_names = NULL;
113   arc.dst_vars = NULL;
114   arc.src_values = NULL;
115   arc.var_cnt = 0;
116   arc.src_values_pool = NULL;
117   arc.direction = ASCENDING;
118   arc.print = 0;
119   dst_cnt = 0;
120
121   lex_match_id (lexer, "VARIABLES");
122   lex_match (lexer, '=');
123   if (!parse_variables_const (lexer, dataset_dict (ds), &arc.src_vars,
124                               &arc.var_cnt, PV_NO_DUPLICATE))
125     goto lossage;
126   if (!lex_force_match_id (lexer, "INTO"))
127     goto lossage;
128   lex_match (lexer, '=');
129   if (!parse_DATA_LIST_vars (lexer, &arc.dst_names, &dst_cnt, PV_NONE))
130     goto lossage;
131   if (dst_cnt != arc.var_cnt)
132     {
133       size_t i;
134
135       msg (SE, _("Source variable count (%zu) does not match "
136                  "target variable count (%zu)."),
137            arc.var_cnt, dst_cnt);
138
139       for (i = 0; i < dst_cnt; i++)
140         free (arc.dst_names[i]);
141       free (arc.dst_names);
142       arc.dst_names = NULL;
143
144       goto lossage;
145     }
146   while (lex_match (lexer, '/'))
147     if (lex_match_id (lexer, "DESCENDING"))
148       arc.direction = DESCENDING;
149     else if (lex_match_id (lexer, "PRINT"))
150       arc.print = 1;
151   if (lex_token (lexer) != '.')
152     {
153       lex_error (lexer, _("expecting end of command"));
154       goto lossage;
155     }
156
157   for (i = 0; i < arc.var_cnt; i++)
158     {
159       int j;
160
161       if (dict_lookup_var (dataset_dict (ds), arc.dst_names[i]) != NULL)
162         {
163           msg (SE, _("Target variable %s duplicates existing variable %s."),
164                arc.dst_names[i], arc.dst_names[i]);
165           goto lossage;
166         }
167       for (j = 0; j < i; j++)
168         if (!strcasecmp (arc.dst_names[i], arc.dst_names[j]))
169           {
170             msg (SE, _("Duplicate variable name %s among target variables."),
171                  arc.dst_names[i]);
172             goto lossage;
173           }
174     }
175
176   arc.src_values_pool = pool_create ();
177   arc.dst_vars = xnmalloc (arc.var_cnt, sizeof *arc.dst_vars);
178   arc.src_values = xnmalloc (arc.var_cnt, sizeof *arc.src_values);
179   for (i = 0; i < dst_cnt; i++)
180     {
181       /* FIXME: consolodate this hsh_create */
182       if (var_is_alpha (arc.src_vars[i]))
183         arc.src_values[i] = hsh_create (10, compare_alpha_value,
184                                         hash_alpha_value, NULL, arc.src_vars[i]);
185       else
186         arc.src_values[i] = hsh_create (10, compare_numeric_value,
187                                         hash_numeric_value, NULL, NULL);
188     }
189
190   input = proc_open (ds);
191   for (; (c = casereader_read (input)) != NULL; case_unref (c))
192     for (i = 0; i < arc.var_cnt; i++)
193       {
194         union arc_value v, *vp, **vpp;
195
196         if (var_is_numeric (arc.src_vars[i]))
197           v.f = case_num (c, arc.src_vars[i]);
198         else
199           v.c = (char *) case_str (c, arc.src_vars[i]);
200
201         vpp = (union arc_value **) hsh_probe (arc.src_values[i], &v);
202         if (*vpp == NULL)
203           {
204             vp = pool_alloc (arc.src_values_pool, sizeof *vp);
205             if (var_is_numeric (arc.src_vars[i]))
206               vp->f = v.f;
207             else
208               vp->c = pool_clone (arc.src_values_pool,
209                                   v.c, var_get_width (arc.src_vars[i]));
210             *vpp = vp;
211           }
212       }
213   ok = casereader_destroy (input);
214   ok = proc_commit (ds) && ok;
215
216   for (i = 0; i < arc.var_cnt; i++)
217     arc.dst_vars[i] = dict_create_var_assert (dataset_dict (ds),
218                                               arc.dst_names[i], 0);
219
220   recode (ds, &arc);
221   arc_free (&arc);
222   return ok ? CMD_SUCCESS : CMD_CASCADING_FAILURE;
223
224 lossage:
225   arc_free (&arc);
226   return CMD_CASCADING_FAILURE;
227 }
228
229 static void
230 arc_free (struct autorecode_pgm *arc)
231 {
232   free (arc->src_vars);
233   if (arc->dst_names != NULL)
234     {
235       size_t i;
236
237       for (i = 0; i < arc->var_cnt; i++)
238         free (arc->dst_names[i]);
239       free (arc->dst_names);
240     }
241   free (arc->dst_vars);
242   if (arc->src_values != NULL)
243     {
244       size_t i;
245
246       for (i = 0; i < arc->var_cnt; i++)
247         hsh_destroy (arc->src_values[i]);
248       free (arc->src_values);
249     }
250   pool_destroy (arc->src_values_pool);
251 }
252
253 \f
254 /* AUTORECODE transformation. */
255
256 static void
257 recode (struct dataset *ds, const struct autorecode_pgm *arc)
258 {
259   struct autorecode_trns *trns;
260   size_t i;
261
262   trns = pool_create_container (struct autorecode_trns, pool);
263   trns->specs = pool_nalloc (trns->pool, arc->var_cnt, sizeof *trns->specs);
264   trns->spec_cnt = arc->var_cnt;
265   for (i = 0; i < arc->var_cnt; i++)
266     {
267       struct arc_spec *spec = &trns->specs[i];
268       void *const *p = hsh_sort (arc->src_values[i]);
269       int count = hsh_count (arc->src_values[i]);
270       int j;
271
272       spec->src = arc->src_vars[i];
273       spec->dest = arc->dst_vars[i];
274
275       if (var_is_alpha (arc->src_vars[i]))
276         spec->items = hsh_create (2 * count, compare_alpha_value,
277                                   hash_alpha_value, NULL, arc->src_vars[i]);
278       else
279         spec->items = hsh_create (2 * count, compare_numeric_value,
280                                   hash_numeric_value, NULL, NULL);
281
282       for (j = 0; *p; p++, j++)
283         {
284           struct arc_item *item = pool_alloc (trns->pool, sizeof *item);
285           union arc_value *vp = *p;
286
287           if (var_is_numeric (arc->src_vars[i]))
288             item->from.f = vp->f;
289           else
290             item->from.c = pool_clone (trns->pool, vp->c,
291                                        var_get_width (arc->src_vars[i]));
292           item->to = arc->direction == ASCENDING ? j + 1 : count - j;
293           hsh_force_insert (spec->items, item);
294         }
295     }
296   add_transformation (ds,
297                       autorecode_trns_proc, autorecode_trns_free, trns);
298 }
299
300 /* Executes an AUTORECODE transformation. */
301 static int
302 autorecode_trns_proc (void *trns_, struct ccase **c,
303                       casenumber case_idx UNUSED)
304 {
305   struct autorecode_trns *trns = trns_;
306   size_t i;
307
308   *c = case_unshare (*c);
309   for (i = 0; i < trns->spec_cnt; i++)
310     {
311       struct arc_spec *spec = &trns->specs[i];
312       struct arc_item *item;
313       union arc_value v;
314
315       if (var_is_numeric (spec->src))
316         v.f = case_num (*c, spec->src);
317       else
318         v.c = (char *) case_str (*c, spec->src);
319       item = hsh_force_find (spec->items, &v);
320
321       case_data_rw (*c, spec->dest)->f = item->to;
322     }
323   return TRNS_CONTINUE;
324 }
325
326 /* Frees an AUTORECODE transformation. */
327 static bool
328 autorecode_trns_free (void *trns_)
329 {
330   struct autorecode_trns *trns = trns_;
331   size_t i;
332
333   for (i = 0; i < trns->spec_cnt; i++)
334     hsh_destroy (trns->specs[i].items);
335   pool_destroy (trns->pool);
336   return true;
337 }
338 \f
339 /* AUTORECODE procedure. */
340
341 static int
342 compare_alpha_value (const void *a_, const void *b_, const void *v_)
343 {
344   const union arc_value *a = a_;
345   const union arc_value *b = b_;
346   const struct variable *v = v_;
347
348   return memcmp (a->c, b->c, var_get_width (v));
349 }
350
351 static unsigned
352 hash_alpha_value (const void *a_, const void *v_)
353 {
354   const union arc_value *a = a_;
355   const struct variable *v = v_;
356
357   return hash_bytes (a->c, var_get_width (v), 0);
358 }
359
360 static int
361 compare_numeric_value (const void *a_, const void *b_, const void *aux UNUSED)
362 {
363   const union arc_value *a = a_;
364   const union arc_value *b = b_;
365
366   return a->f < b->f ? -1 : a->f > b->f;
367 }
368
369 static unsigned
370 hash_numeric_value (const void *a_, const void *aux UNUSED)
371 {
372   const union arc_value *a = a_;
373
374   return hash_double (a->f, 0);
375 }