AUTORECODE: Do not create value labels when they would be empty.
[pspp] / src / language / stats / autorecode.c
1 /* PSPP - a program for statistical analysis.
2    Copyright (C) 1997-9, 2000, 2009, 2010, 2012, 2013, 2014 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 <float.h>
20 #include <stdlib.h>
21
22 #include "data/case.h"
23 #include "data/casereader.h"
24 #include "data/dataset.h"
25 #include "data/dictionary.h"
26 #include "data/transformations.h"
27 #include "data/variable.h"
28 #include "language/command.h"
29 #include "language/lexer/lexer.h"
30 #include "language/lexer/variable-parser.h"
31 #include "libpspp/array.h"
32 #include "libpspp/compiler.h"
33 #include "libpspp/hash-functions.h"
34 #include "libpspp/hmap.h"
35 #include "libpspp/i18n.h"
36 #include "libpspp/message.h"
37 #include "libpspp/pool.h"
38 #include "libpspp/str.h"
39
40 #include "gl/xalloc.h"
41 #include "gl/c-xvasprintf.h"
42 #include "gl/mbiter.h"
43
44
45 #include "gettext.h"
46 #define _(msgid) gettext (msgid)
47
48 /* FIXME: Implement PRINT subcommand. */
49
50 /* Explains how to recode one value. */
51 struct arc_item
52   {
53     struct hmap_node hmap_node; /* Element in "struct arc_spec" hash table. */
54     union value from;           /* Original value. */
55     int width;                  /* Width of the original value */
56
57     double to;                  /* Recoded value. */
58   };
59
60 /* Explains how to recode an AUTORECODE variable. */
61 struct arc_spec
62   {
63     int width;                  /* Variable width. */
64     int src_idx;                /* Case index of source variable. */
65     struct variable *dst;       /* Target variable. */
66     struct rec_items *items;
67   };
68
69 /* Descending or ascending sort order. */
70 enum arc_direction
71   {
72     ASCENDING,
73     DESCENDING
74   };
75
76 struct rec_items
77 {
78   struct hmap ht;         /* Hash table of "struct arc_item"s. */
79 };
80
81
82
83 /* AUTORECODE data. */
84 struct autorecode_pgm
85 {
86   struct arc_spec *specs;
87   size_t n_specs;
88
89   bool blank_valid;
90 };
91
92 static trns_proc_func autorecode_trns_proc;
93 static trns_free_func autorecode_trns_free;
94
95 static int compare_arc_items (const void *, const void *, const void *aux);
96 static void arc_free (struct autorecode_pgm *);
97 static struct arc_item *find_arc_item (
98   const struct rec_items *, const union value *, int width,
99   size_t hash);
100
101 /* Returns WIDTH with any trailing spaces in VALUE trimmed off (except that a
102    minimum width of 1 is always returned because otherwise the width would
103    indicate a numeric type). */
104 static int
105 value_trim_spaces (const union value *value, int width)
106 {
107   while (width > 1 && value->s[width - 1] == ' ')
108     width--;
109   return width;
110 }
111
112 /* Performs the AUTORECODE procedure. */
113 int
114 cmd_autorecode (struct lexer *lexer, struct dataset *ds)
115 {
116   struct dictionary *dict = dataset_dict (ds);
117
118   const struct variable **src_vars = NULL;
119   size_t n_srcs = 0;
120
121   char **dst_names = NULL;
122   size_t n_dsts = 0;
123
124   enum arc_direction direction = ASCENDING;
125
126   /* Create procedure. */
127   struct autorecode_pgm *arc = xzalloc (sizeof *arc);
128   arc->blank_valid = true;
129
130   /* Parse variable lists. */
131   lex_match_id (lexer, "VARIABLES");
132   lex_match (lexer, T_EQUALS);
133   if (!parse_variables_const (lexer, dict, &src_vars, &n_srcs,
134                               PV_NO_DUPLICATE | PV_NO_SCRATCH))
135     goto error;
136   lex_match (lexer, T_SLASH);
137   if (!lex_force_match_id (lexer, "INTO"))
138     goto error;
139   lex_match (lexer, T_EQUALS);
140   if (!parse_DATA_LIST_vars (lexer, dict, &dst_names, &n_dsts,
141                              PV_NO_DUPLICATE))
142     goto error;
143   if (n_dsts != n_srcs)
144     {
145       msg (SE, _("Source variable count (%zu) does not match "
146                  "target variable count (%zu)."),
147            n_srcs, n_dsts);
148
149       goto error;
150     }
151   for (size_t i = 0; i < n_dsts; i++)
152     {
153       const char *name = dst_names[i];
154
155       if (dict_lookup_var (dict, name) != NULL)
156         {
157           msg (SE, _("Target variable %s duplicates existing variable %s."),
158                name, name);
159           goto error;
160         }
161     }
162
163   /* Parse options. */
164   bool group = false;
165   while (lex_match (lexer, T_SLASH))
166     {
167       if (lex_match_id (lexer, "DESCENDING"))
168         direction = DESCENDING;
169       else if (lex_match_id (lexer, "PRINT"))
170         {
171           /* Not yet implemented. */
172         }
173       else if (lex_match_id (lexer, "GROUP"))
174         group = true;
175       else if (lex_match_id (lexer, "BLANK"))
176         {
177           lex_match (lexer, T_EQUALS);
178           if (lex_match_id (lexer, "VALID"))
179             {
180               arc->blank_valid = true;
181             }
182           else if (lex_match_id (lexer, "MISSING"))
183             {
184               arc->blank_valid = false;
185             }
186           else
187             {
188               lex_error_expecting (lexer, "VALID", "MISSING");
189               goto error;
190             }
191         }
192       else
193         {
194           lex_error_expecting (lexer, "DESCENDING", "PRINT", "GROUP", "BLANK");
195           goto error;
196         }
197     }
198
199   if (lex_token (lexer) != T_ENDCMD)
200     {
201       lex_error (lexer, _("expecting end of command"));
202       goto error;
203     }
204
205   /* If GROUP is specified, verify that the variables are all string or all
206      numeric.  */
207   if (group)
208     {
209       enum val_type type = var_get_type (src_vars[0]);
210       for (size_t i = 1; i < n_dsts; i++)
211         {
212           if (var_get_type (src_vars[i]) != type)
213             {
214               size_t string_idx = type == VAL_STRING ? 0 : i;
215               size_t numeric_idx = type == VAL_STRING ? i : 0;
216               lex_error (lexer, _("With GROUP, variables may not mix string "
217                                   "variables (such as %s) and numeric "
218                                   "variables (such as %s)."),
219                          var_get_name (src_vars[string_idx]),
220                          var_get_name (src_vars[numeric_idx]));
221               goto error;
222             }
223         }
224     }
225
226   /* Allocate all the specs and the rec_items that they point to.
227
228      If GROUP is specified, there is only a single global rec_items, with the
229      maximum width 'width', and all of the specs point to it; otherwise each
230      spec has its own rec_items. */
231   arc->specs = xmalloc (n_dsts * sizeof *arc->specs);
232   arc->n_specs = n_dsts;
233   for (size_t i = 0; i < n_dsts; i++)
234     {
235       struct arc_spec *spec = &arc->specs[i];
236
237       spec->width = var_get_width (src_vars[i]);
238       spec->src_idx = var_get_case_index (src_vars[i]);
239
240       if (group && i > 0)
241         spec->items = arc->specs[0].items;
242       else
243         {
244           spec->items = xzalloc (sizeof (*spec->items));
245           hmap_init (&spec->items->ht);
246         }
247     }
248
249   /* Execute procedure. */
250   struct casereader *input = proc_open (ds);
251   struct ccase *c;
252   for (; (c = casereader_read (input)) != NULL; case_unref (c))
253     for (size_t i = 0; i < arc->n_specs; i++)
254       {
255         struct arc_spec *spec = &arc->specs[i];
256         const union value *value = case_data_idx (c, spec->src_idx);
257         int width = value_trim_spaces (value, spec->width);
258         if (width == 1 && value->s[0] == ' ' && !arc->blank_valid)
259           continue;
260
261         size_t hash = value_hash (value, width, 0);
262         if (find_arc_item (spec->items, value, width, hash))
263           continue;
264
265         struct arc_item *item = xmalloc (sizeof *item);
266         item->width = width;
267         value_clone (&item->from, value, width);
268         hmap_insert (&spec->items->ht, &item->hmap_node, hash);
269       }
270   bool ok = casereader_destroy (input);
271   ok = proc_commit (ds) && ok;
272
273   /* Re-fetch dictionary because it might have changed (if TEMPORARY was in
274      use). */
275   dict = dataset_dict (ds);
276
277   /* Create transformation. */
278   for (size_t i = 0; i < arc->n_specs; i++)
279     {
280       struct arc_spec *spec = &arc->specs[i];
281       struct arc_item **items;
282       struct arc_item *item;
283       size_t n_items;
284       size_t j;
285
286       /* Create destination variable. */
287       spec->dst = dict_create_var_assert (dict, dst_names[i], 0);
288
289       /* Create array of pointers to items. */
290       n_items = hmap_count (&spec->items->ht);
291       items = xmalloc (n_items * sizeof *items);
292       j = 0;
293       HMAP_FOR_EACH (item, struct arc_item, hmap_node, &spec->items->ht)
294         items[j++] = item;
295
296       assert (j == n_items);
297
298       /* Sort array by value. */
299       sort (items, n_items, sizeof *items, compare_arc_items, NULL);
300
301       /* Assign recoded values in sorted order. */
302       for (j = 0; j < n_items; j++)
303         items[j]->to = direction == ASCENDING ? j + 1 : n_items - j;
304
305       /* Add value labels to the destination variable which indicate
306          the source value from whence the new value comes. */
307       for (j = 0; j < n_items; j++)
308         {
309           const union value *from = &items[j]->from;
310           const int src_width = items[j]->width;
311           char *recoded_value;
312           if (src_width > 0)
313             {
314               const char *str = CHAR_CAST_BUG (const char *, from->s);
315
316               recoded_value = recode_string (UTF8, dict_get_encoding (dict),
317                                              str, src_width);
318             }
319           else
320             recoded_value = c_xasprintf ("%.*g", DBL_DIG + 1, from->f);
321
322           /* Remove trailing whitespace. */
323           size_t len = strlen (recoded_value);
324           while (len > 0 && recoded_value[len - 1] == ' ')
325             recoded_value[--len] = '\0';
326
327           /* Add value label, if it would be nonempty. */
328           if (len)
329             {
330               union value to_val = { .f = items[j]->to };
331               var_add_value_label (spec->dst, &to_val, recoded_value);
332             }
333           free (recoded_value);
334         }
335
336       /* Free array. */
337       free (items);
338     }
339   add_transformation (ds, autorecode_trns_proc, autorecode_trns_free, arc);
340
341   for (size_t i = 0; i < n_dsts; i++)
342     free (dst_names[i]);
343   free (dst_names);
344   free (src_vars);
345
346   return ok ? CMD_SUCCESS : CMD_CASCADING_FAILURE;
347
348 error:
349   for (size_t i = 0; i < n_dsts; i++)
350     free (dst_names[i]);
351   free (dst_names);
352   free (src_vars);
353   arc_free (arc);
354   return CMD_CASCADING_FAILURE;
355 }
356
357 static void
358 arc_free (struct autorecode_pgm *arc)
359 {
360   if (arc != NULL)
361     {
362       for (size_t i = 0; i < arc->n_specs; i++)
363         {
364           struct arc_spec *spec = &arc->specs[i];
365           struct arc_item *item, *next;
366
367           HMAP_FOR_EACH_SAFE (item, next, struct arc_item, hmap_node,
368                               &spec->items->ht)
369             {
370               value_destroy (&item->from, item->width);
371               hmap_delete (&spec->items->ht, &item->hmap_node);
372               free (item);
373             }
374         }
375
376       size_t n_rec_items =
377         (arc->n_specs == 1 || arc->specs[0].items == arc->specs[1].items
378          ? 1
379          : arc->n_specs);
380       for (size_t i = 0; i < n_rec_items; i++)
381         {
382           struct arc_spec *spec = &arc->specs[i];
383           hmap_destroy (&spec->items->ht);
384           free (spec->items);
385         }
386
387       free (arc->specs);
388       free (arc);
389     }
390 }
391
392 static struct arc_item *
393 find_arc_item (const struct rec_items *items,
394                const union value *value, int width,
395                size_t hash)
396 {
397   struct arc_item *item;
398
399   HMAP_FOR_EACH_WITH_HASH (item, struct arc_item, hmap_node, hash, &items->ht)
400     if (item->width == width && value_equal (value, &item->from, width))
401       return item;
402   return NULL;
403 }
404
405 static int
406 compare_arc_items (const void *a_, const void *b_, const void *aux UNUSED)
407 {
408   const struct arc_item *const *a = a_;
409   const struct arc_item *const *b = b_;
410   int width_a = (*a)->width;
411   int width_b = (*b)->width;
412
413   if ( width_a == width_b)
414     return value_compare_3way (&(*a)->from, &(*b)->from, width_a);
415
416   if ( width_a == 0 && width_b != 0)
417     return -1;
418
419   if ( width_b == 0 && width_a != 0)
420     return +1;
421
422   return buf_compare_rpad (CHAR_CAST_BUG (const char *, (*a)->from.s), width_a,
423                            CHAR_CAST_BUG (const char *, (*b)->from.s), width_b);
424 }
425
426 static int
427 autorecode_trns_proc (void *arc_, struct ccase **c,
428                       casenumber case_idx UNUSED)
429 {
430   struct autorecode_pgm *arc = arc_;
431
432   *c = case_unshare (*c);
433   for (size_t i = 0; i < arc->n_specs; i++)
434     {
435       const struct arc_spec *spec = &arc->specs[i];
436       const union value *value = case_data_idx (*c, spec->src_idx);
437       int width = value_trim_spaces (value, spec->width);
438       size_t hash = value_hash (value, width, 0);
439       const struct arc_item *item = find_arc_item (spec->items, value, width,
440                                                    hash);
441       case_data_rw (*c, spec->dst)->f = item ? item->to : SYSMIS;
442     }
443
444   return TRNS_CONTINUE;
445 }
446
447 static bool
448 autorecode_trns_free (void *arc_)
449 {
450   struct autorecode_pgm *arc = arc_;
451
452   arc_free (arc);
453   return true;
454 }