Update all #include directives to the currently preferred style.
[pspp-builds.git] / src / math / interaction.c
1 /* PSPP - a program for statistical analysis.
2    Copyright (C) 2009, 2010, 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 /*
18   An interaction is a structure containing a "product" of other
19   variables. The variables can be either categorical or numeric.
20   If the variables are all numeric, the interaction is just the
21   scalar product. If any of the variables are categorical, their
22   product is a vector containing 0's in all but one entry. This entry
23   is found by combining the vectors corresponding to the variables'
24   OBS_VALS member. If there are K categorical variables, each with
25   N_1, N_2, ..., N_K categories, then the interaction will have
26   N_1 * N_2 * N_3 *...* N_K - 1 entries.
27
28   When using these functions, make sure the orders of variables and
29   values match when appropriate.
30  */
31
32 #include <config.h>
33
34 #include "math/interaction.h"
35
36 #include <assert.h>
37 #include <string.h>
38 #include <unistr.h>
39
40 #include "data/dictionary.h"
41 #include "data/value.h"
42 #include "data/variable.h"
43
44 #include "gl/xalloc.h"
45
46 struct interaction_variable
47 {
48   int n_vars;
49   const struct variable **members;
50   struct variable *intr;
51   size_t n_alpha;
52 };
53
54 struct interaction_value
55 {
56   const struct interaction_variable *intr;
57   union value val; /* Concatenation of the string values in this
58                       interaction's value, or the product of a bunch
59                       of numeric values for a purely numeric
60                       interaction.
61                     */
62   double f; /* Product of the numerical values in this interaction's value. */
63 };
64
65 /*
66   An interaction_variable has type alpha if any of members have type
67   alpha. Otherwise, its type is numeric.
68  */
69 struct interaction_variable *
70 interaction_variable_create (const struct variable **vars, int n_vars)
71 {
72   struct interaction_variable *result = NULL;
73   size_t i;
74
75   if (n_vars > 0)
76     {
77       int width = 0;
78
79       result = xmalloc (sizeof (*result));
80       result->n_alpha = 0;
81       result->members = xnmalloc (n_vars, sizeof (*result->members));
82       result->n_vars = n_vars;
83       for (i = 0; i < n_vars; i++)
84         {
85           result->members[i] = vars[i];
86           if (var_is_alpha (vars[i]))
87             {
88               result->n_alpha++;
89               width += var_get_width (vars[i]);
90             }
91         }
92       result->intr = dict_create_internal_var (0, width);
93     }
94
95   return result;
96 }
97 void interaction_variable_destroy (struct interaction_variable *iv)
98 {
99   dict_destroy_internal_var (iv->intr);
100   free (iv->members);
101   free (iv);
102 }
103
104 /*
105   Get one of the member variables.
106  */
107 const struct variable *
108 interaction_get_member (const struct interaction_variable *iv, size_t i)
109 {
110   return iv->members[i];
111 }
112
113 size_t
114 interaction_get_n_vars (const struct interaction_variable *iv)
115 {
116   return (iv == NULL) ? 0 : iv->n_vars;
117 }
118
119 size_t
120 interaction_get_n_alpha (const struct interaction_variable *iv)
121 {
122   return iv->n_alpha;
123 }
124
125 size_t
126 interaction_get_n_numeric (const struct interaction_variable *iv)
127 {
128   return (interaction_get_n_vars (iv) - interaction_get_n_alpha (iv));
129 }
130
131 /*
132   Get the interaction variable itself.
133  */
134 const struct variable *
135 interaction_get_variable (const struct interaction_variable *iv)
136 {
137   return iv->intr;
138 }
139 /*
140   Given list of values, compute the value of the corresponding
141   interaction.  This "value" is not stored as the typical vector of
142   0's and one double, but rather the string values are concatenated to
143   make one big string value, and the numerical values are multiplied
144   together to give the non-zero entry of the corresponding vector.
145  */
146 struct interaction_value *
147 interaction_value_create (const struct interaction_variable *var, const union value **vals)
148 {
149   struct interaction_value *result = NULL;
150   
151   if (var != NULL)
152     {
153       size_t i;
154       int val_width = var_get_width (interaction_get_variable (var));
155       int offset = 0;
156       size_t n_vars = interaction_get_n_vars (var);
157
158       result = xmalloc (sizeof (*result));
159       result->intr = var;
160
161       value_init (&result->val, val_width);
162
163       result->f = 1.0;
164       for (i = 0; i < n_vars; i++)
165         {
166           const struct variable *member = interaction_get_member (var, i);
167
168           if (var_is_value_missing (member, vals[i], MV_ANY))
169             {
170               value_set_missing (&result->val, val_width);
171               result->f = SYSMIS;
172               break;
173             }
174           else
175             {
176               if (var_is_alpha (var->members[i]))
177                 {
178                   uint8_t *val = value_str_rw (&result->val, val_width);
179                   int w = var_get_width (var->members[i]);
180                   u8_cpy (val + offset, value_str (vals[i], w), w);
181                   offset += w;
182                 }
183               else if (var_is_numeric (var->members[i]))
184                 {
185                   result->f *= vals[i]->f;
186                 }
187             }
188         }
189       if (interaction_get_n_alpha (var) == 0)
190         {
191           /*
192             If there are no categorical variables, then the
193             interaction consists of only numeric data. In this case,
194             code that uses this interaction_value will see the union
195             member as the numeric value. If we were to store that
196             numeric value in result->f as well, the calling code may
197             inadvertently square this value by multiplying by
198             result->val->f. Such multiplication would be correct for an
199             interaction consisting of both categorical and numeric
200             data, but a mistake for purely numerical interactions. To
201             avoid the error, we set result->f to 1.0 for numeric
202             interactions.
203            */
204           result->val.f = result->f;
205           result->f = 1.0;
206         }
207     }
208   return result;
209 }
210
211 const union value *
212 interaction_value_get (const struct interaction_value *val)
213 {
214   return &val->val;
215 }
216
217 /*
218   Returns the numeric value of the non-zero entry for the vector
219   corresponding to this interaction.  Do not use this function to get
220   the numeric value of a purely numeric interaction. Instead, use the
221   union value * returned by interaction_value_get.
222  */
223 double 
224 interaction_value_get_nonzero_entry (const struct interaction_value *val)
225 {
226   if (val != NULL)
227     return val->f;
228   return 1.0;
229 }
230
231 void 
232 interaction_value_destroy (struct interaction_value *val)
233 {
234   if (val != NULL)
235     {
236       int val_width = var_get_width (interaction_get_variable (val->intr));
237
238       value_destroy (&val->val, val_width);
239       free (val);
240     }
241 }
242
243 /*
244   Return a value from a variable that is an interaction. 
245  */
246 struct interaction_value *
247 interaction_case_data (const struct ccase *ccase, const struct interaction_variable *iv)
248 {
249   size_t i;
250   size_t n_vars;
251   const struct variable *member;
252   const union value **vals = NULL;
253
254   n_vars = interaction_get_n_vars (iv);
255   vals = xnmalloc (n_vars, sizeof (*vals));
256
257   for (i = 0; i < n_vars; i++)
258         {
259           member = interaction_get_member (iv, i);
260           vals[i] = case_data (ccase, member);
261         }
262
263   return interaction_value_create (iv, vals);
264 }
265
266 bool
267 is_interaction (const struct variable *var, const struct interaction_variable **iv, size_t n_intr)
268 {
269   size_t i;
270   const struct variable *intr;
271   
272   for (i = 0; i < n_intr; i++)
273     {
274       intr = interaction_get_variable (iv[i]);
275       if (intr == var)
276         {
277           return true;
278         }
279     }
280   return false;
281 }
282