Remove "Written by Ben Pfaff <blp@gnu.org>" lines everywhere.
[pspp-builds.git] / src / data / missing-values.c
1 /* PSPP - computes sample statistics.
2    Copyright (C) 2005 Free Software Foundation, Inc.
3
4    This program is free software; you can redistribute it and/or
5    modify it under the terms of the GNU General Public License as
6    published by the Free Software Foundation; either version 2 of the
7    License, or (at your option) any later version.
8
9    This program is distributed in the hope that it will be useful, but
10    WITHOUT ANY WARRANTY; without even the implied warranty of
11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12    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, write to the Free Software
16    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
17    02110-1301, USA. */
18
19 #include <config.h>
20 #include "missing-values.h"
21 #include <assert.h>
22 #include <stdlib.h>
23 #include <libpspp/assertion.h>
24 #include <libpspp/str.h>
25
26
27 /* Initializes MV as a set of missing values for a variable of
28    the given WIDTH.  Although only numeric variables and short
29    string variables may have missing values, WIDTH may be any
30    valid variable width. */
31 void
32 mv_init (struct missing_values *mv, int width) 
33 {
34   assert (width >= 0 && width <= MAX_STRING);
35   mv->type = MV_NONE;
36   mv->width = width;
37 }
38
39 void 
40 mv_set_type(struct missing_values *mv, enum mv_type type)
41 {
42   mv->type = type;
43 }
44
45
46 /* Copies SRC to MV. */
47 void
48 mv_copy (struct missing_values *mv, const struct missing_values *src) 
49 {
50   assert(src);
51
52   *mv = *src;
53 }
54
55 /* Returns true if MV is an empty set of missing values. */
56 bool
57 mv_is_empty (const struct missing_values *mv) 
58 {
59   return mv->type == MV_NONE;
60 }
61
62 /* Returns the width of the missing values that MV may
63    contain. */
64 int
65 mv_get_width (const struct missing_values *mv)
66 {
67   return mv->width;
68 }
69
70 /* Attempts to add individual value V to the set of missing
71    values MV.  Returns true if successful, false if MV has no
72    more room for missing values.  (Long string variables never
73    accept missing values.) */
74 bool
75 mv_add_value (struct missing_values *mv, const union value *v)
76 {
77   if (mv->width > MAX_SHORT_STRING)
78     return false;
79   switch (mv->type) 
80     {
81     case MV_NONE:
82     case MV_1:
83     case MV_2:
84     case MV_RANGE:
85       mv->values[mv->type & 3] = *v;
86       mv->type++;
87       return true;
88
89     case MV_3:
90     case MV_RANGE_1:
91       return false;
92     }
93   NOT_REACHED ();
94 }
95
96 /* Attempts to add S to the set of string missing values MV.  S
97    must contain exactly as many characters as MV's width.
98    Returns true if successful, false if MV has no more room for
99    missing values.  (Long string variables never accept missing
100    values.) */
101 bool
102 mv_add_str (struct missing_values *mv, const char s[]) 
103 {
104   assert (mv->width > 0);
105   return mv_add_value (mv, (union value *) s);
106 }
107
108 /* Attempts to add D to the set of numeric missing values MV.
109    Returns true if successful, false if MV has no more room for
110    missing values.  */
111 bool
112 mv_add_num (struct missing_values *mv, double d) 
113 {
114   assert (mv->width == 0);
115   return mv_add_value (mv, (union value *) &d);
116 }
117
118 /* Attempts to add range [LOW, HIGH] to the set of numeric
119    missing values MV.  Returns true if successful, false if MV
120    has no room for a range, or if LOW > HIGH. */
121 bool
122 mv_add_num_range (struct missing_values *mv, double low, double high) 
123 {
124   assert (mv->width == 0);
125   if (low > high)
126     return false;
127   switch (mv->type) 
128     {
129     case MV_NONE:
130     case MV_1:
131       mv->values[1].f = low;
132       mv->values[2].f = high;
133       mv->type |= 4;
134       return true;
135
136     case MV_2:
137     case MV_3:
138     case MV_RANGE:
139     case MV_RANGE_1:
140       return false;
141     }
142   NOT_REACHED ();
143 }
144
145 /* Returns true if MV contains an individual value,
146    false if MV is empty (or contains only a range). */
147 bool
148 mv_has_value (const struct missing_values *mv)
149 {
150   switch (mv->type) 
151     {
152     case MV_1:
153     case MV_2:
154     case MV_3:
155     case MV_RANGE_1:
156       return true;
157       
158     case MV_NONE:
159     case MV_RANGE:
160       return false;
161     }
162   NOT_REACHED ();
163 }
164
165 /* Removes one individual value from MV and stores it in *V.
166    MV must contain an individual value (as determined by
167    mv_has_value()). */
168 void
169 mv_pop_value (struct missing_values *mv, union value *v) 
170 {
171   assert (mv_has_value (mv));
172   mv->type--;
173   *v = mv->values[mv->type & 3];
174 }
175
176 /* Stores  a value  in *V.
177    MV must contain an individual value (as determined by
178    mv_has_value()). 
179    IDX is the zero based index of the value to get
180 */
181 void
182 mv_peek_value (const struct missing_values *mv, union value *v, int idx) 
183 {
184   assert (idx >= 0 ) ;
185   assert (idx < 3);
186
187   assert (mv_has_value (mv));
188   *v = mv->values[idx];
189 }
190
191 void 
192 mv_replace_value (struct missing_values *mv, const union value *v, int idx)
193 {
194   assert (idx >= 0) ;
195   assert (idx < mv_n_values(mv));
196
197   mv->values[idx] = *v;
198 }
199
200
201
202 int  
203 mv_n_values (const struct missing_values *mv)
204 {
205   assert(mv_has_value(mv));
206   return mv->type & 3;
207 }
208
209
210 /* Returns true if MV contains a numeric range,
211    false if MV is empty (or contains only individual values). */
212 bool
213 mv_has_range (const struct missing_values *mv) 
214 {
215   switch (mv->type) 
216     {
217     case MV_RANGE:
218     case MV_RANGE_1:
219       return true;
220       
221     case MV_NONE:
222     case MV_1:
223     case MV_2:
224     case MV_3:
225       return false;
226     }
227   NOT_REACHED ();
228 }
229
230 /* Removes the numeric range from MV and stores it in *LOW and
231    *HIGH.  MV must contain a individual range (as determined by
232    mv_has_range()). */
233 void
234 mv_pop_range (struct missing_values *mv, double *low, double *high) 
235 {
236   assert (mv_has_range (mv));
237   *low = mv->values[1].f;
238   *high = mv->values[2].f;
239   mv->type &= 3;
240 }
241
242
243 /* Returns the numeric range from MV  into *LOW and
244    *HIGH.  MV must contain a individual range (as determined by
245    mv_has_range()). */
246 void
247 mv_peek_range (const struct missing_values *mv, double *low, double *high) 
248 {
249   assert (mv_has_range (mv));
250   *low = mv->values[1].f;
251   *high = mv->values[2].f;
252 }
253
254
255 /* Returns true if values[IDX] is in use when the `type' member
256    is set to TYPE (in struct missing_values),
257    false otherwise. */
258 static bool
259 using_element (unsigned type, int idx) 
260 {
261   assert (idx >= 0 && idx < 3);
262   
263   switch (type) 
264     {
265     case MV_NONE:
266       return false;
267     case MV_1:
268       return idx < 1;
269     case MV_2:
270       return idx < 2;
271     case MV_3:
272       return true;
273     case MV_RANGE:
274       return idx > 0;
275     case MV_RANGE_1:
276       return true;
277     }
278   NOT_REACHED ();
279 }
280
281 /* Returns true if S contains only spaces between indexes
282    NEW_WIDTH (inclusive) and OLD_WIDTH (exclusive),
283    false otherwise. */
284 static bool
285 can_resize_string (const char *s, int old_width, int new_width) 
286 {
287   int i;
288
289   assert (new_width < old_width);
290   for (i = new_width; i < old_width; i++)
291     if (s[i] != ' ')
292       return false;
293   return true;
294 }
295
296 /* Returns true if MV can be resized to the given WIDTH with
297    mv_resize(), false otherwise.  Resizing to the same width is
298    always possible.  Resizing to a long string WIDTH is only
299    possible if MV is an empty set of missing values; otherwise,
300    resizing to a larger WIDTH is always possible.  Resizing to a
301    shorter width is possible only when each missing value
302    contains only spaces in the characters that will be
303    trimmed. */
304 bool
305 mv_is_resizable (const struct missing_values *mv, int width) 
306 {
307   assert ((width == 0) == (mv->width == 0));
308   if (width > MAX_SHORT_STRING && mv->type != MV_NONE)
309     return false;
310   else if (width >= mv->width)
311     return true;
312   else 
313     {
314       int i;
315       
316       for (i = 0; i < 3; i++)
317         if (using_element (mv->type, i)
318             && !can_resize_string (mv->values[i].s, mv->width, width))
319           return false;
320       return true;
321     }
322 }
323
324 /* Resizes MV to the given WIDTH.  WIDTH must fit the constraints
325    explained for mv_is_resizable(). */
326 void
327 mv_resize (struct missing_values *mv, int width) 
328 {
329   assert (mv_is_resizable (mv, width));
330   if (width > mv->width && mv->type != MV_NONE) 
331     {
332       int i;
333       
334       for (i = 0; i < 3; i++)
335         memset (mv->values[i].s + mv->width, ' ', width - mv->width);
336     }
337   mv->width = width;
338 }
339
340 /* Returns true if V is system missing or a missing value in MV,
341    false otherwise. */
342 bool
343 mv_is_value_missing (const struct missing_values *mv, const union value *v)
344 {
345   return (mv->width == 0
346           ? mv_is_num_missing (mv, v->f)
347           : mv_is_str_missing (mv, v->s));
348 }
349
350 /* Returns true if D is system missing or a missing value in MV,
351    false otherwise.
352    MV must be a set of numeric missing values. */
353 bool
354 mv_is_num_missing (const struct missing_values *mv, double d)
355 {
356   assert (mv->width == 0);
357   return d == SYSMIS || mv_is_num_user_missing (mv, d);
358 }
359
360 /* Returns true if S[] is a missing value in MV, false otherwise.
361    MV must be a set of string missing values. 
362    S[] must contain exactly as many characters as MV's width. */
363 bool
364 mv_is_str_missing (const struct missing_values *mv, const char s[])
365 {
366   return mv_is_str_user_missing (mv, s);
367 }
368
369 /* Returns true if V is a missing value in MV, false otherwise. */
370 bool
371 mv_is_value_user_missing (const struct missing_values *mv,
372                           const union value *v)
373 {
374   return (mv->width == 0
375           ? mv_is_num_user_missing (mv, v->f)
376           : mv_is_str_user_missing (mv, v->s));
377 }
378
379 /* Returns true if D is a missing value in MV, false otherwise.
380    MV must be a set of numeric missing values. */
381 bool
382 mv_is_num_user_missing (const struct missing_values *mv, double d)
383 {
384   const union value *v = mv->values;
385   assert (mv->width == 0);
386   switch (mv->type) 
387     {
388     case MV_NONE:
389       return false;
390     case MV_1:
391       return v[0].f == d;
392     case MV_2:
393       return v[0].f == d || v[1].f == d;
394     case MV_3:
395       return v[0].f == d || v[1].f == d || v[2].f == d;
396     case MV_RANGE:
397       return v[1].f <= d && d <= v[2].f;
398     case MV_RANGE_1:
399       return v[0].f == d || (v[1].f <= d && d <= v[2].f);
400     }
401   NOT_REACHED ();
402 }
403
404 /* Returns true if S[] is a missing value in MV, false otherwise.
405    MV must be a set of string missing values. 
406    S[] must contain exactly as many characters as MV's width. */
407 bool
408 mv_is_str_user_missing (const struct missing_values *mv,
409                         const char s[])
410 {
411   const union value *v = mv->values;
412   assert (mv->width > 0);
413   switch (mv->type) 
414     {
415     case MV_NONE:
416       return false;
417     case MV_1:
418       return !memcmp (v[0].s, s, mv->width);
419     case MV_2:
420       return (!memcmp (v[0].s, s, mv->width)
421               || !memcmp (v[1].s, s, mv->width));
422     case MV_3:
423       return (!memcmp (v[0].s, s, mv->width)
424               || !memcmp (v[1].s, s, mv->width)
425               || !memcmp (v[2].s, s, mv->width));
426     case MV_RANGE:
427     case MV_RANGE_1:
428       NOT_REACHED ();
429     }
430   NOT_REACHED ();
431 }
432
433 /* Returns true if MV is a set of numeric missing values and V is
434    the system missing value. */
435 bool
436 mv_is_value_system_missing (const struct missing_values *mv,
437                             const union value *v)
438 {
439   return mv->width == 0 && v->f == SYSMIS;
440 }