doc: Document that type-7 records must appear in ascending numerical order.
[pspp-builds.git] / src / data / missing-values.c
1 /* PSPP - a program for statistical analysis.
2    Copyright (C) 2005, 2009 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 <data/missing-values.h>
19 #include <assert.h>
20 #include <stdlib.h>
21 #include <data/variable.h>
22 #include <libpspp/assertion.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.  MV should be destroyed with mv_destroy when
39    it is no longer needed. */
40 void
41 mv_init (struct missing_values *mv, int width)
42 {
43   int i;
44
45   assert (width >= 0 && width <= MAX_STRING);
46   mv->type = MVT_NONE;
47   mv->width = width;
48   for (i = 0; i < 3; i++)
49     value_init (&mv->values[i], width);
50 }
51
52 /* Initializes MV as a set of missing values for a variable of
53    the given WIDTH.  MV will be automatically destroyed along
54    with POOL; it must not be passed to mv_destroy for explicit
55    destruction. */
56 void
57 mv_init_pool (struct pool *pool, struct missing_values *mv, int width)
58 {
59   int i;
60
61   assert (width >= 0 && width <= MAX_STRING);
62   mv->type = MVT_NONE;
63   mv->width = width;
64   for (i = 0; i < 3; i++)
65     value_init_pool (pool, &mv->values[i], width);
66 }
67
68 /* Frees any storage allocated by mv_init for MV. */
69 void
70 mv_destroy (struct missing_values *mv)
71 {
72   if (mv != NULL)
73     {
74       int i;
75
76       for (i = 0; i < 3; i++)
77         value_destroy (&mv->values[i], mv->width);
78     }
79 }
80
81 /* Removes any missing values from MV. */
82 void
83 mv_clear (struct missing_values *mv)
84 {
85   mv->type = MVT_NONE;
86 }
87
88 /* Initializes MV as a copy of SRC. */
89 void
90 mv_copy (struct missing_values *mv, const struct missing_values *src)
91 {
92   int i;
93
94   mv_init (mv, src->width);
95   mv->type = src->type;
96   for (i = 0; i < 3; i++)
97     value_copy (&mv->values[i], &src->values[i], mv->width);
98 }
99
100 /* Returns true if VALUE, of the given WIDTH, may be added to a
101    missing value set also of the given WIDTH.  This is normally
102    the case, but string missing values over MV_MAX_STRING bytes
103    long must consist solely of spaces after the first
104    MV_MAX_STRING bytes.  */
105 bool
106 mv_is_acceptable (const union value *value, int width)
107 {
108   int i;
109
110   for (i = MV_MAX_STRING; i < width; i++)
111     if (value_str (value, width)[i] != ' ')
112       return false;
113   return true;
114 }
115
116 /* Returns true if MV is an empty set of missing values. */
117 bool
118 mv_is_empty (const struct missing_values *mv)
119 {
120   return mv->type == MVT_NONE;
121 }
122
123 /* Returns the width of the missing values that MV may
124    contain. */
125 int
126 mv_get_width (const struct missing_values *mv)
127 {
128   return mv->width;
129 }
130
131 /* Attempts to add individual value V to the set of missing
132    values MV.  Returns true if successful, false if MV has no
133    more room for missing values or if V is not an acceptable
134    missing value. */
135 bool
136 mv_add_value (struct missing_values *mv, const union value *v)
137 {
138   if (!mv_is_acceptable (v, mv->width))
139     return false;
140
141   switch (mv->type)
142     {
143     case MVT_NONE:
144     case MVT_1:
145     case MVT_2:
146     case MVT_RANGE:
147       value_copy (&mv->values[mv->type & 3], v, mv->width);
148       mv->type++;
149       return true;
150
151     case MVT_3:
152     case MVT_RANGE_1:
153       return false;
154     }
155   NOT_REACHED ();
156 }
157
158 /* Attempts to add S to the set of string missing values MV.  S
159    must contain exactly as many characters as MV's width.
160    Returns true if successful, false if MV has no more room for
161    missing values or if S is not an acceptable missing value. */
162 bool
163 mv_add_str (struct missing_values *mv, const uint8_t s[])
164 {
165   union value v;
166   bool ok;
167
168   assert (mv->width > 0);
169   value_init (&v, mv->width);
170   memcpy (value_str_rw (&v, mv->width), s, mv->width);
171   ok = mv_add_value (mv, &v);
172   value_destroy (&v, mv->width);
173
174   return ok;
175 }
176
177 /* Attempts to add D to the set of numeric missing values MV.
178    Returns true if successful, false if MV has no more room for
179    missing values.  */
180 bool
181 mv_add_num (struct missing_values *mv, double d)
182 {
183   union value v;
184   bool ok;
185
186   assert (mv->width == 0);
187   value_init (&v, 0);
188   v.f = d;
189   ok = mv_add_value (mv, &v);
190   value_destroy (&v, 0);
191
192   return ok;
193 }
194
195 /* Attempts to add range [LOW, HIGH] to the set of numeric
196    missing values MV.  Returns true if successful, false if MV
197    has no room for a range, or if LOW > HIGH. */
198 bool
199 mv_add_range (struct missing_values *mv, double low, double high)
200 {
201   assert (mv->width == 0);
202   if (low <= high && (mv->type == MVT_NONE || mv->type == MVT_1))
203     {
204       mv->values[1].f = low;
205       mv->values[2].f = high;
206       mv->type |= 4;
207       return true;
208     }
209   else
210     return false;
211 }
212
213 /* Returns true if MV contains an individual value,
214    false if MV is empty (or contains only a range). */
215 bool
216 mv_has_value (const struct missing_values *mv)
217 {
218   return mv_n_values (mv) > 0;
219 }
220
221 /* Removes one individual value from MV and stores it in V, which
222    must have been initialized as a value with the same width as MV.
223    MV must contain an individual value (as determined by
224    mv_has_value()).
225
226    We remove the first value from MV, not the last, because the
227    common use for this function is in iterating through a set of
228    missing values.  If we remove the last value then we'll output
229    the missing values in order opposite of that in which they
230    were added, so that a GET followed by a SAVE would reverse the
231    order of missing values in the system file, a weird effect. */
232 void
233 mv_pop_value (struct missing_values *mv, union value *v)
234 {
235   union value tmp;
236
237   assert (mv_has_value (mv));
238
239   value_copy (v, &mv->values[0], mv->width);
240   tmp = mv->values[0];
241   mv->values[0] = mv->values[1];
242   mv->values[1] = mv->values[2];
243   mv->values[2] = tmp;
244   mv->type--;
245 }
246
247 /* Returns MV's discrete value with index IDX.  The caller must
248    not modify or free this value, or access it after MV is
249    modified or freed.
250    IDX must be less than the number of discrete values in MV, as
251    reported by mv_n_values. */
252 const union value *
253 mv_get_value (const struct missing_values *mv, int idx)
254 {
255   assert (idx >= 0 && idx < mv_n_values (mv));
256   return &mv->values[idx];
257 }
258
259 /* Replaces MV's discrete value with index IDX by a copy of V,
260    which must have the same width as MV.
261    IDX must be less than the number of discrete values in MV, as
262    reported by mv_n_values. */
263 bool
264 mv_replace_value (struct missing_values *mv, const union value *v, int idx)
265 {
266   assert (idx >= 0) ;
267   assert (idx < mv_n_values(mv));
268
269   if (!mv_is_acceptable (v, mv->width))
270     return false;
271
272   value_copy (&mv->values[idx], v, mv->width);
273   return true;
274 }
275
276 /* Returns the number of individual (not part of a range) missing
277    values in MV. */
278 int
279 mv_n_values (const struct missing_values *mv)
280 {
281   return mv->type & 3;
282 }
283
284
285 /* Returns true if MV contains a numeric range,
286    false if MV is empty (or contains only individual values). */
287 bool
288 mv_has_range (const struct missing_values *mv)
289 {
290   return mv->type == MVT_RANGE || mv->type == MVT_RANGE_1;
291 }
292
293 /* Removes the numeric range from MV and stores it in *LOW and
294    *HIGH.  MV must contain a individual range (as determined by
295    mv_has_range()). */
296 void
297 mv_pop_range (struct missing_values *mv, double *low, double *high)
298 {
299   assert (mv_has_range (mv));
300   *low = mv->values[1].f;
301   *high = mv->values[2].f;
302   mv->type &= 3;
303 }
304
305 /* Returns the numeric range from MV  into *LOW and
306    *HIGH.  MV must contain a individual range (as determined by
307    mv_has_range()). */
308 void
309 mv_get_range (const struct missing_values *mv, double *low, double *high)
310 {
311   assert (mv_has_range (mv));
312   *low = mv->values[1].f;
313   *high = mv->values[2].f;
314 }
315
316 /* Returns true if values[IDX] is in use when the `type' member
317    is set to TYPE (in struct missing_values),
318    false otherwise. */
319 static bool
320 using_element (unsigned type, int idx)
321 {
322   assert (idx >= 0 && idx < 3);
323
324   switch (type)
325     {
326     case MVT_NONE:
327       return false;
328     case MVT_1:
329       return idx < 1;
330     case MVT_2:
331       return idx < 2;
332     case MVT_3:
333       return true;
334     case MVT_RANGE:
335       return idx > 0;
336     case MVT_RANGE_1:
337       return true;
338     }
339   NOT_REACHED ();
340 }
341
342 /* Returns true if MV can be resized to the given WIDTH with
343    mv_resize(), false otherwise.  Resizing is possible only when
344    each value in MV (if any) is resizable from MV's current width
345    to WIDTH, as determined by value_is_resizable. */
346 bool
347 mv_is_resizable (const struct missing_values *mv, int width)
348 {
349   int i;
350
351   for (i = 0; i < 3; i++)
352     if (using_element (mv->type, i)
353         && !value_is_resizable (&mv->values[i], mv->width, width))
354       return false;
355
356   return true;
357 }
358
359 /* Resizes MV to the given WIDTH.  WIDTH must fit the constraints
360    explained for mv_is_resizable. */
361 void
362 mv_resize (struct missing_values *mv, int width)
363 {
364   int i;
365
366   assert (mv_is_resizable (mv, width));
367   for (i = 0; i < 3; i++)
368     if (using_element (mv->type, i))
369       value_resize (&mv->values[i], mv->width, width);
370     else
371       {
372         value_destroy (&mv->values[i], mv->width);
373         value_init (&mv->values[i], width);
374       }
375   mv->width = width;
376 }
377
378 /* Returns true if D is a missing value in MV, false otherwise.
379    MV must be a set of numeric missing values. */
380 static bool
381 is_num_user_missing (const struct missing_values *mv, double d)
382 {
383   const union value *v = mv->values;
384   assert (mv->width == 0);
385   switch (mv->type)
386     {
387     case MVT_NONE:
388       return false;
389     case MVT_1:
390       return v[0].f == d;
391     case MVT_2:
392       return v[0].f == d || v[1].f == d;
393     case MVT_3:
394       return v[0].f == d || v[1].f == d || v[2].f == d;
395     case MVT_RANGE:
396       return v[1].f <= d && d <= v[2].f;
397     case MVT_RANGE_1:
398       return v[0].f == d || (v[1].f <= d && d <= v[2].f);
399     }
400   NOT_REACHED ();
401 }
402
403 /* Returns true if S[] is a missing value in MV, false otherwise.
404    MV must be a set of string missing values.
405    S[] must contain exactly as many characters as MV's width. */
406 static bool
407 is_str_user_missing (const struct missing_values *mv, const uint8_t s[])
408 {
409   const union value *v = mv->values;
410   assert (mv->width > 0);
411   switch (mv->type)
412     {
413     case MVT_NONE:
414       return false;
415     case MVT_1:
416       return !memcmp (value_str (&v[0], mv->width), s, mv->width);
417     case MVT_2:
418       return (!memcmp (value_str (&v[0], mv->width), s, mv->width)
419               || !memcmp (value_str (&v[1], mv->width), s, mv->width));
420     case MVT_3:
421       return (!memcmp (value_str (&v[0], mv->width), s, mv->width)
422               || !memcmp (value_str (&v[1], mv->width), s, mv->width)
423               || !memcmp (value_str (&v[2], mv->width), s, mv->width));
424     case MVT_RANGE:
425     case MVT_RANGE_1:
426       NOT_REACHED ();
427     }
428   NOT_REACHED ();
429 }
430
431 /* Returns true if V is a missing value in the given CLASS in MV,
432    false otherwise. */
433 bool
434 mv_is_value_missing (const struct missing_values *mv, const union value *v,
435                      enum mv_class class)
436 {
437   return (mv->width == 0
438           ? mv_is_num_missing (mv, v->f, class)
439           : mv_is_str_missing (mv, value_str (v, mv->width), class));
440 }
441
442 /* Returns true if D is a missing value in the given CLASS in MV,
443    false otherwise.
444    MV must be a set of numeric missing values. */
445 bool
446 mv_is_num_missing (const struct missing_values *mv, double d,
447                    enum mv_class class)
448 {
449   assert (mv->width == 0);
450   return ((class & MV_SYSTEM && d == SYSMIS)
451           || (class & MV_USER && is_num_user_missing (mv, d)));
452 }
453
454 /* Returns true if S[] is a missing value in the given CLASS in
455    MV, false otherwise.
456    MV must be a set of string missing values.
457    S[] must contain exactly as many characters as MV's width. */
458 bool
459 mv_is_str_missing (const struct missing_values *mv, const uint8_t s[],
460                    enum mv_class class)
461 {
462   assert (mv->width > 0);
463   return class & MV_USER && is_str_user_missing (mv, s);
464 }