Clean up treatment of missing values by moving all the code into
[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 unsigned 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. */
111 bool
112 mv_add_num_range (struct missing_values *mv, double low, double high) 
113 {
114   assert (mv->width == 0);
115   switch (mv->type) 
116     {
117     case MV_NONE:
118     case MV_1:
119       mv->values[1].f = low;
120       mv->values[2].f = high;
121       mv->type |= 4;
122       return true;
123
124     case MV_2:
125     case MV_3:
126     case MV_RANGE:
127     case MV_RANGE_1:
128       return false;
129     }
130   abort ();
131 }
132
133 /* Returns true if MV contains an individual value,
134    false if MV is empty (or contains only a range). */
135 bool
136 mv_has_value (struct missing_values *mv)
137 {
138   switch (mv->type) 
139     {
140     case MV_1:
141     case MV_2:
142     case MV_3:
143     case MV_RANGE_1:
144       return true;
145       
146     case MV_NONE:
147     case MV_RANGE:
148       return false;
149     }
150   abort ();
151 }
152
153 /* Removes one individual value from MV and stores it in *V.
154    MV must contain an individual value (as determined by
155    mv_has_value()). */
156 void
157 mv_pop_value (struct missing_values *mv, union value *v) 
158 {
159   assert (mv_has_value (mv));
160   mv->type--;
161   *v = mv->values[mv->type & 3];
162 }
163
164 /* Returns true if MV contains a numeric range,
165    false if MV is empty (or contains only individual values). */
166 bool
167 mv_has_range (struct missing_values *mv) 
168 {
169   switch (mv->type) 
170     {
171     case MV_RANGE:
172     case MV_RANGE_1:
173       return true;
174       
175     case MV_NONE:
176     case MV_1:
177     case MV_2:
178     case MV_3:
179       return false;
180     }
181   abort ();
182 }
183
184 /* Removes the numeric range from MV and stores it in *LOW and
185    *HIGH.  MV must contain a individual range (as determined by
186    mv_has_range()). */
187 void
188 mv_pop_range (struct missing_values *mv, double *low, double *high) 
189 {
190   assert (mv_has_range (mv));
191   *low = mv->values[1].f;
192   *high = mv->values[2].f;
193   mv->type &= 3;
194 }
195
196 /* Returns true if values[IDX] is in use when the `type' member
197    is set to TYPE (in struct missing_values),
198    false otherwise. */
199 static bool
200 using_element (unsigned type, int idx) 
201 {
202   assert (idx >= 0 && idx < 3);
203   
204   switch (type) 
205     {
206     case MV_NONE:
207       return false;
208     case MV_1:
209       return idx < 1;
210     case MV_2:
211       return idx < 2;
212     case MV_3:
213       return true;
214     case MV_RANGE:
215       return idx > 0;
216     case MV_RANGE_1:
217       return true;
218     }
219   abort ();
220 }
221
222 /* Returns true if S contains only spaces between indexes
223    NEW_WIDTH (inclusive) and OLD_WIDTH (exclusive),
224    false otherwise. */
225 static bool
226 can_resize_string (const unsigned char *s, int old_width, int new_width) 
227 {
228   int i;
229
230   assert (new_width < old_width);
231   for (i = new_width; i < old_width; i++)
232     if (s[i] != ' ')
233       return false;
234   return true;
235 }
236
237 /* Returns true if MV can be resized to the given WIDTH with
238    mv_resize(), false otherwise.  Resizing to the same width is
239    always possible.  Resizing to a long string WIDTH is only
240    possible if MV is an empty set of missing values; otherwise,
241    resizing to a larger WIDTH is always possible.  Resizing to a
242    shorter width is possible only when each missing value
243    contains only spaces in the characters that will be
244    trimmed. */
245 bool
246 mv_is_resizable (struct missing_values *mv, int width) 
247 {
248   assert ((width == 0) == (mv->width == 0));
249   if (width > MAX_SHORT_STRING && mv->type != MV_NONE)
250     return false;
251   else if (width >= mv->width)
252     return true;
253   else 
254     {
255       int i;
256       
257       for (i = 0; i < 3; i++)
258         if (using_element (mv->type, i)
259             && !can_resize_string (mv->values[i].s, mv->width, width))
260           return false;
261       return true;
262     }
263 }
264
265 /* Resizes MV to the given WIDTH.  WIDTH must fit the constraints
266    explained for mv_is_resizable(). */
267 void
268 mv_resize (struct missing_values *mv, int width) 
269 {
270   assert (mv_is_resizable (mv, width));
271   if (width > mv->width) 
272     {
273       int i;
274       
275       for (i = 0; i < 3; i++)
276         memset (mv->values[i].s + mv->width, ' ', width - mv->width);
277     }
278   mv->width = width;
279 }
280
281 /* Returns true if V is system missing or a missing value in MV,
282    false otherwise. */
283 bool
284 mv_is_value_missing (const struct missing_values *mv, const union value *v)
285 {
286   return (mv->width == 0
287           ? mv_is_num_missing (mv, v->f)
288           : mv_is_str_missing (mv, v->s));
289 }
290
291 /* Returns true if D is system missing or a missing value in MV,
292    false otherwise.
293    MV must be a set of numeric missing values. */
294 bool
295 mv_is_num_missing (const struct missing_values *mv, double d)
296 {
297   assert (mv->width == 0);
298   return d == SYSMIS || mv_is_num_user_missing (mv, d);
299 }
300
301 /* Returns true if S[] is a missing value in MV, false otherwise.
302    MV must be a set of string missing values. 
303    S[] must contain exactly as many characters as MV's width. */
304 bool
305 mv_is_str_missing (const struct missing_values *mv,
306                    const unsigned char s[])
307 {
308   return mv_is_str_user_missing (mv, s);
309 }
310
311 /* Returns true if V is a missing value in MV, false otherwise. */
312 bool
313 mv_is_value_user_missing (const struct missing_values *mv,
314                           const union value *v)
315 {
316   return (mv->width == 0
317           ? mv_is_num_user_missing (mv, v->f)
318           : mv_is_str_user_missing (mv, v->s));
319 }
320
321 /* Returns true if D is a missing value in MV, false otherwise.
322    MV must be a set of numeric missing values. */
323 bool
324 mv_is_num_user_missing (const struct missing_values *mv, double d)
325 {
326   const union value *v = mv->values;
327   assert (mv->width == 0);
328   switch (mv->type) 
329     {
330     case MV_NONE:
331       return false;
332     case MV_1:
333       return v[0].f == d;
334     case MV_2:
335       return v[0].f == d || v[1].f == d;
336     case MV_3:
337       return v[0].f == d || v[1].f == d || v[2].f == d;
338     case MV_RANGE:
339       return v[1].f <= d && d <= v[2].f;
340     case MV_RANGE_1:
341       return v[0].f == d || (v[1].f <= d && d <= v[2].f);
342     }
343   abort ();
344 }
345
346 /* Returns true if S[] is a missing value in MV, false otherwise.
347    MV must be a set of string missing values. 
348    S[] must contain exactly as many characters as MV's width. */
349 bool
350 mv_is_str_user_missing (const struct missing_values *mv,
351                         const unsigned char s[])
352 {
353   const union value *v = mv->values;
354   assert (mv->width > 0);
355   switch (mv->type) 
356     {
357     case MV_NONE:
358       return false;
359     case MV_1:
360       return !memcmp (v[0].s, s, mv->width);
361     case MV_2:
362       return (!memcmp (v[0].s, s, mv->width)
363               || !memcmp (v[1].s, s, mv->width));
364     case MV_3:
365       return (!memcmp (v[0].s, s, mv->width)
366               || !memcmp (v[1].s, s, mv->width)
367               || !memcmp (v[2].s, s, mv->width));
368     case MV_RANGE:
369     case MV_RANGE_1:
370       abort ();
371     }
372   abort ();
373 }
374
375 /* Returns true if MV is a set of numeric missing values and V is
376    the system missing value. */
377 bool
378 mv_is_value_system_missing (const struct missing_values *mv,
379                             const union value *v)
380 {
381   return mv->width == 0 ? v->f == SYSMIS : false;
382 }