AUTORECODE: Use "union value" instead of custom "union arc_value".
[pspp-builds.git] / 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
19 #include <stdlib.h>
20
21 #include "data/case.h"
22 #include "data/casereader.h"
23 #include "data/dictionary.h"
24 #include "data/procedure.h"
25 #include "data/transformations.h"
26 #include "data/variable.h"
27 #include "language/command.h"
28 #include "language/lexer/lexer.h"
29 #include "language/lexer/variable-parser.h"
30 #include "libpspp/compiler.h"
31 #include "libpspp/hash.h"
32 #include "libpspp/message.h"
33 #include "libpspp/pool.h"
34 #include "libpspp/str.h"
35
36 #include "gl/xalloc.h"
37
38 #include "gettext.h"
39 #define _(msgid) gettext (msgid)
40
41 /* FIXME: Implement PRINT subcommand. */
42
43 /* Explains how to recode one value.  `from' must be first element.  */
44 struct arc_item
45   {
46     union value from;           /* Original value. */
47     double to;                  /* Recoded value. */
48   };
49
50 /* Explains how to recode an AUTORECODE variable. */
51 struct arc_spec
52   {
53     const struct variable *src; /* Source variable. */
54     struct variable *dest;      /* Target variable. */
55     struct hsh_table *items;    /* Hash table of `freq's. */
56   };
57
58 /* AUTORECODE transformation. */
59 struct autorecode_trns
60   {
61     struct pool *pool;          /* Contains AUTORECODE specs. */
62     struct arc_spec *specs;     /* AUTORECODE specifications. */
63     size_t spec_cnt;            /* Number of specifications. */
64   };
65
66 /* Descending or ascending sort order. */
67 enum direction
68   {
69     ASCENDING,
70     DESCENDING
71   };
72
73 /* AUTORECODE data. */
74 struct autorecode_pgm
75   {
76     const struct variable **src_vars;    /* Source variables. */
77     char **dst_names;              /* Target variable names. */
78     struct variable **dst_vars;    /* Target variables. */
79     struct hsh_table **src_values; /* `union value's of source vars. */
80     size_t var_cnt;                /* Number of variables. */
81     struct pool *src_values_pool;  /* Pool used by src_values. */
82     enum direction direction;      /* Sort order. */
83     int print;                     /* Print mapping table if nonzero. */
84   };
85
86 static trns_proc_func autorecode_trns_proc;
87 static trns_free_func autorecode_trns_free;
88 static hsh_compare_func compare_value;
89 static hsh_hash_func hash_value;
90
91 static void recode (struct dataset *, const struct autorecode_pgm *);
92 static void arc_free (struct autorecode_pgm *);
93
94 /* Performs the AUTORECODE procedure. */
95 int
96 cmd_autorecode (struct lexer *lexer, struct dataset *ds)
97 {
98   struct autorecode_pgm arc;
99   struct casereader *input;
100   struct ccase *c;
101   size_t dst_cnt;
102   size_t i;
103   bool ok;
104
105   arc.src_vars = NULL;
106   arc.dst_names = NULL;
107   arc.dst_vars = NULL;
108   arc.src_values = NULL;
109   arc.var_cnt = 0;
110   arc.src_values_pool = NULL;
111   arc.direction = ASCENDING;
112   arc.print = 0;
113   dst_cnt = 0;
114
115   lex_match_id (lexer, "VARIABLES");
116   lex_match (lexer, '=');
117   if (!parse_variables_const (lexer, dataset_dict (ds), &arc.src_vars,
118                               &arc.var_cnt, PV_NO_DUPLICATE))
119     goto lossage;
120   if (!lex_force_match_id (lexer, "INTO"))
121     goto lossage;
122   lex_match (lexer, '=');
123   if (!parse_DATA_LIST_vars (lexer, &arc.dst_names, &dst_cnt, PV_NONE))
124     goto lossage;
125   if (dst_cnt != arc.var_cnt)
126     {
127       size_t i;
128
129       msg (SE, _("Source variable count (%zu) does not match "
130                  "target variable count (%zu)."),
131            arc.var_cnt, dst_cnt);
132
133       for (i = 0; i < dst_cnt; i++)
134         free (arc.dst_names[i]);
135       free (arc.dst_names);
136       arc.dst_names = NULL;
137
138       goto lossage;
139     }
140   while (lex_match (lexer, '/'))
141     if (lex_match_id (lexer, "DESCENDING"))
142       arc.direction = DESCENDING;
143     else if (lex_match_id (lexer, "PRINT"))
144       arc.print = 1;
145   if (lex_token (lexer) != '.')
146     {
147       lex_error (lexer, _("expecting end of command"));
148       goto lossage;
149     }
150
151   for (i = 0; i < arc.var_cnt; i++)
152     {
153       int j;
154
155       if (dict_lookup_var (dataset_dict (ds), arc.dst_names[i]) != NULL)
156         {
157           msg (SE, _("Target variable %s duplicates existing variable %s."),
158                arc.dst_names[i], arc.dst_names[i]);
159           goto lossage;
160         }
161       for (j = 0; j < i; j++)
162         if (!strcasecmp (arc.dst_names[i], arc.dst_names[j]))
163           {
164             msg (SE, _("Duplicate variable name %s among target variables."),
165                  arc.dst_names[i]);
166             goto lossage;
167           }
168     }
169
170   arc.src_values_pool = pool_create ();
171   arc.dst_vars = xnmalloc (arc.var_cnt, sizeof *arc.dst_vars);
172   arc.src_values = xnmalloc (arc.var_cnt, sizeof *arc.src_values);
173   for (i = 0; i < dst_cnt; i++)
174     arc.src_values[i] = hsh_create (10, compare_value, hash_value, NULL,
175                                     arc.src_vars[i]);
176
177
178   input = proc_open (ds);
179   for (; (c = casereader_read (input)) != NULL; case_unref (c))
180     for (i = 0; i < arc.var_cnt; i++)
181       {
182         const union value *vp;
183         union value **vpp;
184
185         vp = case_data (c, arc.src_vars[i]);
186         vpp = (union value **) hsh_probe (arc.src_values[i], vp);
187         if (*vpp == NULL)
188           {
189             *vpp = pool_alloc (arc.src_values_pool, sizeof **vpp);
190             value_clone_pool (arc.src_values_pool, *vpp, vp,
191                               var_get_width (arc.src_vars[i]));
192           }
193       }
194   ok = casereader_destroy (input);
195   ok = proc_commit (ds) && ok;
196
197   for (i = 0; i < arc.var_cnt; i++)
198     arc.dst_vars[i] = dict_create_var_assert (dataset_dict (ds),
199                                               arc.dst_names[i], 0);
200
201   recode (ds, &arc);
202   arc_free (&arc);
203   return ok ? CMD_SUCCESS : CMD_CASCADING_FAILURE;
204
205 lossage:
206   arc_free (&arc);
207   return CMD_CASCADING_FAILURE;
208 }
209
210 static void
211 arc_free (struct autorecode_pgm *arc)
212 {
213   free (arc->src_vars);
214   if (arc->dst_names != NULL)
215     {
216       size_t i;
217
218       for (i = 0; i < arc->var_cnt; i++)
219         free (arc->dst_names[i]);
220       free (arc->dst_names);
221     }
222   free (arc->dst_vars);
223   if (arc->src_values != NULL)
224     {
225       size_t i;
226
227       for (i = 0; i < arc->var_cnt; i++)
228         hsh_destroy (arc->src_values[i]);
229       free (arc->src_values);
230     }
231   pool_destroy (arc->src_values_pool);
232 }
233
234 \f
235 /* AUTORECODE transformation. */
236
237 static void
238 recode (struct dataset *ds, const struct autorecode_pgm *arc)
239 {
240   struct autorecode_trns *trns;
241   size_t i;
242
243   trns = pool_create_container (struct autorecode_trns, pool);
244   trns->specs = pool_nalloc (trns->pool, arc->var_cnt, sizeof *trns->specs);
245   trns->spec_cnt = arc->var_cnt;
246   for (i = 0; i < arc->var_cnt; i++)
247     {
248       struct arc_spec *spec = &trns->specs[i];
249       void *const *p = hsh_sort (arc->src_values[i]);
250       int count = hsh_count (arc->src_values[i]);
251       int j;
252
253       spec->src = arc->src_vars[i];
254       spec->dest = arc->dst_vars[i];
255       spec->items = hsh_create (2 * count, compare_value, hash_value,
256                                 NULL, arc->src_vars[i]);
257
258       for (j = 0; *p; p++, j++)
259         {
260           struct arc_item *item = pool_alloc (trns->pool, sizeof *item);
261           union value *vp = *p;
262
263           value_clone_pool (trns->pool, &item->from, vp,
264                             var_get_width (arc->src_vars[i]));
265           item->to = arc->direction == ASCENDING ? j + 1 : count - j;
266           hsh_force_insert (spec->items, item);
267         }
268     }
269   add_transformation (ds,
270                       autorecode_trns_proc, autorecode_trns_free, trns);
271 }
272
273 /* Executes an AUTORECODE transformation. */
274 static int
275 autorecode_trns_proc (void *trns_, struct ccase **c,
276                       casenumber case_idx UNUSED)
277 {
278   struct autorecode_trns *trns = trns_;
279   size_t i;
280
281   *c = case_unshare (*c);
282   for (i = 0; i < trns->spec_cnt; i++)
283     {
284       struct arc_spec *spec = &trns->specs[i];
285       struct arc_item *item;
286
287       item = hsh_force_find (spec->items, case_data (*c, spec->src));
288
289       case_data_rw (*c, spec->dest)->f = item->to;
290     }
291   return TRNS_CONTINUE;
292 }
293
294 /* Frees an AUTORECODE transformation. */
295 static bool
296 autorecode_trns_free (void *trns_)
297 {
298   struct autorecode_trns *trns = trns_;
299   size_t i;
300
301   for (i = 0; i < trns->spec_cnt; i++)
302     hsh_destroy (trns->specs[i].items);
303   pool_destroy (trns->pool);
304   return true;
305 }
306 \f
307 /* AUTORECODE procedure. */
308
309 static int
310 compare_value (const void *a_, const void *b_, const void *var_)
311 {
312   const union value *a = a_;
313   const union value *b = b_;
314   const struct variable *var = var_;
315
316   return value_compare_3way (a, b, var_get_width (var));
317 }
318
319 static unsigned
320 hash_value (const void *value_, const void *var_)
321 {
322   const union value *value = value_;
323   const struct variable *var = var_;
324
325   return value_hash (value, var_get_width (var), 0);
326 }