1 /* PSPP - a program for statistical analysis.
2 Copyright (C) 2005, 2009, 2011-2015 Free Software Foundation, Inc.
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.
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.
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/>. */
19 #include "data/missing-values.h"
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"
30 /* Types of user-missing values.
31 Invisible--use access functions defined below instead. */
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. */
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. */
46 mv_init (struct missing_values *mv, int width)
50 assert (width >= 0 && width <= MAX_STRING);
53 for (i = 0; i < 3; i++)
54 value_init (&mv->values[i], width);
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
62 mv_init_pool (struct pool *pool, struct missing_values *mv, int width)
66 assert (width >= 0 && width <= MAX_STRING);
69 for (i = 0; i < 3; i++)
70 value_init_pool (pool, &mv->values[i], width);
73 /* Frees any storage allocated by mv_init for MV. */
75 mv_destroy (struct missing_values *mv)
81 for (i = 0; i < 3; i++)
82 value_destroy (&mv->values[i], mv->width);
86 /* Removes any missing values from MV. */
88 mv_clear (struct missing_values *mv)
93 /* Initializes MV as a copy of SRC. */
95 mv_copy (struct missing_values *mv, const struct missing_values *src)
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);
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. */
111 mv_is_acceptable (const union value *value, int width)
115 for (i = MV_MAX_STRING; i < width; i++)
116 if (value_str (value, width)[i] != ' ')
121 /* Returns true if MV is an empty set of missing values. */
123 mv_is_empty (const struct missing_values *mv)
125 return mv->type == MVT_NONE;
128 /* Returns the width of the missing values that MV may
131 mv_get_width (const struct missing_values *mv)
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
141 mv_add_value (struct missing_values *mv, const union value *v)
143 if (!mv_is_acceptable (v, mv->width))
152 value_copy (&mv->values[mv->type & 3], v, mv->width);
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. */
167 mv_add_str (struct missing_values *mv, const uint8_t s[], size_t len)
172 assert (mv->width > 0);
173 while (len > mv->width)
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);
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
190 mv_add_num (struct missing_values *mv, double d)
195 assert (mv->width == 0);
198 ok = mv_add_value (mv, &v);
199 value_destroy (&v, 0);
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. */
208 mv_add_range (struct missing_values *mv, double low, double high)
210 assert (mv->width == 0);
211 if (low <= high && (mv->type == MVT_NONE || mv->type == MVT_1))
213 mv->values[1].f = low;
214 mv->values[2].f = high;
222 /* Returns true if MV contains an individual value,
223 false if MV is empty (or contains only a range). */
225 mv_has_value (const struct missing_values *mv)
227 return mv_n_values (mv) > 0;
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
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. */
242 mv_pop_value (struct missing_values *mv, union value *v)
246 assert (mv_has_value (mv));
248 value_copy (v, &mv->values[0], mv->width);
250 mv->values[0] = mv->values[1];
251 mv->values[1] = mv->values[2];
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
259 IDX must be less than the number of discrete values in MV, as
260 reported by mv_n_values. */
262 mv_get_value (const struct missing_values *mv, int idx)
264 assert (idx >= 0 && idx < mv_n_values (mv));
265 return &mv->values[idx];
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. */
273 mv_replace_value (struct missing_values *mv, const union value *v, int idx)
276 assert (idx < mv_n_values(mv));
278 if (!mv_is_acceptable (v, mv->width))
281 value_copy (&mv->values[idx], v, mv->width);
285 /* Returns the number of individual (not part of a range) missing
288 mv_n_values (const struct missing_values *mv)
294 /* Returns true if MV contains a numeric range,
295 false if MV is empty (or contains only individual values). */
297 mv_has_range (const struct missing_values *mv)
299 return mv->type == MVT_RANGE || mv->type == MVT_RANGE_1;
302 /* Removes the numeric range from MV and stores it in *LOW and
303 *HIGH. MV must contain a individual range (as determined by
306 mv_pop_range (struct missing_values *mv, double *low, double *high)
308 assert (mv_has_range (mv));
309 *low = mv->values[1].f;
310 *high = mv->values[2].f;
314 /* Returns the numeric range from MV into *LOW and
315 *HIGH. MV must contain a individual range (as determined by
318 mv_get_range (const struct missing_values *mv, double *low, double *high)
320 assert (mv_has_range (mv));
321 *low = mv->values[1].f;
322 *high = mv->values[2].f;
325 /* Returns true if values[IDX] is in use when the `type' member
326 is set to TYPE (in struct missing_values),
329 using_element (unsigned type, int idx)
331 assert (idx >= 0 && idx < 3);
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. */
356 mv_is_resizable (const struct missing_values *mv, int width)
360 for (i = 0; i < 3; i++)
361 if (using_element (mv->type, i)
362 && !value_is_resizable (&mv->values[i], mv->width, width))
368 /* Resizes MV to the given WIDTH. WIDTH must fit the constraints
369 explained for mv_is_resizable. */
371 mv_resize (struct missing_values *mv, int width)
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);
381 value_destroy (&mv->values[i], mv->width);
382 value_init (&mv->values[i], width);
387 /* Returns true if D is a missing value in MV, false otherwise.
388 MV must be a set of numeric missing values. */
390 is_num_user_missing (const struct missing_values *mv, double d)
392 const union value *v = mv->values;
393 assert (mv->width == 0);
401 return v[0].f == d || v[1].f == d;
403 return v[0].f == d || v[1].f == d || v[2].f == d;
405 return v[1].f <= d && d <= v[2].f;
407 return v[0].f == d || (v[1].f <= d && d <= v[2].f);
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. */
416 is_str_user_missing (const struct missing_values *mv, const uint8_t s[])
418 const union value *v = mv->values;
419 assert (mv->width > 0);
425 return !memcmp (value_str (&v[0], mv->width), s, mv->width);
427 return (!memcmp (value_str (&v[0], mv->width), s, mv->width)
428 || !memcmp (value_str (&v[1], mv->width), s, mv->width));
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));
440 /* Returns true if V is a missing value in the given CLASS in MV,
443 mv_is_value_missing (const struct missing_values *mv, const union value *v,
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));
451 /* Returns true if D is a missing value in the given CLASS in MV,
453 MV must be a set of numeric missing values. */
455 mv_is_num_missing (const struct missing_values *mv, double d,
458 assert (mv->width == 0);
459 return ((class & MV_SYSTEM && d == SYSMIS)
460 || (class & MV_USER && is_num_user_missing (mv, d)));
463 /* Returns true if S[] is a missing value in the given CLASS in
465 MV must be a set of string missing values.
466 S[] must contain exactly as many characters as MV's width. */
468 mv_is_str_missing (const struct missing_values *mv, const uint8_t s[],
471 assert (mv->width > 0);
472 return class & MV_USER && is_str_user_missing (mv, s);
476 mv_save (const struct missing_values *mv, struct pxd *pxd)
478 struct pxd_builder b;
481 pxd_builder_init (&b, pxd);
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);
489 return pxd_builder_commit (&b);
493 mv_load (struct missing_values *mv, struct pxd_object *object,
494 const struct pxd *pxd)
499 pxd_parser_init (&p, object, pxd);
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);
507 value_init (&mv->values[i], mv->width);
509 pxd_parser_destroy (&p);