Test both compressed and uncompressed system files with very long
[pspp] / src / data / caseinit.c
1 /* PSPP - a program for statistical analysis.
2    Copyright (C) 2007 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/dictionary.h>
27 #include <data/value.h>
28 #include <data/variable.h>
29 #include <libpspp/array.h>
30 #include <libpspp/assertion.h>
31 #include <libpspp/compiler.h>
32
33 #include "xalloc.h"
34 \f
35 /* Initializer list: a set of values to write to locations within
36    a case. */
37
38 /* Binds a value with a place to put it. */
39 struct init_value
40   {
41     union value value;
42     size_t case_index;
43   };
44
45 /* A set of values to initialize in a case. */
46 struct init_list
47   {
48     struct init_value *values;
49     size_t cnt;
50   };
51
52 /* A bitmap of the "left" status of variables. */
53 enum leave_class
54   {
55     LEAVE_REINIT = 0x001,       /* Reinitalize for every case. */
56     LEAVE_LEFT = 0x002          /* Keep the value from one case to the next. */
57   };
58
59 /* Initializes LIST as an empty initializer list. */
60 static void
61 init_list_create (struct init_list *list)
62 {
63   list->values = NULL;
64   list->cnt = 0;
65 }
66
67 /* Frees the storage associated with LIST. */
68 static void
69 init_list_destroy (struct init_list *list)
70 {
71   free (list->values);
72 }
73
74 /* Clears LIST, making it an empty list. */
75 static void
76 init_list_clear (struct init_list *list)
77 {
78   init_list_destroy (list);
79   init_list_create (list);
80 }
81
82 /* Compares `struct init_value's A and B by case_index and
83    returns a strcmp()-type result. */
84 static int
85 compare_init_values (const void *a_, const void *b_, const void *aux UNUSED)
86 {
87   const struct init_value *a = a_;
88   const struct init_value *b = b_;
89
90   return a->case_index < b->case_index ? -1 : a->case_index > b->case_index;
91 }
92
93 /* Returns true if LIST includes CASE_INDEX, false otherwise. */
94 static bool
95 init_list_includes (const struct init_list *list, size_t case_index)
96 {
97   struct init_value value;
98   value.case_index = case_index;
99   return binary_search (list->values, list->cnt, sizeof *list->values,
100                         &value, compare_init_values, NULL) != NULL;
101 }
102
103 /* Marks LIST to initialize the `union value's for the variables
104    in dictionary D that both (1) fall in the leave class or
105    classes designated by INCLUDE and (2) are not in EXCLUDE. */
106 static void
107 init_list_mark (struct init_list *list, const struct init_list *exclude,
108                 enum leave_class include, const struct dictionary *d)
109 {
110   size_t var_cnt = dict_get_var_cnt (d);
111   size_t i;
112
113   assert (list != exclude);
114   list->values = xnrealloc (list->values,
115                             list->cnt + dict_get_next_value_idx (d),
116                             sizeof *list->values);
117   for (i = 0; i < var_cnt; i++)
118     {
119       struct variable *v = dict_get_var (d, i);
120       size_t case_index = var_get_case_index (v);
121       int offset;
122
123       /* Only include the correct class. */
124       if (!(include & (var_get_leave (v) ? LEAVE_LEFT : LEAVE_REINIT)))
125         continue;
126
127       /* Don't include those to be excluded. */
128       if (exclude != NULL && init_list_includes (exclude, case_index))
129         continue;
130
131       offset = 0;
132       do
133         {
134           struct init_value *iv = &list->values[list->cnt++];
135           iv->case_index = case_index++;
136           if (var_is_numeric (v))
137             iv->value.f = var_get_leave (v) ? 0 : SYSMIS;
138           else
139             memset (iv->value.s, ' ', sizeof iv->value.s);
140
141           offset += sizeof iv->value.s;
142         }
143       while (offset < var_get_width (v));
144     }
145
146   /* Drop duplicates. */
147   list->cnt = sort_unique (list->values, list->cnt, sizeof *list->values,
148                            compare_init_values, NULL);
149 }
150
151 /* Initializes data in case C to the values in the initializer
152    LIST. */
153 static void
154 init_list_init (const struct init_list *list, struct ccase *c)
155 {
156   size_t i;
157
158   for (i = 0; i < list->cnt; i++)
159     {
160       const struct init_value *value = &list->values[i];
161       *case_data_rw_idx (c, value->case_index) = value->value;
162     }
163 }
164
165 /* Updates the values in the initializer LIST from the data in
166    case C. */
167 static void
168 init_list_update (const struct init_list *list, const struct ccase *c)
169 {
170   size_t i;
171
172   for (i = 0; i < list->cnt; i++)
173     {
174       struct init_value *value = &list->values[i];
175       value->value = *case_data_idx (c, value->case_index);
176     }
177 }
178 \f
179 /* A case initializer. */
180 struct caseinit
181   {
182     /* Values that do not need to be initialized by the
183        procedure, because they are initialized by the data
184        source. */
185     struct init_list preinited_values;
186
187     /* Values that need to be initialized to SYSMIS or spaces in
188        each case. */
189     struct init_list reinit_values;
190
191     /* Values that need to be initialized to 0 or spaces in the
192        first case and thereafter retain their values from case to
193        case. */
194     struct init_list left_values;
195   };
196
197 /* Creates and returns a new case initializer. */
198 struct caseinit *
199 caseinit_create (void)
200 {
201   struct caseinit *ci = xmalloc (sizeof *ci);
202   init_list_create (&ci->preinited_values);
203   init_list_create (&ci->reinit_values);
204   init_list_create (&ci->left_values);
205   return ci;
206 }
207
208 /* Clears the contents of case initializer CI. */
209 void
210 caseinit_clear (struct caseinit *ci)
211 {
212   init_list_clear (&ci->preinited_values);
213   init_list_clear (&ci->reinit_values);
214   init_list_clear (&ci->left_values);
215 }
216
217 /* Destroys case initializer CI. */
218 void
219 caseinit_destroy (struct caseinit *ci)
220 {
221   if (ci != NULL)
222     {
223       init_list_destroy (&ci->preinited_values);
224       init_list_destroy (&ci->reinit_values);
225       init_list_destroy (&ci->left_values);
226       free (ci);
227     }
228 }
229
230 /* Marks the variables from dictionary D in CI as being
231    initialized by the data source, so that the case initializer
232    need not initialize them itself. */
233 void
234 caseinit_mark_as_preinited (struct caseinit *ci, const struct dictionary *d)
235 {
236   init_list_mark (&ci->preinited_values, NULL, LEAVE_REINIT | LEAVE_LEFT, d);
237 }
238
239 /* Marks in CI the variables from dictionary D, except for any
240    variables that were already marked with
241    caseinit_mark_as_preinited, as needing initialization
242    according to their leave status. */
243 void
244 caseinit_mark_for_init (struct caseinit *ci, const struct dictionary *d)
245 {
246   init_list_mark (&ci->reinit_values, &ci->preinited_values, LEAVE_REINIT, d);
247   init_list_mark (&ci->left_values, &ci->preinited_values, LEAVE_LEFT, d);
248 }
249
250 /* Initializes variables in C as described by CI. */
251 void
252 caseinit_init_vars (const struct caseinit *ci, struct ccase *c)
253 {
254   init_list_init (&ci->reinit_values, c);
255   init_list_init (&ci->left_values, c);
256 }
257
258 /* Updates the left vars in CI from the data in C, so that the
259    next call to caseinit_init_vars will store those values in the
260    next case. */
261 void
262 caseinit_update_left_vars (struct caseinit *ci, const struct ccase *c)
263 {
264   init_list_update (&ci->left_values, c);
265 }
266