b5f52f5e6985301b27066df9f7ab41b751ae69cf
[pspp-builds.git] / src / data / 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 <libpspp/assertion.h>
25 #include <libpspp/str.h>
26
27
28 /* Initializes MV as a set of missing values for a variable of
29    the given WIDTH.  Although only numeric variables and short
30    string variables may have missing values, WIDTH may be any
31    valid variable width. */
32 void
33 mv_init (struct missing_values *mv, int width) 
34 {
35   assert (width >= 0 && width <= MAX_STRING);
36   mv->type = MV_NONE;
37   mv->width = width;
38 }
39
40 void 
41 mv_set_type(struct missing_values *mv, enum mv_type type)
42 {
43   mv->type = type;
44 }
45
46
47 /* Copies SRC to MV. */
48 void
49 mv_copy (struct missing_values *mv, const struct missing_values *src) 
50 {
51   assert(src);
52
53   *mv = *src;
54 }
55
56 /* Returns true if MV is an empty set of missing values. */
57 bool
58 mv_is_empty (const struct missing_values *mv) 
59 {
60   return mv->type == MV_NONE;
61 }
62
63 /* Returns the width of the missing values that MV may
64    contain. */
65 int
66 mv_get_width (const struct missing_values *mv)
67 {
68   return mv->width;
69 }
70
71 /* Attempts to add individual value V to the set of missing
72    values MV.  Returns true if successful, false if MV has no
73    more room for missing values.  (Long string variables never
74    accept missing values.) */
75 bool
76 mv_add_value (struct missing_values *mv, const union value *v)
77 {
78   if (mv->width > MAX_SHORT_STRING)
79     return false;
80   switch (mv->type) 
81     {
82     case MV_NONE:
83     case MV_1:
84     case MV_2:
85     case MV_RANGE:
86       mv->values[mv->type & 3] = *v;
87       mv->type++;
88       return true;
89
90     case MV_3:
91     case MV_RANGE_1:
92       return false;
93     }
94   NOT_REACHED ();
95 }
96
97 /* Attempts to add S to the set of string missing values MV.  S
98    must contain exactly as many characters as MV's width.
99    Returns true if successful, false if MV has no more room for
100    missing values.  (Long string variables never accept missing
101    values.) */
102 bool
103 mv_add_str (struct missing_values *mv, const char s[]) 
104 {
105   assert (mv->width > 0);
106   return mv_add_value (mv, (union value *) s);
107 }
108
109 /* Attempts to add D to the set of numeric missing values MV.
110    Returns true if successful, false if MV has no more room for
111    missing values.  */
112 bool
113 mv_add_num (struct missing_values *mv, double d) 
114 {
115   assert (mv->width == 0);
116   return mv_add_value (mv, (union value *) &d);
117 }
118
119 /* Attempts to add range [LOW, HIGH] to the set of numeric
120    missing values MV.  Returns true if successful, false if MV
121    has no room for a range, or if LOW > HIGH. */
122 bool
123 mv_add_num_range (struct missing_values *mv, double low, double high) 
124 {
125   assert (mv->width == 0);
126   if (low > high)
127     return false;
128   switch (mv->type) 
129     {
130     case MV_NONE:
131     case MV_1:
132       mv->values[1].f = low;
133       mv->values[2].f = high;
134       mv->type |= 4;
135       return true;
136
137     case MV_2:
138     case MV_3:
139     case MV_RANGE:
140     case MV_RANGE_1:
141       return false;
142     }
143   NOT_REACHED ();
144 }
145
146 /* Returns true if MV contains an individual value,
147    false if MV is empty (or contains only a range). */
148 bool
149 mv_has_value (const struct missing_values *mv)
150 {
151   switch (mv->type) 
152     {
153     case MV_1:
154     case MV_2:
155     case MV_3:
156     case MV_RANGE_1:
157       return true;
158       
159     case MV_NONE:
160     case MV_RANGE:
161       return false;
162     }
163   NOT_REACHED ();
164 }
165
166 /* Removes one individual value from MV and stores it in *V.
167    MV must contain an individual value (as determined by
168    mv_has_value()). */
169 void
170 mv_pop_value (struct missing_values *mv, union value *v) 
171 {
172   assert (mv_has_value (mv));
173   mv->type--;
174   *v = mv->values[mv->type & 3];
175 }
176
177 /* Stores  a value  in *V.
178    MV must contain an individual value (as determined by
179    mv_has_value()). 
180    IDX is the zero based index of the value to get
181 */
182 void
183 mv_peek_value (const struct missing_values *mv, union value *v, int idx) 
184 {
185   assert (idx >= 0 ) ;
186   assert (idx < 3);
187
188   assert (mv_has_value (mv));
189   *v = mv->values[idx];
190 }
191
192 void 
193 mv_replace_value (struct missing_values *mv, const union value *v, int idx)
194 {
195   assert (idx >= 0) ;
196   assert (idx < mv_n_values(mv));
197
198   mv->values[idx] = *v;
199 }
200
201
202
203 int  
204 mv_n_values (const struct missing_values *mv)
205 {
206   assert(mv_has_value(mv));
207   return mv->type & 3;
208 }
209
210
211 /* Returns true if MV contains a numeric range,
212    false if MV is empty (or contains only individual values). */
213 bool
214 mv_has_range (const struct missing_values *mv) 
215 {
216   switch (mv->type) 
217     {
218     case MV_RANGE:
219     case MV_RANGE_1:
220       return true;
221       
222     case MV_NONE:
223     case MV_1:
224     case MV_2:
225     case MV_3:
226       return false;
227     }
228   NOT_REACHED ();
229 }
230
231 /* Removes the numeric range from MV and stores it in *LOW and
232    *HIGH.  MV must contain a individual range (as determined by
233    mv_has_range()). */
234 void
235 mv_pop_range (struct missing_values *mv, double *low, double *high) 
236 {
237   assert (mv_has_range (mv));
238   *low = mv->values[1].f;
239   *high = mv->values[2].f;
240   mv->type &= 3;
241 }
242
243
244 /* Returns the numeric range from MV  into *LOW and
245    *HIGH.  MV must contain a individual range (as determined by
246    mv_has_range()). */
247 void
248 mv_peek_range (const struct missing_values *mv, double *low, double *high) 
249 {
250   assert (mv_has_range (mv));
251   *low = mv->values[1].f;
252   *high = mv->values[2].f;
253 }
254
255
256 /* Returns true if values[IDX] is in use when the `type' member
257    is set to TYPE (in struct missing_values),
258    false otherwise. */
259 static bool
260 using_element (unsigned type, int idx) 
261 {
262   assert (idx >= 0 && idx < 3);
263   
264   switch (type) 
265     {
266     case MV_NONE:
267       return false;
268     case MV_1:
269       return idx < 1;
270     case MV_2:
271       return idx < 2;
272     case MV_3:
273       return true;
274     case MV_RANGE:
275       return idx > 0;
276     case MV_RANGE_1:
277       return true;
278     }
279   NOT_REACHED ();
280 }
281
282 /* Returns true if S contains only spaces between indexes
283    NEW_WIDTH (inclusive) and OLD_WIDTH (exclusive),
284    false otherwise. */
285 static bool
286 can_resize_string (const char *s, int old_width, int new_width) 
287 {
288   int i;
289
290   assert (new_width < old_width);
291   for (i = new_width; i < old_width; i++)
292     if (s[i] != ' ')
293       return false;
294   return true;
295 }
296
297 /* Returns true if MV can be resized to the given WIDTH with
298    mv_resize(), false otherwise.  Resizing to the same width is
299    always possible.  Resizing to a long string WIDTH is only
300    possible if MV is an empty set of missing values; otherwise,
301    resizing to a larger WIDTH is always possible.  Resizing to a
302    shorter width is possible only when each missing value
303    contains only spaces in the characters that will be
304    trimmed. */
305 bool
306 mv_is_resizable (const struct missing_values *mv, int width) 
307 {
308   assert ((width == 0) == (mv->width == 0));
309   if (width > MAX_SHORT_STRING && mv->type != MV_NONE)
310     return false;
311   else if (width >= mv->width)
312     return true;
313   else 
314     {
315       int i;
316       
317       for (i = 0; i < 3; i++)
318         if (using_element (mv->type, i)
319             && !can_resize_string (mv->values[i].s, mv->width, width))
320           return false;
321       return true;
322     }
323 }
324
325 /* Resizes MV to the given WIDTH.  WIDTH must fit the constraints
326    explained for mv_is_resizable(). */
327 void
328 mv_resize (struct missing_values *mv, int width) 
329 {
330   assert (mv_is_resizable (mv, width));
331   if (width > mv->width && mv->type != MV_NONE) 
332     {
333       int i;
334       
335       for (i = 0; i < 3; i++)
336         memset (mv->values[i].s + mv->width, ' ', width - mv->width);
337     }
338   mv->width = width;
339 }
340
341 /* Returns true if V is system missing or a missing value in MV,
342    false otherwise. */
343 bool
344 mv_is_value_missing (const struct missing_values *mv, const union value *v)
345 {
346   return (mv->width == 0
347           ? mv_is_num_missing (mv, v->f)
348           : mv_is_str_missing (mv, v->s));
349 }
350
351 /* Returns true if D is system missing or a missing value in MV,
352    false otherwise.
353    MV must be a set of numeric missing values. */
354 bool
355 mv_is_num_missing (const struct missing_values *mv, double d)
356 {
357   assert (mv->width == 0);
358   return d == SYSMIS || mv_is_num_user_missing (mv, d);
359 }
360
361 /* Returns true if S[] is a missing value in MV, false otherwise.
362    MV must be a set of string missing values. 
363    S[] must contain exactly as many characters as MV's width. */
364 bool
365 mv_is_str_missing (const struct missing_values *mv, const char s[])
366 {
367   return mv_is_str_user_missing (mv, s);
368 }
369
370 /* Returns true if V is a missing value in MV, false otherwise. */
371 bool
372 mv_is_value_user_missing (const struct missing_values *mv,
373                           const union value *v)
374 {
375   return (mv->width == 0
376           ? mv_is_num_user_missing (mv, v->f)
377           : mv_is_str_user_missing (mv, v->s));
378 }
379
380 /* Returns true if D is a missing value in MV, false otherwise.
381    MV must be a set of numeric missing values. */
382 bool
383 mv_is_num_user_missing (const struct missing_values *mv, double d)
384 {
385   const union value *v = mv->values;
386   assert (mv->width == 0);
387   switch (mv->type) 
388     {
389     case MV_NONE:
390       return false;
391     case MV_1:
392       return v[0].f == d;
393     case MV_2:
394       return v[0].f == d || v[1].f == d;
395     case MV_3:
396       return v[0].f == d || v[1].f == d || v[2].f == d;
397     case MV_RANGE:
398       return v[1].f <= d && d <= v[2].f;
399     case MV_RANGE_1:
400       return v[0].f == d || (v[1].f <= d && d <= v[2].f);
401     }
402   NOT_REACHED ();
403 }
404
405 /* Returns true if S[] is a missing value in MV, false otherwise.
406    MV must be a set of string missing values. 
407    S[] must contain exactly as many characters as MV's width. */
408 bool
409 mv_is_str_user_missing (const struct missing_values *mv,
410                         const char s[])
411 {
412   const union value *v = mv->values;
413   assert (mv->width > 0);
414   switch (mv->type) 
415     {
416     case MV_NONE:
417       return false;
418     case MV_1:
419       return !memcmp (v[0].s, s, mv->width);
420     case MV_2:
421       return (!memcmp (v[0].s, s, mv->width)
422               || !memcmp (v[1].s, s, mv->width));
423     case MV_3:
424       return (!memcmp (v[0].s, s, mv->width)
425               || !memcmp (v[1].s, s, mv->width)
426               || !memcmp (v[2].s, s, mv->width));
427     case MV_RANGE:
428     case MV_RANGE_1:
429       NOT_REACHED ();
430     }
431   NOT_REACHED ();
432 }
433
434 /* Returns true if MV is a set of numeric missing values and V is
435    the system missing value. */
436 bool
437 mv_is_value_system_missing (const struct missing_values *mv,
438                             const union value *v)
439 {
440   return mv->width == 0 && v->f == SYSMIS;
441 }