1 /* PSPP - a program for statistical analysis.
2 Copyright (C) 2005 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/>. */
18 #include "missing-values.h"
21 #include <libpspp/array.h>
22 #include <libpspp/assertion.h>
24 #include <libpspp/str.h>
26 /* Types of user-missing values.
27 Invisible--use access functions defined below instead. */
30 MVT_NONE = 0, /* No user-missing values. */
31 MVT_1 = 1, /* One user-missing value. */
32 MVT_2 = 2, /* Two user-missing values. */
33 MVT_3 = 3, /* Three user-missing values. */
34 MVT_RANGE = 4, /* A range of user-missing values. */
35 MVT_RANGE_1 = 5 /* A range plus an individual value. */
38 /* Initializes MV as a set of missing values for a variable of
39 the given WIDTH. Although only numeric variables and short
40 string variables may have missing values, WIDTH may be any
41 valid variable width. */
43 mv_init (struct missing_values *mv, int width)
45 assert (width >= 0 && width <= MAX_STRING);
50 /* Removes any missing values from MV. */
52 mv_clear (struct missing_values *mv)
57 /* Copies SRC to MV. */
59 mv_copy (struct missing_values *mv, const struct missing_values *src)
66 /* Returns true if MV is an empty set of missing values. */
68 mv_is_empty (const struct missing_values *mv)
70 return mv->type == MVT_NONE;
73 /* Returns the width of the missing values that MV may
76 mv_get_width (const struct missing_values *mv)
81 /* Attempts to add individual value V to the set of missing
82 values MV. Returns true if successful, false if MV has no
83 more room for missing values. (Long string variables never
84 accept missing values.) */
86 mv_add_value (struct missing_values *mv, const union value *v)
88 if (mv->width > MAX_SHORT_STRING)
96 mv->values[mv->type & 3] = *v;
107 /* Attempts to add S to the set of string missing values MV. S
108 must contain exactly as many characters as MV's width.
109 Returns true if successful, false if MV has no more room for
110 missing values. (Long string variables never accept missing
113 mv_add_str (struct missing_values *mv, const char s[])
115 assert (mv->width > 0);
116 return mv_add_value (mv, (union value *) s);
119 /* Attempts to add D to the set of numeric missing values MV.
120 Returns true if successful, false if MV has no more room for
123 mv_add_num (struct missing_values *mv, double d)
125 assert (mv->width == 0);
126 return mv_add_value (mv, (union value *) &d);
129 /* Attempts to add range [LOW, HIGH] to the set of numeric
130 missing values MV. Returns true if successful, false if MV
131 has no room for a range, or if LOW > HIGH. */
133 mv_add_range (struct missing_values *mv, double low, double high)
135 assert (mv->width == 0);
136 if (low <= high && (mv->type == MVT_NONE || mv->type == MVT_1))
138 mv->values[1].f = low;
139 mv->values[2].f = high;
147 /* Returns true if MV contains an individual value,
148 false if MV is empty (or contains only a range). */
150 mv_has_value (const struct missing_values *mv)
152 return mv_n_values (mv) > 0;
155 /* Removes one individual value from MV and stores it in *V.
156 MV must contain an individual value (as determined by
159 We remove the first value from MV, not the last, because the
160 common use for this function is in iterating through a set of
161 missing values. If we remove the last value then we'll output
162 the missing values in order opposite of that in which they
163 were added, so that a GET followed by a SAVE would reverse the
164 order of missing values in the system file, a weird effect. */
166 mv_pop_value (struct missing_values *mv, union value *v)
168 assert (mv_has_value (mv));
171 remove_element (mv->values, mv->type & 3, sizeof *mv->values, 0);
175 /* Stores MV's value with index IDX in *V.
176 IDX must be less than the number of discrete values in MV, as
177 reported by mv_n_values(MV). */
179 mv_get_value (const struct missing_values *mv, union value *v, int idx)
181 assert (idx >= 0 && idx < mv_n_values (mv));
182 *v = mv->values[idx];
186 mv_replace_value (struct missing_values *mv, const union value *v, int idx)
189 assert (idx < mv_n_values(mv));
191 mv->values[idx] = *v;
195 /* Returns the number of individual (not part of a range) missing
198 mv_n_values (const struct missing_values *mv)
204 /* Returns true if MV contains a numeric range,
205 false if MV is empty (or contains only individual values). */
207 mv_has_range (const struct missing_values *mv)
209 return mv->type == MVT_RANGE || mv->type == MVT_RANGE_1;
212 /* Removes the numeric range from MV and stores it in *LOW and
213 *HIGH. MV must contain a individual range (as determined by
216 mv_pop_range (struct missing_values *mv, double *low, double *high)
218 assert (mv_has_range (mv));
219 *low = mv->values[1].f;
220 *high = mv->values[2].f;
224 /* Returns the numeric range from MV into *LOW and
225 *HIGH. MV must contain a individual range (as determined by
228 mv_get_range (const struct missing_values *mv, double *low, double *high)
230 assert (mv_has_range (mv));
231 *low = mv->values[1].f;
232 *high = mv->values[2].f;
236 /* Returns true if values[IDX] is in use when the `type' member
237 is set to TYPE (in struct missing_values),
240 using_element (unsigned type, int idx)
242 assert (idx >= 0 && idx < 3);
262 /* Returns true if MV can be resized to the given WIDTH with
263 mv_resize(), false otherwise. Resizing is possible only when
264 each value in MV (if any) is resizable from MV's current width
265 to WIDTH, as determined by value_is_resizable. In addition,
266 resizing must not produce a non-empty set of long string
269 mv_is_resizable (const struct missing_values *mv, int width)
273 if (width > MAX_SHORT_STRING && mv->type != MVT_NONE)
276 for (i = 0; i < 3; i++)
277 if (using_element (mv->type, i)
278 && !value_is_resizable (&mv->values[i], mv->width, width))
284 /* Resizes MV to the given WIDTH. WIDTH must fit the constraints
285 explained for mv_is_resizable. */
287 mv_resize (struct missing_values *mv, int width)
291 assert (mv_is_resizable (mv, width));
292 for (i = 0; i < 3; i++)
293 if (using_element (mv->type, i))
294 value_resize (&mv->values[i], mv->width, width);
298 /* Returns true if D is a missing value in MV, false otherwise.
299 MV must be a set of numeric missing values. */
301 is_num_user_missing (const struct missing_values *mv, double d)
303 const union value *v = mv->values;
304 assert (mv->width == 0);
312 return v[0].f == d || v[1].f == d;
314 return v[0].f == d || v[1].f == d || v[2].f == d;
316 return v[1].f <= d && d <= v[2].f;
318 return v[0].f == d || (v[1].f <= d && d <= v[2].f);
323 /* Returns true if S[] is a missing value in MV, false otherwise.
324 MV must be a set of string missing values.
325 S[] must contain exactly as many characters as MV's width. */
327 is_str_user_missing (const struct missing_values *mv,
330 const union value *v = mv->values;
331 assert (mv->width > 0);
337 return !memcmp (v[0].s, s, mv->width);
339 return (!memcmp (v[0].s, s, mv->width)
340 || !memcmp (v[1].s, s, mv->width));
342 return (!memcmp (v[0].s, s, mv->width)
343 || !memcmp (v[1].s, s, mv->width)
344 || !memcmp (v[2].s, s, mv->width));
352 /* Returns true if V is a missing value in the given CLASS in MV,
355 mv_is_value_missing (const struct missing_values *mv, const union value *v,
358 return (mv->width == 0
359 ? mv_is_num_missing (mv, v->f, class)
360 : mv_is_str_missing (mv, v->s, class));
363 /* Returns true if D is a missing value in the given CLASS in MV,
365 MV must be a set of numeric missing values. */
367 mv_is_num_missing (const struct missing_values *mv, double d,
370 assert (mv->width == 0);
371 return ((class & MV_SYSTEM && d == SYSMIS)
372 || (class & MV_USER && is_num_user_missing (mv, d)));
375 /* Returns true if S[] is a missing value in the given CLASS in
377 MV must be a set of string missing values.
378 S[] must contain exactly as many characters as MV's width. */
380 mv_is_str_missing (const struct missing_values *mv, const char s[],
383 assert (mv->width > 0);
384 return class & MV_USER && is_str_user_missing (mv, s);