1 /* PSPP - a program for statistical analysis.
2 Copyright (C) 1997-9, 2000, 2006, 2007, 2009 Free Software Foundation, Inc.
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.
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.
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/>. */
19 #include <data/case-map.h>
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>
36 struct caseproto *proto; /* Prototype for output cases. */
37 int *map; /* For each destination index, the
38 corresponding source index. */
41 static struct ccase *translate_case (struct ccase *, void *map_);
42 static bool destroy_case_map (void *map_);
44 /* Creates and returns an empty map that outputs cases matching
46 static struct case_map *
47 create_case_map (const struct caseproto *proto)
49 size_t n_values = caseproto_get_n_widths (proto);
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++)
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
66 insert_mapping (struct case_map *map, size_t from, size_t to)
68 assert (to < caseproto_get_n_widths (map->proto));
69 assert (map->map[to] == -1);
73 /* Destroys case map MAP. */
75 case_map_destroy (struct case_map *map)
79 caseproto_unref (map->proto);
85 /* If MAP is nonnull, returns a new case that is the result of
86 applying case map MAP to SRC, and unrefs SRC.
88 If MAP is null, returns SRC unchanged. */
90 case_map_execute (const struct case_map *map, struct ccase *src)
94 size_t n_values = caseproto_get_n_widths (map->proto);
98 dst = case_create (map->proto);
99 for (dst_idx = 0; dst_idx < n_values; dst_idx++)
101 int src_idx = map->map[dst_idx];
103 value_copy (case_data_rw_idx (dst, dst_idx), case_data_idx (src, src_idx), caseproto_get_width (map->proto, dst_idx));
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)
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.
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. */
129 case_map_create_input_translator (struct case_map *map,
130 struct casereader *subreader)
132 return casereader_create_translator (subreader,
133 case_map_get_proto (map),
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
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. */
149 case_map_create_output_translator (struct case_map *map,
150 struct casewriter *subwriter)
152 return casewriter_create_translator (subwriter,
153 case_map_get_proto (map),
159 /* Casereader/casewriter translation callback. */
160 static struct ccase *
161 translate_case (struct ccase *input, void *map_)
163 struct case_map *map = map_;
164 return case_map_execute (map, input);
167 /* Casereader/casewriter destruction callback. */
169 destroy_case_map (void *map_)
171 struct case_map *map = map_;
172 case_map_destroy (map);
176 /* Creates and returns a case_map that can be used to compact
177 cases for dictionary D.
179 Compacting a case eliminates "holes" between values and after
180 the last value. (Holes are created by deleting variables.)
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. */
187 case_map_to_compact_dict (const struct dictionary *d,
188 unsigned int exclude_classes)
190 size_t n_vars = dict_get_var_cnt (d);
191 struct caseproto *proto;
192 struct case_map *map;
196 /* Create the case mapping. */
197 proto = dict_get_compacted_proto (d, exclude_classes);
198 map = create_case_map (proto);
199 caseproto_unref (proto);
201 /* Add the values to the case mapping. */
203 for (i = 0; i < n_vars; i++)
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++);
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
218 Uses D's aux members, which must otherwise not be in use. */
220 case_map_prepare_dict (const struct dictionary *d)
222 size_t var_cnt = dict_get_var_cnt (d);
225 for (i = 0; i < var_cnt; i++)
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);
234 /* Produces a case map from dictionary D, which must have been
235 previously prepared with case_map_prepare_dict().
237 Does not retain any reference to D, and clears the aux members
238 set up by case_map_prepare_dict().
240 Returns the new case map, or a null pointer if no mapping is
241 required (that is, no data has changed position). */
243 case_map_from_dict (const struct dictionary *d)
245 struct case_map *map;
246 size_t var_cnt = dict_get_var_cnt (d);
249 bool identity_map = true;
251 map = create_case_map (dict_get_proto (d));
252 for (i = 0; i < var_cnt; i++)
254 struct variable *v = dict_get_var (d, i);
255 int *src_fv = var_detach_aux (v);
257 if (var_get_case_index (v) != *src_fv)
258 identity_map = false;
260 insert_mapping (map, *src_fv, var_get_case_index (v));
267 case_map_destroy (map);
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);
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,
283 case_map_by_name (const struct dictionary *old,
284 const struct dictionary *new)
286 struct case_map *map;
287 size_t var_cnt = dict_get_var_cnt (new);
290 map = create_case_map (dict_get_proto (new));
291 for (i = 0; i < var_cnt; i++)
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));
301 /* Prints the mapping represented by case map CM to stdout, for
302 debugging purposes. */
304 case_map_dump (const struct case_map *cm)
307 for (i = 0 ; i < caseproto_get_n_widths (cm->proto); ++i )
308 printf ("%d -> %d\n", i, cm->map[i]);