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