gui: Fix behavior of print preview in psppire-output-window.
[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/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 "gl/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     size_t case_index;
42     int width;
43     union value value;
44   };
45
46 /* A set of values to initialize in a case. */
47 struct init_list
48   {
49     struct init_value *values;
50     size_t cnt;
51   };
52
53 /* A bitmap of the "left" status of variables. */
54 enum leave_class
55   {
56     LEAVE_REINIT = 0x001,       /* Reinitialize for every case. */
57     LEAVE_LEFT = 0x002          /* Keep the value from one case to the next. */
58   };
59
60 /* Initializes LIST as an empty initializer list. */
61 static void
62 init_list_create (struct init_list *list)
63 {
64   list->values = NULL;
65   list->cnt = 0;
66 }
67
68 /* Initializes NEW as a copy of OLD. */
69 static void
70 init_list_clone (struct init_list *new, const struct init_list *old)
71 {
72   size_t i;
73
74   new->values = xmemdup (old->values, old->cnt * sizeof *old->values);
75   new->cnt = old->cnt;
76
77   for (i = 0; i < new->cnt; i++)
78     {
79       struct init_value *iv = &new->values[i];
80       value_clone (&iv->value, &iv->value, iv->width);
81     }
82 }
83
84 /* Frees the storage associated with LIST. */
85 static void
86 init_list_destroy (struct init_list *list)
87 {
88   struct init_value *iv;
89
90   for (iv = &list->values[0]; iv < &list->values[list->cnt]; iv++)
91     value_destroy (&iv->value, iv->width);
92   free (list->values);
93 }
94
95 /* Clears LIST, making it an empty list. */
96 static void
97 init_list_clear (struct init_list *list)
98 {
99   init_list_destroy (list);
100   init_list_create (list);
101 }
102
103 /* Compares `struct init_value's A and B by case_index and
104    returns a strcmp()-type result. */
105 static int
106 compare_init_values (const void *a_, const void *b_, const void *aux UNUSED)
107 {
108   const struct init_value *a = a_;
109   const struct init_value *b = b_;
110
111   return a->case_index < b->case_index ? -1 : a->case_index > b->case_index;
112 }
113
114 /* Returns true if LIST includes CASE_INDEX, false otherwise. */
115 static bool
116 init_list_includes (const struct init_list *list, size_t case_index)
117 {
118   struct init_value value;
119   value.case_index = case_index;
120   return binary_search (list->values, list->cnt, sizeof *list->values,
121                         &value, compare_init_values, NULL) != NULL;
122 }
123
124 /* Marks LIST to initialize the `union value's for the variables
125    in dictionary D that both (1) fall in the leave class or
126    classes designated by INCLUDE and (2) are not in EXCLUDE. */
127 static void
128 init_list_mark (struct init_list *list, const struct init_list *exclude,
129                 enum leave_class include, const struct dictionary *d)
130 {
131   size_t var_cnt = dict_get_var_cnt (d);
132   size_t i;
133
134   assert (list != exclude);
135   list->values = xnrealloc (list->values, list->cnt + dict_get_var_cnt (d),
136                             sizeof *list->values);
137   for (i = 0; i < var_cnt; i++)
138     {
139       struct variable *v = dict_get_var (d, i);
140       size_t case_index = var_get_case_index (v);
141       struct init_value *iv;
142
143       /* Only include the correct class. */
144       if (!(include & (var_get_leave (v) ? LEAVE_LEFT : LEAVE_REINIT)))
145         continue;
146
147       /* Don't include those to be excluded. */
148       if (exclude != NULL && init_list_includes (exclude, case_index))
149         continue;
150
151       iv = &list->values[list->cnt++];
152       iv->case_index = case_index;
153       iv->width = var_get_width (v);
154       value_init (&iv->value, iv->width);
155       if (var_is_numeric (v) && var_get_leave (v))
156         iv->value.f = 0;
157       else
158         value_set_missing (&iv->value, iv->width);
159     }
160
161   /* Drop duplicates. */
162   list->cnt = sort_unique (list->values, list->cnt, sizeof *list->values,
163                            compare_init_values, NULL);
164 }
165
166 /* Initializes data in case C to the values in the initializer
167    LIST. */
168 static void
169 init_list_init (const struct init_list *list, struct ccase *c)
170 {
171   const struct init_value *iv;
172
173   for (iv = &list->values[0]; iv < &list->values[list->cnt]; iv++)
174     value_copy (case_data_rw_idx (c, iv->case_index), &iv->value, iv->width);
175 }
176
177 /* Updates the values in the initializer LIST from the data in
178    case C. */
179 static void
180 init_list_update (const struct init_list *list, const struct ccase *c)
181 {
182   struct init_value *iv;
183
184   for (iv = &list->values[0]; iv < &list->values[list->cnt]; iv++)
185     value_copy (&iv->value, case_data_idx (c, iv->case_index), iv->width);
186 }
187 \f
188 /* A case initializer. */
189 struct caseinit
190   {
191     /* Values that do not need to be initialized by the
192        procedure, because they are initialized by the data
193        source. */
194     struct init_list preinited_values;
195
196     /* Values that need to be initialized to SYSMIS or spaces in
197        each case. */
198     struct init_list reinit_values;
199
200     /* Values that need to be initialized to 0 or spaces in the
201        first case and thereafter retain their values from case to
202        case. */
203     struct init_list left_values;
204   };
205
206 /* Creates and returns a new case initializer. */
207 struct caseinit *
208 caseinit_create (void)
209 {
210   struct caseinit *ci = xmalloc (sizeof *ci);
211   init_list_create (&ci->preinited_values);
212   init_list_create (&ci->reinit_values);
213   init_list_create (&ci->left_values);
214   return ci;
215 }
216
217 /* Creates and returns a copy of OLD. */
218 struct caseinit *
219 caseinit_clone (struct caseinit *old)
220 {
221   struct caseinit *new = xmalloc (sizeof *new);
222   init_list_clone (&new->preinited_values, &old->preinited_values);
223   init_list_clone (&new->reinit_values, &old->reinit_values);
224   init_list_clone (&new->left_values, &old->left_values);
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   init_list_init (&ci->left_values, c);
277 }
278
279 /* Updates the left vars in CI from the data in C, so that the
280    next call to caseinit_init_vars will store those values in the
281    next case. */
282 void
283 caseinit_update_left_vars (struct caseinit *ci, const struct ccase *c)
284 {
285   init_list_update (&ci->left_values, c);
286 }
287