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