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