07ebb77a17312a388ced25b3f04e2ae4f4f0fea5
[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/array.h>
22 #include <libpspp/assertion.h>
23 #include "variable.h"
24 #include <libpspp/str.h>
25
26 /* Types of user-missing values.
27    Invisible--use access functions defined below instead. */
28 enum mv_type
29   {
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. */
36   };
37
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. */
42 void
43 mv_init (struct missing_values *mv, int width)
44 {
45   assert (width >= 0 && width <= MAX_STRING);
46   mv->type = MVT_NONE;
47   mv->width = width;
48 }
49
50 /* Removes any missing values from MV. */
51 void
52 mv_clear (struct missing_values *mv)
53 {
54   mv->type = MVT_NONE;
55 }
56
57 /* Copies SRC to MV. */
58 void
59 mv_copy (struct missing_values *mv, const struct missing_values *src)
60 {
61   assert(src);
62
63   *mv = *src;
64 }
65
66 /* Returns true if MV is an empty set of missing values. */
67 bool
68 mv_is_empty (const struct missing_values *mv)
69 {
70   return mv->type == MVT_NONE;
71 }
72
73 /* Returns the width of the missing values that MV may
74    contain. */
75 int
76 mv_get_width (const struct missing_values *mv)
77 {
78   return mv->width;
79 }
80
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.) */
85 bool
86 mv_add_value (struct missing_values *mv, const union value *v)
87 {
88   if (mv->width > MAX_SHORT_STRING)
89     return false;
90   switch (mv->type)
91     {
92     case MVT_NONE:
93     case MVT_1:
94     case MVT_2:
95     case MVT_RANGE:
96       mv->values[mv->type & 3] = *v;
97       mv->type++;
98       return true;
99
100     case MVT_3:
101     case MVT_RANGE_1:
102       return false;
103     }
104   NOT_REACHED ();
105 }
106
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
111    values.) */
112 bool
113 mv_add_str (struct missing_values *mv, const char s[])
114 {
115   assert (mv->width > 0);
116   return mv_add_value (mv, (union value *) s);
117 }
118
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
121    missing values.  */
122 bool
123 mv_add_num (struct missing_values *mv, double d)
124 {
125   assert (mv->width == 0);
126   return mv_add_value (mv, (union value *) &d);
127 }
128
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. */
132 bool
133 mv_add_range (struct missing_values *mv, double low, double high)
134 {
135   assert (mv->width == 0);
136   if (low <= high && (mv->type == MVT_NONE || mv->type == MVT_1))
137     {
138       mv->values[1].f = low;
139       mv->values[2].f = high;
140       mv->type |= 4;
141       return true;
142     }
143   else
144     return false;
145 }
146
147 /* Returns true if MV contains an individual value,
148    false if MV is empty (or contains only a range). */
149 bool
150 mv_has_value (const struct missing_values *mv)
151 {
152   return mv_n_values (mv) > 0;
153 }
154
155 /* Removes one individual value from MV and stores it in *V.
156    MV must contain an individual value (as determined by
157    mv_has_value()).
158
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. */
165 void
166 mv_pop_value (struct missing_values *mv, union value *v)
167 {
168   assert (mv_has_value (mv));
169
170   *v = mv->values[0];
171   remove_element (mv->values, mv->type & 3, sizeof *mv->values, 0);
172   mv->type--;
173 }
174
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). */
178 void
179 mv_get_value (const struct missing_values *mv, union value *v, int idx)
180 {
181   assert (idx >= 0 && idx < mv_n_values (mv));
182   *v = mv->values[idx];
183 }
184
185 void
186 mv_replace_value (struct missing_values *mv, const union value *v, int idx)
187 {
188   assert (idx >= 0) ;
189   assert (idx < mv_n_values(mv));
190
191   mv->values[idx] = *v;
192 }
193
194
195 /* Returns the number of individual (not part of a range) missing
196    values in MV. */
197 int
198 mv_n_values (const struct missing_values *mv)
199 {
200   return mv->type & 3;
201 }
202
203
204 /* Returns true if MV contains a numeric range,
205    false if MV is empty (or contains only individual values). */
206 bool
207 mv_has_range (const struct missing_values *mv)
208 {
209   return mv->type == MVT_RANGE || mv->type == MVT_RANGE_1;
210 }
211
212 /* Removes the numeric range from MV and stores it in *LOW and
213    *HIGH.  MV must contain a individual range (as determined by
214    mv_has_range()). */
215 void
216 mv_pop_range (struct missing_values *mv, double *low, double *high)
217 {
218   assert (mv_has_range (mv));
219   *low = mv->values[1].f;
220   *high = mv->values[2].f;
221   mv->type &= 3;
222 }
223
224 /* Returns the numeric range from MV  into *LOW and
225    *HIGH.  MV must contain a individual range (as determined by
226    mv_has_range()). */
227 void
228 mv_get_range (const struct missing_values *mv, double *low, double *high)
229 {
230   assert (mv_has_range (mv));
231   *low = mv->values[1].f;
232   *high = mv->values[2].f;
233 }
234
235
236 /* Returns true if values[IDX] is in use when the `type' member
237    is set to TYPE (in struct missing_values),
238    false otherwise. */
239 static bool
240 using_element (unsigned type, int idx)
241 {
242   assert (idx >= 0 && idx < 3);
243
244   switch (type)
245     {
246     case MVT_NONE:
247       return false;
248     case MVT_1:
249       return idx < 1;
250     case MVT_2:
251       return idx < 2;
252     case MVT_3:
253       return true;
254     case MVT_RANGE:
255       return idx > 0;
256     case MVT_RANGE_1:
257       return true;
258     }
259   NOT_REACHED ();
260 }
261
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
267    missing values. */
268 bool
269 mv_is_resizable (const struct missing_values *mv, int width)
270 {
271   int i;
272
273   if (width > MAX_SHORT_STRING && mv->type != MVT_NONE)
274     return false;
275
276   for (i = 0; i < 3; i++)
277     if (using_element (mv->type, i)
278         && !value_is_resizable (&mv->values[i], mv->width, width))
279       return false;
280
281   return true;
282 }
283
284 /* Resizes MV to the given WIDTH.  WIDTH must fit the constraints
285    explained for mv_is_resizable. */
286 void
287 mv_resize (struct missing_values *mv, int width)
288 {
289   int i;
290
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);
295   mv->width = width;
296 }
297
298 /* Returns true if D is a missing value in MV, false otherwise.
299    MV must be a set of numeric missing values. */
300 static bool
301 is_num_user_missing (const struct missing_values *mv, double d)
302 {
303   const union value *v = mv->values;
304   assert (mv->width == 0);
305   switch (mv->type)
306     {
307     case MVT_NONE:
308       return false;
309     case MVT_1:
310       return v[0].f == d;
311     case MVT_2:
312       return v[0].f == d || v[1].f == d;
313     case MVT_3:
314       return v[0].f == d || v[1].f == d || v[2].f == d;
315     case MVT_RANGE:
316       return v[1].f <= d && d <= v[2].f;
317     case MVT_RANGE_1:
318       return v[0].f == d || (v[1].f <= d && d <= v[2].f);
319     }
320   NOT_REACHED ();
321 }
322
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. */
326 static bool
327 is_str_user_missing (const struct missing_values *mv,
328                         const char s[])
329 {
330   const union value *v = mv->values;
331   assert (mv->width > 0);
332   switch (mv->type)
333     {
334     case MVT_NONE:
335       return false;
336     case MVT_1:
337       return !memcmp (v[0].s, s, mv->width);
338     case MVT_2:
339       return (!memcmp (v[0].s, s, mv->width)
340               || !memcmp (v[1].s, s, mv->width));
341     case MVT_3:
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));
345     case MVT_RANGE:
346     case MVT_RANGE_1:
347       NOT_REACHED ();
348     }
349   NOT_REACHED ();
350 }
351
352 /* Returns true if V is a missing value in the given CLASS in MV,
353    false otherwise. */
354 bool
355 mv_is_value_missing (const struct missing_values *mv, const union value *v,
356                      enum mv_class class)
357 {
358   return (mv->width == 0
359           ? mv_is_num_missing (mv, v->f, class)
360           : mv_is_str_missing (mv, v->s, class));
361 }
362
363 /* Returns true if D is a missing value in the given CLASS in MV,
364    false otherwise.
365    MV must be a set of numeric missing values. */
366 bool
367 mv_is_num_missing (const struct missing_values *mv, double d,
368                    enum mv_class class)
369 {
370   assert (mv->width == 0);
371   return ((class & MV_SYSTEM && d == SYSMIS)
372           || (class & MV_USER && is_num_user_missing (mv, d)));
373 }
374
375 /* Returns true if S[] is a missing value in the given CLASS in
376    MV, false otherwise.
377    MV must be a set of string missing values.
378    S[] must contain exactly as many characters as MV's width. */
379 bool
380 mv_is_str_missing (const struct missing_values *mv, const char s[],
381                    enum mv_class class)
382 {
383   assert (mv->width > 0);
384   return class & MV_USER && is_str_user_missing (mv, s);
385 }