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