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