Missing value-handling
[pspp-builds.git] / src / missing-values.c
1 /* PSPP - computes sample statistics.
2    Copyright (C) 2005 Free Software Foundation, Inc.
3    Written by Ben Pfaff <blp@gnu.org>.
4
5    This program is free software; you can redistribute it and/or
6    modify it under the terms of the GNU General Public License as
7    published by the Free Software Foundation; either version 2 of the
8    License, or (at your option) any later version.
9
10    This program is distributed in the hope that it will be useful, but
11    WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13    General Public License for more details.
14
15    You should have received a copy of the GNU General Public License
16    along with this program; if not, write to the Free Software
17    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
18    02110-1301, USA. */
19
20 #include <config.h>
21 #include "missing-values.h"
22 #include <assert.h>
23 #include <stdlib.h>
24 #include "str.h"
25
26 /* Initializes MV as a set of missing values for a variable of
27    the given WIDTH.  Although only numeric variables and short
28    string variables may have missing values, WIDTH may be any
29    valid variable width. */
30 void
31 mv_init (struct missing_values *mv, int width) 
32 {
33   assert (width >= 0 && width <= MAX_STRING);
34   mv->type = MV_NONE;
35   mv->width = width;
36 }
37
38 /* Copies SRC to MV. */
39 void
40 mv_copy (struct missing_values *mv, const struct missing_values *src) 
41 {
42   *mv = *src;
43 }
44
45 /* Returns true if MV is an empty set of missing values. */
46 bool
47 mv_is_empty (const struct missing_values *mv) 
48 {
49   return mv->type == MV_NONE;
50 }
51
52 /* Returns the width of the missing values that MV may
53    contain. */
54 int
55 mv_get_width (const struct missing_values *mv)
56 {
57   return mv->width;
58 }
59
60 /* Attempts to add individual value V to the set of missing
61    values MV.  Returns true if successful, false if MV has no
62    more room for missing values.  (Long string variables never
63    accept missing values.) */
64 bool
65 mv_add_value (struct missing_values *mv, const union value *v)
66 {
67   if (mv->width > MAX_SHORT_STRING)
68     return false;
69   switch (mv->type) 
70     {
71     case MV_NONE:
72     case MV_1:
73     case MV_2:
74     case MV_RANGE:
75       mv->values[mv->type & 3] = *v;
76       mv->type++;
77       return true;
78
79     case MV_3:
80     case MV_RANGE_1:
81       return false;
82     }
83   abort ();
84 }
85
86 /* Attempts to add S to the set of string missing values MV.  S
87    must contain exactly as many characters as MV's width.
88    Returns true if successful, false if MV has no more room for
89    missing values.  (Long string variables never accept missing
90    values.) */
91 bool
92 mv_add_str (struct missing_values *mv, const char s[]) 
93 {
94   assert (mv->width > 0);
95   return mv_add_value (mv, (union value *) s);
96 }
97
98 /* Attempts to add D to the set of numeric missing values MV.
99    Returns true if successful, false if MV has no more room for
100    missing values.  */
101 bool
102 mv_add_num (struct missing_values *mv, double d) 
103 {
104   assert (mv->width == 0);
105   return mv_add_value (mv, (union value *) &d);
106 }
107
108 /* Attempts to add range [LOW, HIGH] to the set of numeric
109    missing values MV.  Returns true if successful, false if MV
110    has no room for a range, or if LOW > HIGH. */
111 bool
112 mv_add_num_range (struct missing_values *mv, double low, double high) 
113 {
114   assert (mv->width == 0);
115   if (low > high)
116     return false;
117   switch (mv->type) 
118     {
119     case MV_NONE:
120     case MV_1:
121       mv->values[1].f = low;
122       mv->values[2].f = high;
123       mv->type |= 4;
124       return true;
125
126     case MV_2:
127     case MV_3:
128     case MV_RANGE:
129     case MV_RANGE_1:
130       return false;
131     }
132   abort ();
133 }
134
135 /* Returns true if MV contains an individual value,
136    false if MV is empty (or contains only a range). */
137 bool
138 mv_has_value (struct missing_values *mv)
139 {
140   switch (mv->type) 
141     {
142     case MV_1:
143     case MV_2:
144     case MV_3:
145     case MV_RANGE_1:
146       return true;
147       
148     case MV_NONE:
149     case MV_RANGE:
150       return false;
151     }
152   abort ();
153 }
154
155 /* Removes one individual value from MV and stores it in *V.
156    MV must contain an individual value (as determined by
157    mv_has_value()). */
158 void
159 mv_pop_value (struct missing_values *mv, union value *v) 
160 {
161   assert (mv_has_value (mv));
162   mv->type--;
163   *v = mv->values[mv->type & 3];
164 }
165
166 /* Returns true if MV contains a numeric range,
167    false if MV is empty (or contains only individual values). */
168 bool
169 mv_has_range (struct missing_values *mv) 
170 {
171   switch (mv->type) 
172     {
173     case MV_RANGE:
174     case MV_RANGE_1:
175       return true;
176       
177     case MV_NONE:
178     case MV_1:
179     case MV_2:
180     case MV_3:
181       return false;
182     }
183   abort ();
184 }
185
186 /* Removes the numeric range from MV and stores it in *LOW and
187    *HIGH.  MV must contain a individual range (as determined by
188    mv_has_range()). */
189 void
190 mv_pop_range (struct missing_values *mv, double *low, double *high) 
191 {
192   assert (mv_has_range (mv));
193   *low = mv->values[1].f;
194   *high = mv->values[2].f;
195   mv->type &= 3;
196 }
197
198 /* Returns true if values[IDX] is in use when the `type' member
199    is set to TYPE (in struct missing_values),
200    false otherwise. */
201 static bool
202 using_element (unsigned type, int idx) 
203 {
204   assert (idx >= 0 && idx < 3);
205   
206   switch (type) 
207     {
208     case MV_NONE:
209       return false;
210     case MV_1:
211       return idx < 1;
212     case MV_2:
213       return idx < 2;
214     case MV_3:
215       return true;
216     case MV_RANGE:
217       return idx > 0;
218     case MV_RANGE_1:
219       return true;
220     }
221   abort ();
222 }
223
224 /* Returns true if S contains only spaces between indexes
225    NEW_WIDTH (inclusive) and OLD_WIDTH (exclusive),
226    false otherwise. */
227 static bool
228 can_resize_string (const char *s, int old_width, int new_width) 
229 {
230   int i;
231
232   assert (new_width < old_width);
233   for (i = new_width; i < old_width; i++)
234     if (s[i] != ' ')
235       return false;
236   return true;
237 }
238
239 /* Returns true if MV can be resized to the given WIDTH with
240    mv_resize(), false otherwise.  Resizing to the same width is
241    always possible.  Resizing to a long string WIDTH is only
242    possible if MV is an empty set of missing values; otherwise,
243    resizing to a larger WIDTH is always possible.  Resizing to a
244    shorter width is possible only when each missing value
245    contains only spaces in the characters that will be
246    trimmed. */
247 bool
248 mv_is_resizable (struct missing_values *mv, int width) 
249 {
250   assert ((width == 0) == (mv->width == 0));
251   if (width > MAX_SHORT_STRING && mv->type != MV_NONE)
252     return false;
253   else if (width >= mv->width)
254     return true;
255   else 
256     {
257       int i;
258       
259       for (i = 0; i < 3; i++)
260         if (using_element (mv->type, i)
261             && !can_resize_string (mv->values[i].s, mv->width, width))
262           return false;
263       return true;
264     }
265 }
266
267 /* Resizes MV to the given WIDTH.  WIDTH must fit the constraints
268    explained for mv_is_resizable(). */
269 void
270 mv_resize (struct missing_values *mv, int width) 
271 {
272   assert (mv_is_resizable (mv, width));
273   if (width > mv->width) 
274     {
275       int i;
276       
277       for (i = 0; i < 3; i++)
278         memset (mv->values[i].s + mv->width, ' ', width - mv->width);
279     }
280   mv->width = width;
281 }
282
283 /* Returns true if V is system missing or a missing value in MV,
284    false otherwise. */
285 bool
286 mv_is_value_missing (const struct missing_values *mv, const union value *v)
287 {
288   return (mv->width == 0
289           ? mv_is_num_missing (mv, v->f)
290           : mv_is_str_missing (mv, v->s));
291 }
292
293 /* Returns true if D is system missing or a missing value in MV,
294    false otherwise.
295    MV must be a set of numeric missing values. */
296 bool
297 mv_is_num_missing (const struct missing_values *mv, double d)
298 {
299   assert (mv->width == 0);
300   return d == SYSMIS || mv_is_num_user_missing (mv, d);
301 }
302
303 /* Returns true if S[] is a missing value in MV, false otherwise.
304    MV must be a set of string missing values. 
305    S[] must contain exactly as many characters as MV's width. */
306 bool
307 mv_is_str_missing (const struct missing_values *mv, const char s[])
308 {
309   return mv_is_str_user_missing (mv, s);
310 }
311
312 /* Returns true if V is a missing value in MV, false otherwise. */
313 bool
314 mv_is_value_user_missing (const struct missing_values *mv,
315                           const union value *v)
316 {
317   return (mv->width == 0
318           ? mv_is_num_user_missing (mv, v->f)
319           : mv_is_str_user_missing (mv, v->s));
320 }
321
322 /* Returns true if D is a missing value in MV, false otherwise.
323    MV must be a set of numeric missing values. */
324 bool
325 mv_is_num_user_missing (const struct missing_values *mv, double d)
326 {
327   const union value *v = mv->values;
328   assert (mv->width == 0);
329   switch (mv->type) 
330     {
331     case MV_NONE:
332       return false;
333     case MV_1:
334       return v[0].f == d;
335     case MV_2:
336       return v[0].f == d || v[1].f == d;
337     case MV_3:
338       return v[0].f == d || v[1].f == d || v[2].f == d;
339     case MV_RANGE:
340       return v[1].f <= d && d <= v[2].f;
341     case MV_RANGE_1:
342       return v[0].f == d || (v[1].f <= d && d <= v[2].f);
343     }
344   abort ();
345 }
346
347 /* Returns true if S[] is a missing value in MV, false otherwise.
348    MV must be a set of string missing values. 
349    S[] must contain exactly as many characters as MV's width. */
350 bool
351 mv_is_str_user_missing (const struct missing_values *mv,
352                         const char s[])
353 {
354   const union value *v = mv->values;
355   assert (mv->width > 0);
356   switch (mv->type) 
357     {
358     case MV_NONE:
359       return false;
360     case MV_1:
361       return !memcmp (v[0].s, s, mv->width);
362     case MV_2:
363       return (!memcmp (v[0].s, s, mv->width)
364               || !memcmp (v[1].s, s, mv->width));
365     case MV_3:
366       return (!memcmp (v[0].s, s, mv->width)
367               || !memcmp (v[1].s, s, mv->width)
368               || !memcmp (v[2].s, s, mv->width));
369     case MV_RANGE:
370     case MV_RANGE_1:
371       abort ();
372     }
373   abort ();
374 }
375
376 /* Returns true if MV is a set of numeric missing values and V is
377    the system missing value. */
378 bool
379 mv_is_value_system_missing (const struct missing_values *mv,
380                             const union value *v)
381 {
382   return mv->width == 0 ? v->f == SYSMIS : false;
383 }