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