dictionary: Get rid of case indexes.
[pspp] / src / data / caseinit.c
1 /* PSPP - a program for statistical analysis.
2    Copyright (C) 2007, 2009, 2010, 2011 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/caseinit.h"
20
21 #include <stdbool.h>
22 #include <stdlib.h>
23 #include <string.h>
24
25 #include "data/case.h"
26 #include "data/casereader.h"
27 #include "data/dictionary.h"
28 #include "data/value.h"
29 #include "data/variable.h"
30 #include "libpspp/array.h"
31 #include "libpspp/assertion.h"
32 #include "libpspp/compiler.h"
33
34 #include "gl/xalloc.h"
35 \f
36 /* Initializer list: a set of values to write to locations within
37    a case. */
38
39 /* Binds a value with a place to put it. */
40 struct init_value
41   {
42     size_t case_index;
43     int width;
44     union value value;
45   };
46
47 /* A set of values to initialize in a case. */
48 struct init_list
49   {
50     struct init_value *values;
51     size_t n;
52   };
53
54 /* A bitmap of the "left" status of variables. */
55 enum leave_class
56   {
57     LEAVE_REINIT = 0x001,       /* Reinitialize for every case. */
58     LEAVE_LEFT = 0x002          /* Keep the value from one case to the next. */
59   };
60
61 /* Initializes LIST as an empty initializer list. */
62 static void
63 init_list_create (struct init_list *list)
64 {
65   list->values = NULL;
66   list->n = 0;
67 }
68
69 /* Initializes NEW as a copy of OLD. */
70 static struct init_list
71 init_list_clone (const struct init_list *old)
72 {
73   struct init_value *values = xmemdup (old->values,
74                                        old->n * sizeof *old->values);
75   for (size_t i = 0; i < old->n; i++)
76     {
77       struct init_value *iv = &values[i];
78       value_clone (&iv->value, &iv->value, iv->width);
79     }
80   return (struct init_list) { .values = values, .n = old->n };
81 }
82
83 /* Frees the storage associated with LIST. */
84 static void
85 init_list_destroy (struct init_list *list)
86 {
87   struct init_value *iv;
88
89   for (iv = &list->values[0]; iv < &list->values[list->n]; iv++)
90     value_destroy (&iv->value, iv->width);
91   free (list->values);
92 }
93
94 /* Clears LIST, making it an empty list. */
95 static void
96 init_list_clear (struct init_list *list)
97 {
98   init_list_destroy (list);
99   init_list_create (list);
100 }
101
102 /* Compares `struct init_value's A and B by case_index and
103    returns a strcmp()-type result. */
104 static int
105 compare_init_values (const void *a_, const void *b_, const void *aux UNUSED)
106 {
107   const struct init_value *a = a_;
108   const struct init_value *b = b_;
109
110   return a->case_index < b->case_index ? -1 : a->case_index > b->case_index;
111 }
112
113 /* Returns true if LIST includes CASE_INDEX, false otherwise. */
114 static bool
115 init_list_includes (const struct init_list *list, size_t case_index)
116 {
117   struct init_value value;
118   value.case_index = case_index;
119   return binary_search (list->values, list->n, sizeof *list->values,
120                         &value, compare_init_values, NULL) != NULL;
121 }
122
123 /* Marks LIST to initialize the `union value's for the variables
124    in dictionary D that both (1) fall in the leave class or
125    classes designated by INCLUDE and (2) are not in EXCLUDE. */
126 static void
127 init_list_mark (struct init_list *list, const struct init_list *exclude,
128                 enum leave_class include, const struct dictionary *d)
129 {
130   size_t n_vars = dict_get_n_vars (d);
131
132   assert (list != exclude);
133   list->values = xnrealloc (list->values, list->n + dict_get_n_vars (d),
134                             sizeof *list->values);
135   for (size_t i = 0; i < n_vars; i++)
136     {
137       struct variable *v = dict_get_var (d, i);
138       size_t case_index = var_get_dict_index (v);
139       struct init_value *iv;
140
141       /* Only include the correct class. */
142       if (!(include & (var_get_leave (v) ? LEAVE_LEFT : LEAVE_REINIT)))
143         continue;
144
145       /* Don't include those to be excluded. */
146       if (exclude != NULL && init_list_includes (exclude, case_index))
147         continue;
148
149       iv = &list->values[list->n++];
150       iv->case_index = case_index;
151       iv->width = var_get_width (v);
152       value_init (&iv->value, iv->width);
153       if (var_is_numeric (v) && var_get_leave (v))
154         iv->value.f = 0;
155       else
156         value_set_missing (&iv->value, iv->width);
157     }
158
159   /* Drop duplicates. */
160   list->n = sort_unique (list->values, list->n, sizeof *list->values,
161                          compare_init_values, NULL);
162 }
163
164 /* Initializes data in case C to the values in the initializer
165    LIST. */
166 static void
167 init_list_init (const struct init_list *list, struct ccase *c)
168 {
169   const struct init_value *iv;
170
171   for (iv = &list->values[0]; iv < &list->values[list->n]; iv++)
172     value_copy (case_data_rw_idx (c, iv->case_index), &iv->value, iv->width);
173 }
174
175 /* Updates the values in the initializer LIST from the data in
176    case C. */
177 static void
178 init_list_update (const struct init_list *list, const struct ccase *c)
179 {
180   struct init_value *iv;
181
182   for (iv = &list->values[0]; iv < &list->values[list->n]; iv++)
183     value_copy (&iv->value, case_data_idx (c, iv->case_index), iv->width);
184 }
185 \f
186 /* A case initializer. */
187 struct caseinit
188   {
189     /* Values that do not need to be initialized by the
190        procedure, because they are initialized by the data
191        source. */
192     struct init_list preinited_values;
193
194     /* Values that need to be initialized to SYSMIS or spaces in
195        each case. */
196     struct init_list reinit_values;
197
198     /* Values that need to be initialized to 0 or spaces in the
199        first case and thereafter retain their values from case to
200        case. */
201     struct init_list left_values;
202   };
203
204 /* Creates and returns a new case initializer. */
205 struct caseinit *
206 caseinit_create (void)
207 {
208   struct caseinit *ci = xmalloc (sizeof *ci);
209   init_list_create (&ci->preinited_values);
210   init_list_create (&ci->reinit_values);
211   init_list_create (&ci->left_values);
212   return ci;
213 }
214
215 /* Creates and returns a copy of OLD. */
216 struct caseinit *
217 caseinit_clone (struct caseinit *old)
218 {
219   struct caseinit *new = xmalloc (sizeof *new);
220   *new = (struct caseinit) {
221     .preinited_values = init_list_clone (&old->preinited_values),
222     .reinit_values = init_list_clone (&old->reinit_values),
223     .left_values = init_list_clone (&old->left_values),
224   };
225   return new;
226 }
227
228 /* Clears the contents of case initializer CI. */
229 void
230 caseinit_clear (struct caseinit *ci)
231 {
232   init_list_clear (&ci->preinited_values);
233   init_list_clear (&ci->reinit_values);
234   init_list_clear (&ci->left_values);
235 }
236
237 /* Destroys case initializer CI. */
238 void
239 caseinit_destroy (struct caseinit *ci)
240 {
241   if (ci != NULL)
242     {
243       init_list_destroy (&ci->preinited_values);
244       init_list_destroy (&ci->reinit_values);
245       init_list_destroy (&ci->left_values);
246       free (ci);
247     }
248 }
249
250 /* Marks the variables from dictionary D in CI as being
251    initialized by the data source, so that the case initializer
252    need not initialize them itself. */
253 void
254 caseinit_mark_as_preinited (struct caseinit *ci, const struct dictionary *d)
255 {
256   init_list_mark (&ci->preinited_values, NULL, LEAVE_REINIT | LEAVE_LEFT, d);
257 }
258
259 /* Marks in CI the variables from dictionary D, except for any
260    variables that were already marked with
261    caseinit_mark_as_preinited, as needing initialization
262    according to their leave status. */
263 void
264 caseinit_mark_for_init (struct caseinit *ci, const struct dictionary *d)
265 {
266   init_list_mark (&ci->reinit_values, &ci->preinited_values, LEAVE_REINIT, d);
267   init_list_mark (&ci->left_values, &ci->preinited_values, LEAVE_LEFT, d);
268 }
269
270 /* Initializes variables in *C as described by CI.
271    C must not be shared. */
272 void
273 caseinit_init_vars (const struct caseinit *ci, struct ccase *c)
274 {
275   init_list_init (&ci->reinit_values, c);
276 }
277
278 /* Copies the left vars from CI into C. */
279 void
280 caseinit_restore_left_vars (struct caseinit *ci, struct ccase *c)
281 {
282   init_list_init (&ci->left_values, c);
283 }
284
285 /* Copies the left vars from C into CI. */
286 void
287 caseinit_save_left_vars (struct caseinit *ci, const struct ccase *c)
288 {
289   init_list_update (&ci->left_values, c);
290 }
291 \f
292 struct caseinit_translator
293   {
294     struct init_list reinit_values;
295     struct caseproto *proto;
296   };
297
298 static struct ccase *
299 translate_caseinit (struct ccase *c, void *cit_)
300 {
301   const struct caseinit_translator *cit = cit_;
302
303   c = case_unshare_and_resize (c, cit->proto);
304   init_list_init (&cit->reinit_values, c);
305   return c;
306 }
307
308 static bool
309 translate_destroy (void *cit_)
310 {
311   struct caseinit_translator *cit = cit_;
312
313   init_list_destroy (&cit->reinit_values);
314   caseproto_unref (cit->proto);
315   free (cit);
316
317   return true;
318 }
319
320 /* Returns a new casereader that yields each case from R, resized to match
321    OUTPUT_PROTO and initialized from CI as if with caseinit_init_vars().  Takes
322    ownership of R.
323
324    OUTPUT_PROTO must be conformable with R's prototype.  */
325 struct casereader *
326 caseinit_translate_casereader_to_init_vars (struct caseinit *ci,
327                                             const struct caseproto *output_proto,
328                                             struct casereader *r)
329 {
330   assert (caseproto_is_conformable (casereader_get_proto (r), output_proto));
331   if (caseproto_equal (output_proto, casereader_get_proto (r))
332       && ci->reinit_values.n == 0)
333     return casereader_rename (r);
334
335   struct caseinit_translator *cit = xmalloc (sizeof *cit);
336   *cit = (struct caseinit_translator) {
337     .reinit_values = init_list_clone (&ci->reinit_values),
338     .proto = caseproto_ref (output_proto),
339   };
340
341   static const struct casereader_translator_class class = {
342     .translate = translate_caseinit,
343     .destroy = translate_destroy,
344   };
345   return casereader_translate_stateless (r, output_proto, &class, cit);
346 }
347