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