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