Committed patch #5636
[pspp-builds.git] / src / data / missing-values.c
1 /* PSPP - computes sample statistics.
2    Copyright (C) 2005 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 #include "missing-values.h"
21 #include <assert.h>
22 #include <stdlib.h>
23 #include <libpspp/assertion.h>
24 #include "variable.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   if ( var_type_from_width (width) != var_type_from_width (mv->width) )
309     return false;
310
311   if (width > MAX_SHORT_STRING && mv->type != MV_NONE)
312     return false;
313
314   if (width >= mv->width)
315     return true;
316   else
317     {
318       int i;
319
320       for (i = 0; i < 3; i++)
321         if (using_element (mv->type, i)
322             && !can_resize_string (mv->values[i].s, mv->width, width))
323           return false;
324       return true;
325     }
326 }
327
328 /* Resizes MV to the given WIDTH.  WIDTH must fit the constraints
329    explained for mv_is_resizable(). */
330 void
331 mv_resize (struct missing_values *mv, int width) 
332 {
333   assert (mv_is_resizable (mv, width));
334   if (width > mv->width && mv->type != MV_NONE) 
335     {
336       int i;
337       
338       for (i = 0; i < 3; i++)
339         memset (mv->values[i].s + mv->width, ' ', width - mv->width);
340     }
341   mv->width = width;
342 }
343
344 /* Returns true if V is system missing or a missing value in MV,
345    false otherwise. */
346 bool
347 mv_is_value_missing (const struct missing_values *mv, const union value *v)
348 {
349   return (mv->width == 0
350           ? mv_is_num_missing (mv, v->f)
351           : mv_is_str_missing (mv, v->s));
352 }
353
354 /* Returns true if D is system missing or a missing value in MV,
355    false otherwise.
356    MV must be a set of numeric missing values. */
357 bool
358 mv_is_num_missing (const struct missing_values *mv, double d)
359 {
360   assert (mv->width == 0);
361   return d == SYSMIS || mv_is_num_user_missing (mv, d);
362 }
363
364 /* Returns true if S[] is a missing value in MV, false otherwise.
365    MV must be a set of string missing values. 
366    S[] must contain exactly as many characters as MV's width. */
367 bool
368 mv_is_str_missing (const struct missing_values *mv, const char s[])
369 {
370   return mv_is_str_user_missing (mv, s);
371 }
372
373 /* Returns true if V is a missing value in MV, false otherwise. */
374 bool
375 mv_is_value_user_missing (const struct missing_values *mv,
376                           const union value *v)
377 {
378   return (mv->width == 0
379           ? mv_is_num_user_missing (mv, v->f)
380           : mv_is_str_user_missing (mv, v->s));
381 }
382
383 /* Returns true if D is a missing value in MV, false otherwise.
384    MV must be a set of numeric missing values. */
385 bool
386 mv_is_num_user_missing (const struct missing_values *mv, double d)
387 {
388   const union value *v = mv->values;
389   assert (mv->width == 0);
390   switch (mv->type) 
391     {
392     case MV_NONE:
393       return false;
394     case MV_1:
395       return v[0].f == d;
396     case MV_2:
397       return v[0].f == d || v[1].f == d;
398     case MV_3:
399       return v[0].f == d || v[1].f == d || v[2].f == d;
400     case MV_RANGE:
401       return v[1].f <= d && d <= v[2].f;
402     case MV_RANGE_1:
403       return v[0].f == d || (v[1].f <= d && d <= v[2].f);
404     }
405   NOT_REACHED ();
406 }
407
408 /* Returns true if S[] is a missing value in MV, false otherwise.
409    MV must be a set of string missing values. 
410    S[] must contain exactly as many characters as MV's width. */
411 bool
412 mv_is_str_user_missing (const struct missing_values *mv,
413                         const char s[])
414 {
415   const union value *v = mv->values;
416   assert (mv->width > 0);
417   switch (mv->type) 
418     {
419     case MV_NONE:
420       return false;
421     case MV_1:
422       return !memcmp (v[0].s, s, mv->width);
423     case MV_2:
424       return (!memcmp (v[0].s, s, mv->width)
425               || !memcmp (v[1].s, s, mv->width));
426     case MV_3:
427       return (!memcmp (v[0].s, s, mv->width)
428               || !memcmp (v[1].s, s, mv->width)
429               || !memcmp (v[2].s, s, mv->width));
430     case MV_RANGE:
431     case MV_RANGE_1:
432       NOT_REACHED ();
433     }
434   NOT_REACHED ();
435 }
436
437 /* Returns true if MV is a set of numeric missing values and V is
438    the system missing value. */
439 bool
440 mv_is_value_system_missing (const struct missing_values *mv,
441                             const union value *v)
442 {
443   return mv->width == 0 && v->f == SYSMIS;
444 }