5d2a347472df7e185461dbe372f77aefb63682f2
[pspp] / src / data / case-map.c
1 /* PSPP - a program for statistical analysis.
2    Copyright (C) 1997-9, 2000, 2006, 2007, 2009, 2011, 2013 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 "data/case-map.h"
20
21 #include <stdio.h>
22 #include <stdlib.h>
23
24 #include "data/casereader.h"
25 #include "data/casewriter.h"
26 #include "data/dictionary.h"
27 #include "data/variable.h"
28 #include "data/case.h"
29 #include "libpspp/assertion.h"
30 #include "libpspp/hash-functions.h"
31 #include "libpspp/hmap.h"
32
33 #include "gl/xalloc.h"
34
35 /* A case map. */
36 struct case_map
37   {
38     struct caseproto *proto;   /* Prototype for output cases. */
39     int *map;                  /* For each destination index, the
40                                   corresponding source index. */
41   };
42
43 static struct ccase *translate_case (struct ccase *, void *map_);
44 static bool destroy_case_map (void *map_);
45
46 /* Creates and returns an empty map that outputs cases matching
47    PROTO. */
48 static struct case_map *
49 create_case_map (const struct caseproto *proto)
50 {
51   size_t n_values = caseproto_get_n_widths (proto);
52   struct case_map *map;
53   size_t i;
54
55   map = xmalloc (sizeof *map);
56   map->proto = caseproto_ref (proto);
57   map->map = xnmalloc (n_values, sizeof *map->map);
58   for (i = 0; i < n_values; i++)
59     map->map[i] = -1;
60
61   return map;
62 }
63
64 /* Inserts into MAP a mapping of the value at index FROM in the
65    source case to the value at index TO in the destination
66    case. */
67 static void
68 insert_mapping (struct case_map *map, size_t from, size_t to)
69 {
70   assert (to < caseproto_get_n_widths (map->proto));
71   assert (map->map[to] == -1);
72   map->map[to] = from;
73 }
74
75 /* Returns a copy of OLD, if OLD is nonnull, and otherwise returns NULL.  */
76 struct case_map *
77 case_map_clone (const struct case_map *old)
78 {
79   if (!old)
80     return NULL;
81
82   size_t n_values = caseproto_get_n_widths (old->proto);
83   struct case_map *new = xmalloc (sizeof *new);
84   *new = (struct case_map) {
85     .proto = caseproto_ref (old->proto),
86     .map = xmemdup (old->proto, n_values * sizeof *new->map),
87   };
88   return new;
89 }
90
91 /* Destroys case map MAP. */
92 void
93 case_map_destroy (struct case_map *map)
94 {
95   if (map != NULL)
96     {
97       caseproto_unref (map->proto);
98       free (map->map);
99       free (map);
100     }
101 }
102
103 /* If MAP is nonnull, returns a new case that is the result of
104    applying case map MAP to SRC, and unrefs SRC.
105
106    If MAP is null, returns SRC unchanged. */
107 struct ccase *
108 case_map_execute (const struct case_map *map, struct ccase *src)
109 {
110   if (map != NULL)
111     {
112       size_t n_values = caseproto_get_n_widths (map->proto);
113       struct ccase *dst;
114       size_t dst_idx;
115
116       dst = case_create (map->proto);
117       for (dst_idx = 0; dst_idx < n_values; dst_idx++)
118         {
119           int src_idx = map->map[dst_idx];
120           if (src_idx != -1)
121             value_copy (case_data_rw_idx (dst, dst_idx), case_data_idx (src, src_idx), caseproto_get_width (map->proto, dst_idx));
122         }
123       case_unref (src);
124       return dst;
125     }
126   else
127     return src;
128 }
129
130 /* Returns the prototype for output cases created by MAP.  The
131    caller must not unref the returned case prototype. */
132 const struct caseproto *
133 case_map_get_proto (const struct case_map *map)
134 {
135   return map->proto;
136 }
137
138 /* Creates and returns a new casereader whose cases are produced
139    by reading from SUBREADER and executing the actions of MAP.
140    The casereader will have as many `union value's as MAP.  When
141    the new casereader is destroyed, MAP will be destroyed too.
142
143    After this function is called, SUBREADER must not ever again
144    be referenced directly.  It will be destroyed automatically
145    when the returned casereader is destroyed. */
146 struct casereader *
147 case_map_create_input_translator (struct case_map *map,
148                                   struct casereader *subreader)
149 {
150     return casereader_create_translator (subreader,
151                                          case_map_get_proto (map),
152                                          translate_case,
153                                          destroy_case_map,
154                                          map);
155 }
156
157 /* Creates and returns a new casewriter.  Cases written to the
158    new casewriter will be passed through MAP and written to
159    SUBWRITER.  The casewriter will have as many `union value's as
160    MAP.  When the new casewriter is destroyed, MAP will be
161    destroyed too.
162
163    After this function is called, SUBWRITER must not ever again
164    be referenced directly.  It will be destroyed automatically
165    when the returned casewriter is destroyed. */
166 struct casewriter *
167 case_map_create_output_translator (struct case_map *map,
168                                    struct casewriter *subwriter)
169 {
170     return casewriter_create_translator (subwriter,
171                                          case_map_get_proto (map),
172                                          translate_case,
173                                          destroy_case_map,
174                                          map);
175 }
176
177 /* Casereader/casewriter translation callback. */
178 static struct ccase *
179 translate_case (struct ccase *input, void *map_)
180 {
181   struct case_map *map = map_;
182   return case_map_execute (map, input);
183 }
184
185 /* Casereader/casewriter destruction callback. */
186 static bool
187 destroy_case_map (void *map_)
188 {
189   struct case_map *map = map_;
190   case_map_destroy (map);
191   return true;
192 }
193
194 /* Creates and returns a case_map that can be used to compact
195    cases for dictionary D.
196
197    Compacting a case eliminates "holes" between values and after
198    the last value.  (Holes are created by deleting variables.)
199
200    All variables are compacted if EXCLUDE_CLASSES is 0, or it may
201    contain one or more of DC_ORDINARY, DC_SYSTEM, or DC_SCRATCH
202    to cause the corresponding type of variable to be deleted
203    during compaction. */
204 struct case_map *
205 case_map_to_compact_dict (const struct dictionary *d,
206                           unsigned int exclude_classes)
207 {
208   size_t n_vars = dict_get_n_vars (d);
209   struct caseproto *proto;
210   struct case_map *map;
211   size_t n_values;
212   size_t i;
213
214   /* Create the case mapping. */
215   proto = dict_get_compacted_proto (d, exclude_classes);
216   map = create_case_map (proto);
217   caseproto_unref (proto);
218
219   /* Add the values to the case mapping. */
220   n_values = 0;
221   for (i = 0; i < n_vars; i++)
222     {
223       struct variable *v = dict_get_var (d, i);
224       if (!(exclude_classes & var_get_dict_class (v)))
225         insert_mapping (map, var_get_case_index (v), n_values++);
226     }
227
228   return map;
229 }
230 \f
231 struct stage_var
232   {
233     struct hmap_node hmap_node; /* In struct case_map_stage's 'stage_vars'. */
234     const struct variable *var;
235     int case_index;
236   };
237
238 struct case_map_stage
239   {
240     const struct dictionary *dict;
241     struct hmap stage_vars;
242   };
243
244 /* Prepares and returns a "struct case_map_stage" for producing a case map for
245    DICT.  Afterward, the caller may delete, reorder, or rename variables within
246    DICT at will before using case_map_stage_get_case_map() to produce the case
247    map.
248
249    The caller must *not* add new variables to DICT. */
250 struct case_map_stage *
251 case_map_stage_create (const struct dictionary *dict)
252 {
253   size_t n_vars = dict_get_n_vars (dict);
254   struct case_map_stage *stage;
255   size_t i;
256
257   stage = xmalloc (sizeof *stage);
258   stage->dict = dict;
259   hmap_init (&stage->stage_vars);
260
261   for (i = 0; i < n_vars; i++)
262     {
263       const struct variable *var = dict_get_var (dict, i);
264       struct stage_var *stage_var;
265
266       stage_var = xmalloc (sizeof *stage_var);
267       stage_var->var = var;
268       stage_var->case_index = var_get_case_index (var);
269       hmap_insert (&stage->stage_vars, &stage_var->hmap_node,
270                    hash_pointer (var, 0));
271     }
272
273   return stage;
274 }
275
276 /* Destroys STAGE, which was created by case_map_stage_create(). */
277 void
278 case_map_stage_destroy (struct case_map_stage *stage)
279 {
280   if (stage != NULL)
281     {
282       struct stage_var *stage_var, *next_stage_var;
283
284       HMAP_FOR_EACH_SAFE (stage_var, next_stage_var,
285                           struct stage_var, hmap_node, &stage->stage_vars)
286         {
287           hmap_delete (&stage->stage_vars, &stage_var->hmap_node);
288           free (stage_var);
289         }
290       hmap_destroy (&stage->stage_vars);
291       free (stage);
292     }
293 }
294
295 static const struct stage_var *
296 case_map_stage_find_var (const struct case_map_stage *stage,
297                          const struct variable *var)
298 {
299   const struct stage_var *stage_var;
300
301   HMAP_FOR_EACH_IN_BUCKET (stage_var, struct stage_var, hmap_node,
302                            hash_pointer (var, 0), &stage->stage_vars)
303     if (stage_var->var == var)
304       return stage_var;
305
306   /* If the following assertion is reached, it indicates a bug in the
307      case_map_stage client: the client allowed a new variable to be added to
308      the dictionary.  This is not allowed, because of the risk that the new
309      varaible might have the same address as an old variable that has been
310      deleted. */
311   NOT_REACHED ();
312 }
313
314 /* Produces a case map from STAGE, which must have been previously created with
315    case_map_stage_create().  The case map maps from the original case index of
316    the variables in STAGE's dictionary to their current case indexes.
317
318    Returns the new case map, or a null pointer if no mapping is required (that
319    is, no data has changed position). */
320 struct case_map *
321 case_map_stage_get_case_map (const struct case_map_stage *stage)
322 {
323   struct case_map *map;
324   size_t n_vars = dict_get_n_vars (stage->dict);
325   size_t n_values;
326   size_t i;
327   bool identity_map = true;
328
329   map = create_case_map (dict_get_proto (stage->dict));
330   for (i = 0; i < n_vars; i++)
331     {
332       const struct variable *var = dict_get_var (stage->dict, i);
333       const struct stage_var *stage_var = case_map_stage_find_var (stage, var);
334
335       if (var_get_case_index (var) != stage_var->case_index)
336         identity_map = false;
337
338       insert_mapping (map, stage_var->case_index, var_get_case_index (var));
339     }
340
341   if (identity_map)
342     {
343       case_map_destroy (map);
344       return NULL;
345     }
346
347   n_values = caseproto_get_n_widths (map->proto);
348   while (n_values > 0 && caseproto_get_width (map->proto, n_values - 1) == -1)
349     map->proto = caseproto_remove_widths (map->proto, --n_values, 1);
350
351   return map;
352 }
353
354 /* Creates and returns a case map for mapping variables in OLD to
355    variables in NEW based on their name.  For every variable in
356    NEW, there must be a variable in OLD with the same name, type,
357    and width. */
358 struct case_map *
359 case_map_by_name (const struct dictionary *old,
360                   const struct dictionary *new)
361 {
362   size_t n_vars = dict_get_n_vars (new);
363   struct case_map *map = create_case_map (dict_get_proto (new));
364   for (size_t i = 0; i < n_vars; i++)
365     {
366       struct variable *nv = dict_get_var (new, i);
367       struct variable *ov = dict_lookup_var_assert (old, var_get_name (nv));
368       assert (var_get_width (nv) == var_get_width (ov));
369       insert_mapping (map, var_get_case_index (ov), var_get_case_index (nv));
370     }
371   return map;
372 }
373
374 /* Prints the mapping represented by case map CM to stdout, for
375    debugging purposes. */
376 void
377 case_map_dump (const struct case_map *cm)
378 {
379   int i;
380   for (i = 0 ; i < caseproto_get_n_widths (cm->proto); ++i)
381     printf ("%d -> %d\n", i, cm->map[i]);
382 }