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 size_t value_cnt; /* Number of values in map. */
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. */
45 static struct case_map *
46 create_case_map (size_t n)
51 map = xmalloc (sizeof *map);
53 map->map = xnmalloc (n, sizeof *map->map);
54 for (i = 0; i < map->value_cnt; i++)
60 /* Inserts into MAP a mapping of the CNT values starting at FROM
61 to the CNT values starting at TO. */
63 insert_mapping (struct case_map *map, size_t from, size_t to, size_t cnt)
67 assert (to + cnt <= map->value_cnt);
68 for (i = 0; i < cnt; i++)
70 assert (map->map[to + i] == -1);
71 map->map[to + i] = from + i;
75 /* Destroys case map MAP. */
77 case_map_destroy (struct case_map *map)
86 /* If MAP is nonnull, returns a new case that is the result of
87 applying case map MAP to SRC, and unrefs SRC.
89 If MAP is null, returns SRC unchanged. */
91 case_map_execute (const struct case_map *map, struct ccase *src)
98 dst = case_create (map->value_cnt);
99 for (dst_idx = 0; dst_idx < map->value_cnt; dst_idx++)
101 int src_idx = map->map[dst_idx];
103 *case_data_rw_idx (dst, dst_idx) = *case_data_idx (src, src_idx);
112 /* Returns the number of `union value's in cases created by
115 case_map_get_value_cnt (const struct case_map *map)
117 return map->value_cnt;
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_value_cnt (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_value_cnt (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)
191 struct case_map *map;
195 assert ((exclude_classes & ~((1u << DC_ORDINARY)
197 | (1u << DC_SCRATCH))) == 0);
199 map = create_case_map (dict_count_values (d, exclude_classes));
200 var_cnt = dict_get_var_cnt (d);
202 for (i = 0; i < var_cnt; i++)
204 struct variable *v = dict_get_var (d, i);
205 enum dict_class class = dict_class_from_id (var_get_name (v));
207 if (!(exclude_classes & (1u << class)))
209 size_t value_cnt = var_get_value_cnt (v);
210 insert_mapping (map, var_get_case_index (v), value_idx, value_cnt);
211 value_idx += value_cnt;
214 assert (value_idx == map->value_cnt);
219 /* Prepares dictionary D for producing a case map. Afterward,
220 the caller may delete, reorder, or rename variables within D
221 at will before using case_map_from_dict() to produce the case
224 Uses D's aux members, which must otherwise not be in use. */
226 case_map_prepare_dict (const struct dictionary *d)
228 size_t var_cnt = dict_get_var_cnt (d);
231 for (i = 0; i < var_cnt; i++)
233 struct variable *v = dict_get_var (d, i);
234 int *src_fv = xmalloc (sizeof *src_fv);
235 *src_fv = var_get_case_index (v);
236 var_attach_aux (v, src_fv, var_dtor_free);
240 /* Produces a case map from dictionary D, which must have been
241 previously prepared with case_map_prepare_dict().
243 Does not retain any reference to D, and clears the aux members
244 set up by case_map_prepare_dict().
246 Returns the new case map, or a null pointer if no mapping is
247 required (that is, no data has changed position). */
249 case_map_from_dict (const struct dictionary *d)
251 struct case_map *map;
252 size_t var_cnt = dict_get_var_cnt (d);
254 bool identity_map = true;
256 map = create_case_map (dict_get_next_value_idx (d));
257 for (i = 0; i < var_cnt; i++)
259 struct variable *v = dict_get_var (d, i);
260 size_t value_cnt = var_get_value_cnt (v);
261 int *src_fv = (int *) var_detach_aux (v);
263 if (var_get_case_index (v) != *src_fv)
264 identity_map = false;
266 insert_mapping (map, *src_fv, var_get_case_index (v), value_cnt);
273 case_map_destroy (map);
277 while (map->value_cnt > 0 && map->map[map->value_cnt - 1] == -1)
283 /* Creates and returns a case map for mapping variables in OLD to
284 variables in NEW based on their name. For every variable in
285 NEW, there must be a variable in OLD with the same name, type,
288 case_map_by_name (const struct dictionary *old,
289 const struct dictionary *new)
291 struct case_map *map;
292 size_t var_cnt = dict_get_var_cnt (new);
295 map = create_case_map (dict_get_next_value_idx (new));
296 for (i = 0; i < var_cnt; i++)
298 struct variable *nv = dict_get_var (new, i);
299 struct variable *ov = dict_lookup_var_assert (old, var_get_name (nv));
300 assert (var_get_width (nv) == var_get_width (ov));
301 insert_mapping (map, var_get_case_index (ov), var_get_case_index (nv),
302 var_get_value_cnt (ov));
307 /* Prints the mapping represented by case map CM to stdout, for
308 debugging purposes. */
310 case_map_dump (const struct case_map *cm)
313 for (i = 0 ; i < cm->value_cnt; ++i )
314 printf ("%d -> %d\n", i, cm->map[i]);