pspp: Don't attempt fancy clean-up upon receiving a fatal signal.
[pspp-builds.git] / src / data / case-map.c
1 /* PSPP - a program for statistical analysis.
2    Copyright (C) 1997-9, 2000, 2006, 2007, 2009 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
31 #include "xalloc.h"
32
33 /* A case map. */
34 struct case_map
35   {
36     struct caseproto *proto;   /* Prototype for output cases. */
37     int *map;                  /* For each destination index, the
38                                   corresponding source index. */
39   };
40
41 static struct ccase *translate_case (struct ccase *, void *map_);
42 static bool destroy_case_map (void *map_);
43
44 /* Creates and returns an empty map that outputs cases matching
45    PROTO. */
46 static struct case_map *
47 create_case_map (const struct caseproto *proto)
48 {
49   size_t n_values = caseproto_get_n_widths (proto);
50   struct case_map *map;
51   size_t i;
52
53   map = xmalloc (sizeof *map);
54   map->proto = caseproto_ref (proto);
55   map->map = xnmalloc (n_values, sizeof *map->map);
56   for (i = 0; i < n_values; i++)
57     map->map[i] = -1;
58
59   return map;
60 }
61
62 /* Inserts into MAP a mapping of the value at index FROM in the
63    source case to the value at index TO in the destination
64    case. */
65 static void
66 insert_mapping (struct case_map *map, size_t from, size_t to)
67 {
68   assert (to < caseproto_get_n_widths (map->proto));
69   assert (map->map[to] == -1);
70   map->map[to] = from;
71 }
72
73 /* Destroys case map MAP. */
74 void
75 case_map_destroy (struct case_map *map)
76 {
77   if (map != NULL)
78     {
79       caseproto_unref (map->proto);
80       free (map->map);
81       free (map);
82     }
83 }
84
85 /* If MAP is nonnull, returns a new case that is the result of
86    applying case map MAP to SRC, and unrefs SRC.
87
88    If MAP is null, returns SRC unchanged. */
89 struct ccase *
90 case_map_execute (const struct case_map *map, struct ccase *src)
91 {
92   if (map != NULL)
93     {
94       size_t n_values = caseproto_get_n_widths (map->proto);
95       struct ccase *dst;
96       size_t dst_idx;
97
98       dst = case_create (map->proto);
99       for (dst_idx = 0; dst_idx < n_values; dst_idx++)
100         {
101           int src_idx = map->map[dst_idx];
102           if (src_idx != -1)
103             value_copy (case_data_rw_idx (dst, dst_idx), case_data_idx (src, src_idx), caseproto_get_width (map->proto, dst_idx));
104         }
105       case_unref (src);
106       return dst;
107     }
108   else
109     return src;
110 }
111
112 /* Returns the prototype for output cases created by MAP.  The
113    caller must not unref the returned case prototype. */
114 const struct caseproto *
115 case_map_get_proto (const struct case_map *map)
116 {
117   return map->proto;
118 }
119
120 /* Creates and returns a new casereader whose cases are produced
121    by reading from SUBREADER and executing the actions of MAP.  
122    The casereader will have as many `union value's as MAP.  When
123    the new casereader is destroyed, MAP will be destroyed too.
124
125    After this function is called, SUBREADER must not ever again
126    be referenced directly.  It will be destroyed automatically
127    when the returned casereader is destroyed. */
128 struct casereader *
129 case_map_create_input_translator (struct case_map *map,
130                                   struct casereader *subreader) 
131 {
132     return casereader_create_translator (subreader,
133                                          case_map_get_proto (map),
134                                          translate_case,
135                                          destroy_case_map,
136                                          map);
137 }
138
139 /* Creates and returns a new casewriter.  Cases written to the
140    new casewriter will be passed through MAP and written to
141    SUBWRITER.  The casewriter will have as many `union value's as
142    MAP.  When the new casewriter is destroyed, MAP will be
143    destroyed too.
144
145    After this function is called, SUBWRITER must not ever again
146    be referenced directly.  It will be destroyed automatically
147    when the returned casewriter is destroyed. */
148 struct casewriter *
149 case_map_create_output_translator (struct case_map *map,
150                                    struct casewriter *subwriter) 
151 {
152     return casewriter_create_translator (subwriter,
153                                          case_map_get_proto (map),
154                                          translate_case,
155                                          destroy_case_map,
156                                          map);
157 }
158
159 /* Casereader/casewriter translation callback. */
160 static struct ccase *
161 translate_case (struct ccase *input, void *map_)
162 {
163   struct case_map *map = map_;
164   return case_map_execute (map, input);
165 }
166
167 /* Casereader/casewriter destruction callback. */
168 static bool
169 destroy_case_map (void *map_)
170 {
171   struct case_map *map = map_;
172   case_map_destroy (map);
173   return true;
174 }
175
176 /* Creates and returns a case_map that can be used to compact
177    cases for dictionary D.
178
179    Compacting a case eliminates "holes" between values and after
180    the last value.  (Holes are created by deleting variables.)
181
182    All variables are compacted if EXCLUDE_CLASSES is 0, or it may
183    contain one or more of (1u << DC_ORDINARY), (1u << DC_SYSTEM),
184    or (1u << DC_SCRATCH) to cause the corresponding type of
185    variable to be deleted during compaction. */
186 struct case_map *
187 case_map_to_compact_dict (const struct dictionary *d,
188                           unsigned int exclude_classes)
189 {
190   size_t n_vars = dict_get_var_cnt (d);
191   struct caseproto *proto;
192   struct case_map *map;
193   size_t n_values;
194   size_t i;
195
196   /* Create the case mapping. */
197   proto = dict_get_compacted_proto (d, exclude_classes);
198   map = create_case_map (proto);
199   caseproto_unref (proto);
200
201   /* Add the values to the case mapping. */
202   n_values = 0;
203   for (i = 0; i < n_vars; i++)
204     {
205       struct variable *v = dict_get_var (d, i);
206       if (!(exclude_classes & (1u << var_get_dict_class (v))))
207         insert_mapping (map, var_get_case_index (v), n_values++);
208     }
209
210   return map;
211 }
212
213 /* Prepares dictionary D for producing a case map.  Afterward,
214    the caller may delete, reorder, or rename variables within D
215    at will before using case_map_from_dict() to produce the case
216    map.
217
218    Uses D's aux members, which must otherwise not be in use. */
219 void
220 case_map_prepare_dict (const struct dictionary *d)
221 {
222   size_t var_cnt = dict_get_var_cnt (d);
223   size_t i;
224
225   for (i = 0; i < var_cnt; i++)
226     {
227       struct variable *v = dict_get_var (d, i);
228       int *src_fv = xmalloc (sizeof *src_fv);
229       *src_fv = var_get_case_index (v);
230       var_attach_aux (v, src_fv, var_dtor_free);
231     }
232 }
233
234 /* Produces a case map from dictionary D, which must have been
235    previously prepared with case_map_prepare_dict().
236
237    Does not retain any reference to D, and clears the aux members
238    set up by case_map_prepare_dict().
239
240    Returns the new case map, or a null pointer if no mapping is
241    required (that is, no data has changed position). */
242 struct case_map *
243 case_map_from_dict (const struct dictionary *d)
244 {
245   struct case_map *map;
246   size_t var_cnt = dict_get_var_cnt (d);
247   size_t n_values;
248   size_t i;
249   bool identity_map = true;
250
251   map = create_case_map (dict_get_proto (d));
252   for (i = 0; i < var_cnt; i++)
253     {
254       struct variable *v = dict_get_var (d, i);
255       int *src_fv = var_detach_aux (v);
256
257       if (var_get_case_index (v) != *src_fv)
258         identity_map = false;
259
260       insert_mapping (map, *src_fv, var_get_case_index (v));
261
262       free (src_fv);
263     }
264
265   if (identity_map)
266     {
267       case_map_destroy (map);
268       return NULL;
269     }
270
271   n_values = caseproto_get_n_widths (map->proto);
272   while (n_values > 0 && caseproto_get_width (map->proto, n_values - 1) == -1)
273     map->proto = caseproto_remove_widths (map->proto, --n_values, 1);
274
275   return map;
276 }
277
278 /* Creates and returns a case map for mapping variables in OLD to
279    variables in NEW based on their name.  For every variable in
280    NEW, there must be a variable in OLD with the same name, type,
281    and width. */
282 struct case_map *
283 case_map_by_name (const struct dictionary *old,
284                   const struct dictionary *new)
285 {
286   struct case_map *map;
287   size_t var_cnt = dict_get_var_cnt (new);
288   size_t i;
289
290   map = create_case_map (dict_get_proto (new));
291   for (i = 0; i < var_cnt; i++)
292     {
293       struct variable *nv = dict_get_var (new, i);
294       struct variable *ov = dict_lookup_var_assert (old, var_get_name (nv));
295       assert (var_get_width (nv) == var_get_width (ov));
296       insert_mapping (map, var_get_case_index (ov), var_get_case_index (nv));
297     }
298   return map;
299 }
300
301 /* Prints the mapping represented by case map CM to stdout, for
302    debugging purposes. */
303 void
304 case_map_dump (const struct case_map *cm)
305 {
306   int i;
307   for (i = 0 ; i < caseproto_get_n_widths (cm->proto); ++i )
308     printf ("%d -> %d\n", i, cm->map[i]);
309 }