output: Introduce pivot tables.
[pspp] / src / math / categoricals.c
1 /* PSPP - a program for statistical analysis.
2    Copyright (C) 2009, 2010, 2011, 2012, 2014 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 #include <config.h>
18
19 #include "math/categoricals.h"
20 #include "math/interaction.h"
21
22 #include <float.h>
23 #include <stdio.h>
24
25 #include "data/case.h"
26 #include "data/value.h"
27 #include "data/variable.h"
28 #include "libpspp/array.h"
29 #include "libpspp/hmap.h"
30 #include "libpspp/pool.h"
31 #include "libpspp/str.h"
32 #include "libpspp/hash-functions.h"
33
34 #include "gl/xalloc.h"
35
36 #define CATEGORICALS_DEBUG 0
37
38 struct value_node
39 {
40   struct hmap_node node;      /* Node in hash map. */
41   union value val;            /* The value */
42   int index;                  /* A zero based unique index for this value */
43 };
44
45 static struct value_node *
46 lookup_value (const struct hmap *map, const union value *val,
47               unsigned int hash, int width)
48 {
49   struct value_node *vn;
50   HMAP_FOR_EACH_WITH_HASH (vn, struct value_node, node, hash, map)
51     if (value_equal (&vn->val, val, width))
52       return vn;
53   return NULL;
54 }
55
56 /* A variable used in a categoricals object.  */
57 struct variable_node
58   {
59     struct hmap_node node;      /* In struct categorical's 'varmap'. */
60     const struct variable *var; /* The variable. */
61     struct hmap valmap;         /* Contains "struct value_node"s. */
62     union value *values;        /* Values in valmap, as a sorted array. */
63   };
64
65 static int
66 compare_value_node_3way (const void *vn1_, const void *vn2_, const void *aux)
67 {
68   const struct value_node *const *vn1p = vn1_;
69   const struct value_node *const *vn2p = vn2_;
70
71   const struct variable_node *vn = aux;
72
73   return value_compare_3way (&(*vn1p)->val, &(*vn2p)->val,
74                              var_get_width (vn->var));
75 }
76
77 static struct variable_node *
78 lookup_variable (const struct hmap *map, const struct variable *var)
79 {
80   struct variable_node *vn;
81   HMAP_FOR_EACH_WITH_HASH (vn, struct variable_node, node,
82                            hash_pointer (var, 0), map)
83     if (vn->var == var)
84       return vn;
85   return NULL;
86 }
87
88 struct interact_params
89 {
90   /* The interaction, and an array with iact->n_vars elements such that
91      varnodes[x] points to the variable_node for iact->vars[x]. */
92   const struct interaction *iact;
93   struct variable_node **varnodes;
94
95   /* An example of each interaction that appears in the data, like a frequency
96      table for 'iact'.  By construction, the number of elements must be less
97      than or equal to 'n_cats'.
98
99      categoricals_update() updates 'ivmap' case-by-case, then
100      categoricals_done() dumps 'ivmap' into 'ivs' and sorts it. */
101   struct hmap ivmap;            /* Contains "struct interaction_value"s. */
102   struct interaction_value **ivs;
103
104   int base_df;
105   int base_cats;
106
107   /* Product of hmap_count(&varnodes[*]->valmap), that is, the maximum number
108      of distinct values of this interaction. */
109   int n_cats;
110
111   /* Product of degrees of freedom of all the variables. */
112   int df_prod;
113
114   double *enc_sum;
115
116   /* Sum of ivs[*]->cc. */
117   double cc;
118 };
119
120 struct interaction_value
121   {
122     struct hmap_node node;    /* In struct interact_params's ivmap. */
123     struct ccase *ccase;      /* A case representative of the interaction. */
124     double cc;                /* Total weight of cases for this interaction. */
125     void *user_data;
126   };
127
128 static int
129 compare_interaction_value_3way (const void *vn1_, const void *vn2_,
130                                 const void *aux)
131 {
132   const struct interaction_value *const *vn1p = vn1_;
133   const struct interaction_value *const *vn2p = vn2_;
134
135   const struct interact_params *iap = aux;
136
137   return interaction_case_cmp_3way (iap->iact, (*vn1p)->ccase, (*vn2p)->ccase);
138 }
139
140 struct categoricals
141 {
142   /* The weight variable */
143   const struct variable *wv;
144
145   struct interact_params *iap;  /* Interaction parameters. */
146   size_t n_iap;
147
148   /* Contains a "struct variable_node" for each variable in 'iap'. */
149   struct hmap varmap;
150
151   /* A map to enable the lookup of variables indexed by subscript.
152      This map considers only the N - 1 of the N variables.
153   */
154   int *df_to_iact; /* 'df_sum' elements. */
155   size_t df_sum;
156
157   /* Like the above, but uses all N variables. */
158   int *cat_to_iact; /* 'n_cats_total' elements. */
159   size_t n_cats_total;
160
161   struct pool *pool;
162
163   /* Missing values in the factor variables to be excluded */
164   enum mv_class fctr_excl;
165
166   const void *aux1;
167   void *aux2;
168
169   bool sane;
170
171   const struct payload *payload;
172 };
173
174
175 bool
176 categoricals_isbalanced (const struct categoricals *cat)
177 {
178   for (int i = 0 ; i < cat->n_iap; ++i)
179     {
180       int v;
181       const struct interact_params *iap = &cat->iap[i];
182
183       double oval = -1.0;
184       for (v = 0; v < hmap_count (&iap->ivmap); ++v)
185         {
186           const struct interaction_value *iv = iap->ivs[v];
187           if (oval == -1.0)
188             oval = iv->cc;
189           if (oval != iv->cc)
190             return false;
191         }
192     }
193   return true;
194 }
195
196
197 static void
198 categoricals_dump (const struct categoricals *cat)
199 {
200   if (CATEGORICALS_DEBUG)
201     {
202       int i;
203
204       printf ("df to interaction map:\n");
205       for (i = 0; i < cat->df_sum; ++i)
206         printf (" %d", cat->df_to_iact[i]);
207       printf ("\n");
208
209       printf ("Category to interaction map:\n");
210       for (i = 0; i < cat->n_cats_total; ++i)
211         printf (" %d", cat->cat_to_iact[i]);
212       printf ("\n");
213
214       printf ("Number of interactions %zu\n", cat->n_iap);
215       for (i = 0 ; i < cat->n_iap; ++i)
216         {
217           int v;
218           struct string str;
219           const struct interact_params *iap = &cat->iap[i];
220           const struct interaction *iact = iap->iact;
221
222           ds_init_empty (&str);
223           interaction_to_string (iact, &str);
224
225           printf ("\nInteraction: \"%s\" (number of categories: %d); ", ds_cstr (&str), iap->n_cats);
226           ds_destroy (&str);
227           printf ("Base index (df/categories): %d/%d\n", iap->base_df, iap->base_cats);
228
229           printf ("\t(");
230           for (v = 0; v < hmap_count (&iap->ivmap); ++v)
231             {
232               int vv;
233               const struct interaction_value *iv = iap->ivs[v];
234
235               if (v > 0)  printf ("   ");
236               printf ("{");
237               for (vv = 0; vv < iact->n_vars; ++vv)
238                 {
239                   const struct variable *var = iact->vars[vv];
240                   const union value *val = case_data (iv->ccase, var);
241                   struct variable_node *vn = iap->varnodes[vv];
242                   const int width = var_get_width (var);
243                   unsigned int valhash = value_hash (val, width, 0);
244                   struct value_node *valn = lookup_value (&vn->valmap, val, valhash, width);
245
246                   assert (vn->var == var);
247
248                   printf ("%.*g(%d)", DBL_DIG + 1, val->f, valn->index);
249                   if (vv < iact->n_vars - 1)
250                     printf (", ");
251                 }
252               printf ("}");
253             }
254           printf (")\n");
255         }
256     }
257 }
258
259 void
260 categoricals_destroy (struct categoricals *cat)
261 {
262   if (!cat)
263     return;
264
265   for (int i = 0; i < cat->n_iap; ++i)
266     {
267       /* Unref any cases that we reffed. */
268       struct interaction_value *iv;
269       HMAP_FOR_EACH (iv, struct interaction_value, node, &cat->iap[i].ivmap)
270         {
271           if (cat->payload && cat->payload->destroy)
272             cat->payload->destroy (cat->aux1, cat->aux2, iv->user_data);
273           case_unref (iv->ccase);
274         }
275
276       free (cat->iap[i].enc_sum);
277       hmap_destroy (&cat->iap[i].ivmap);
278     }
279
280   /* Interate over each variable and delete its value map.
281
282      The values themselves are part of the pool. */
283   struct variable_node *vn;
284   HMAP_FOR_EACH (vn, struct variable_node, node, &cat->varmap)
285     hmap_destroy (&vn->valmap);
286
287   hmap_destroy (&cat->varmap);
288
289   pool_destroy (cat->pool);
290
291   free (cat);
292 }
293
294 static struct interaction_value *
295 lookup_case (const struct hmap *map, const struct interaction *iact,
296              const struct ccase *c)
297 {
298   size_t hash = interaction_case_hash (iact, c, 0);
299   struct interaction_value *iv;
300   HMAP_FOR_EACH_WITH_HASH (iv, struct interaction_value, node, hash, map)
301     if (interaction_case_equal (iact, c, iv->ccase))
302       return iv;
303   return NULL;
304 }
305
306 /* Returns true iff CAT is sane, that is, if it is complete and has at least
307    one value. */
308 bool
309 categoricals_sane (const struct categoricals *cat)
310 {
311   return cat->sane;
312 }
313
314 /* Creates and returns a new categoricals object whose variables come from the
315    N_INTER interactions objects in the array starting at INTER.  (The INTER
316    objects must outlive the categoricals object because it uses them
317    internally.)
318
319    FCTR_EXCL determines which cases are listwise ignored by
320    categoricals_update(). */
321 struct categoricals *
322 categoricals_create (struct interaction *const *inter, size_t n_inter,
323                      const struct variable *wv, enum mv_class fctr_excl)
324 {
325   struct categoricals *cat = xzalloc (sizeof *cat);
326   cat->iap = pool_calloc (cat->pool, n_inter, sizeof *cat->iap);
327   cat->n_iap = n_inter;
328   cat->wv = wv;
329   cat->pool = pool_create ();
330   cat->fctr_excl = fctr_excl;
331
332   hmap_init (&cat->varmap);
333   for (size_t i = 0; i < cat->n_iap; ++i)
334     {
335       struct interact_params *iap = &cat->iap[i];
336       hmap_init (&iap->ivmap);
337       iap->iact = inter[i];
338       iap->cc = 0.0;
339       iap->varnodes = pool_nmalloc (cat->pool, iap->iact->n_vars,
340                                     sizeof *iap->varnodes);
341       for (size_t v = 0; v < inter[i]->n_vars; ++v)
342         {
343           const struct variable *var = inter[i]->vars[v];
344           struct variable_node *vn = lookup_variable (&cat->varmap, var);
345           if (!vn)
346             {
347               vn = pool_malloc (cat->pool, sizeof *vn);
348               vn->var = var;
349               vn->values = NULL;
350               hmap_init (&vn->valmap);
351               hmap_insert (&cat->varmap, &vn->node, hash_pointer (var, 0));
352             }
353           iap->varnodes[v] = vn;
354         }
355     }
356
357   return cat;
358 }
359
360 void
361 categoricals_update (struct categoricals *cat, const struct ccase *c)
362 {
363   if (!cat)
364     return;
365   assert (!cat->df_to_iact);
366   assert (!cat->cat_to_iact);
367
368   double weight;
369   weight = cat->wv ? case_data (c, cat->wv)->f : 1.0;
370   weight = var_force_valid_weight (cat->wv, weight, NULL);
371
372   /* Update the frequency table for each variable. */
373   struct variable_node *vn;
374   HMAP_FOR_EACH (vn, struct variable_node, node, &cat->varmap)
375     {
376       const int width = var_get_width (vn->var);
377       const union value *val = case_data (c, vn->var);
378       unsigned int hash = value_hash (val, width, 0);
379
380       struct value_node *valn = lookup_value (&vn->valmap, val, hash, width);
381       if (valn == NULL)
382         {
383           valn = pool_malloc (cat->pool, sizeof *valn);
384           valn->index = -1;
385           value_init (&valn->val, width);
386           value_copy (&valn->val, val, width);
387           hmap_insert (&vn->valmap, &valn->node, hash);
388         }
389     }
390
391   /* Update the frequency table for full interactions. */
392   for (int i = 0; i < cat->n_iap; ++i)
393     {
394       struct interact_params *iap = &cat->iap[i];
395       const struct interaction *iact = iap->iact;
396       if (interaction_case_is_missing (iact, c, cat->fctr_excl))
397         continue;
398
399       unsigned int hash = interaction_case_hash (iact, c, 0);
400       struct interaction_value *node = lookup_case (&iap->ivmap, iact, c);
401       if (!node)
402         {
403           node = pool_malloc (cat->pool, sizeof *node);
404           node->ccase = case_ref (c);
405           node->cc = weight;
406
407           hmap_insert (&iap->ivmap, &node->node, hash);
408
409           if (cat->payload)
410             node->user_data = cat->payload->create (cat->aux1, cat->aux2);
411         }
412       else
413         node->cc += weight;
414       iap->cc += weight;
415
416       if (cat->payload)
417         cat->payload->update (cat->aux1, cat->aux2, node->user_data, c,
418                               weight);
419     }
420 }
421
422 /* Return the number of categories (distinct values) for interaction IDX in
423    CAT. */
424 size_t
425 categoricals_n_count (const struct categoricals *cat, size_t idx)
426 {
427   return hmap_count (&cat->iap[idx].ivmap);
428 }
429
430 /* Return the total number of categories across all interactions in CAT. */
431 size_t
432 categoricals_n_total (const struct categoricals *cat)
433 {
434   return categoricals_is_complete (cat) ? cat->n_cats_total : 0;
435 }
436
437 /* Returns the number of degrees of freedom for interaction IDX within CAT. */
438 size_t
439 categoricals_df (const struct categoricals *cat, size_t idx)
440 {
441   const struct interact_params *iap = &cat->iap[idx];
442   return iap->df_prod;
443 }
444
445 /* Returns the total degrees of freedom for CAT. */
446 size_t
447 categoricals_df_total (const struct categoricals *cat)
448 {
449   return cat ? cat->df_sum : 0;
450 }
451
452 /* Returns true iff categoricals_done() has been called for CAT. */
453 bool
454 categoricals_is_complete (const struct categoricals *cat)
455 {
456   return cat->df_to_iact != NULL;
457 }
458
459 /* This function must be called (once) before any call to the *_by_subscript or
460   *_by_category functions, but AFTER any calls to categoricals_update.  If this
461   function returns false, then no calls to _by_subscript or *_by_category are
462   allowed. */
463 void
464 categoricals_done (const struct categoricals *cat_)
465 {
466   struct categoricals *cat = CONST_CAST (struct categoricals *, cat_);
467   if (!cat || categoricals_is_complete (cat))
468     return;
469
470   /* Assign 'index' to each variables' value_nodes, counting up from 0 in
471      ascending order by value. */
472   struct variable_node *vn;
473   HMAP_FOR_EACH (vn, struct variable_node, node, &cat->varmap)
474     {
475       size_t n_vals = hmap_count (&vn->valmap);
476       if (!n_vals)
477         {
478           cat->sane = false;
479           return;
480         }
481
482       struct value_node **nodes = xcalloc (sizeof *nodes, n_vals);
483       int x = 0;
484       struct value_node *valnd;
485       HMAP_FOR_EACH (valnd, struct value_node, node, &vn->valmap)
486         nodes[x++] = valnd;
487       sort (nodes, n_vals, sizeof *nodes, compare_value_node_3way, vn);
488       for (x = 0; x < n_vals; ++x)
489         nodes[x]->index = x;
490       free (nodes);
491     }
492
493   /* Calculate the degrees of freedom, and the number of categories. */
494   cat->df_sum = 0;
495   cat->n_cats_total = 0;
496   for (int i = 0 ; i < cat->n_iap; ++i)
497     {
498       struct interact_params *iap = &cat->iap[i];
499       const struct interaction *iact = iap->iact;
500
501       iap->df_prod = 1;
502       iap->n_cats = 1;
503       for (int v = 0 ; v < iact->n_vars; ++v)
504         {
505           size_t n_vals = hmap_count (&iap->varnodes[v]->valmap);
506
507           iap->df_prod *= n_vals - 1;
508           iap->n_cats *= n_vals;
509         }
510
511       if (iact->n_vars > 0)
512         cat->df_sum += iap->df_prod;
513       cat->n_cats_total += iap->n_cats;
514     }
515
516
517   cat->df_to_iact = pool_calloc (cat->pool, cat->df_sum,
518                                  sizeof *cat->df_to_iact);
519
520   cat->cat_to_iact = pool_calloc (cat->pool, cat->n_cats_total,
521                                   sizeof *cat->cat_to_iact);
522
523   int idx_df = 0;
524   int idx_cat = 0;
525   for (int i = 0; i < cat->n_iap; ++i)
526     {
527       struct interact_params *iap = &cat->iap[i];
528
529       iap->base_df = idx_df;
530       iap->base_cats = idx_cat;
531
532       /* For some purposes (eg CONTRASTS in ONEWAY) the values need to be
533          sorted. */
534       iap->ivs = pool_nmalloc (cat->pool, hmap_count (&iap->ivmap),
535                                sizeof *iap->ivs);
536       int x = 0;
537       struct interaction_value *ivn;
538       HMAP_FOR_EACH (ivn, struct interaction_value, node, &iap->ivmap)
539         iap->ivs[x++] = ivn;
540       sort (iap->ivs, x, sizeof *iap->ivs,
541             compare_interaction_value_3way, iap);
542
543       /* Populate the variable maps. */
544       if (iap->iact->n_vars)
545         for (int j = 0; j < iap->df_prod; ++j)
546           cat->df_to_iact[idx_df++] = i;
547
548       for (int j = 0; j < iap->n_cats; ++j)
549         cat->cat_to_iact[idx_cat++] = i;
550     }
551
552   categoricals_dump (cat);
553
554   /* Tally up the sums for all the encodings */
555   for (int i = 0; i < cat->n_iap; ++i)
556     {
557       struct interact_params *iap = &cat->iap[i];
558       const struct interaction *iact = iap->iact;
559
560       const int df = iact->n_vars ? iap->df_prod : 0;
561
562       iap->enc_sum = xcalloc (df, sizeof *iap->enc_sum);
563
564       for (int y = 0; y < hmap_count (&iap->ivmap); ++y)
565         {
566           struct interaction_value *iv = iap->ivs[y];
567           for (int x = iap->base_df;
568                x < iap->base_df + df; ++x)
569             {
570               const double bin = categoricals_get_effects_code_for_case (
571                 cat, x, iv->ccase);
572               iap->enc_sum[x - iap->base_df] += bin * iv->cc;
573             }
574           if (cat->payload && cat->payload->calculate)
575             cat->payload->calculate (cat->aux1, cat->aux2, iv->user_data);
576         }
577     }
578
579   cat->sane = true;
580 }
581
582 union value *
583 categoricals_get_var_values (const struct categoricals *cat,
584                              const struct variable *var, size_t *np)
585 {
586   struct variable_node *vn = lookup_variable (&cat->varmap, var);
587   *np = hmap_count (&vn->valmap);
588   if (!vn->values)
589     {
590       vn->values = pool_nalloc (cat->pool, *np, sizeof *vn->values);
591
592       struct value_node *valnd;
593       HMAP_FOR_EACH (valnd, struct value_node, node, &vn->valmap)
594         vn->values[valnd->index] = valnd->val;
595     }
596   return vn->values;
597 }
598
599 static struct interact_params *
600 df_to_iap (const struct categoricals *cat, int subscript)
601 {
602   assert (subscript >= 0);
603   assert (subscript < cat->df_sum);
604
605   return &cat->iap[cat->df_to_iact[subscript]];
606 }
607
608 static struct interact_params *
609 cat_index_to_iap (const struct categoricals *cat, int cat_index)
610 {
611   assert (cat_index >= 0);
612   assert (cat_index < cat->n_cats_total);
613
614   return &cat->iap[cat->cat_to_iact[cat_index]];
615 }
616
617 /* Return the interaction corresponding to SUBSCRIPT. */
618 const struct interaction *
619 categoricals_get_interaction_by_subscript (const struct categoricals *cat,
620                                            int subscript)
621 {
622   return df_to_iap (cat, subscript)->iact;
623 }
624
625 double
626 categoricals_get_weight_by_subscript (const struct categoricals *cat,
627                                       int subscript)
628 {
629   return df_to_iap (cat, subscript)->cc;
630 }
631
632 double
633 categoricals_get_sum_by_subscript (const struct categoricals *cat,
634                                    int subscript)
635 {
636   const struct interact_params *iap = df_to_iap (cat, subscript);
637   return iap->enc_sum[subscript - iap->base_df];
638 }
639
640
641 /* Returns unity if the value in case C at SUBSCRIPT is equal to the category
642    for that subscript */
643 static double
644 categoricals_get_code_for_case (const struct categoricals *cat, int subscript,
645                                 const struct ccase *c,
646                                 bool effects_coding)
647 {
648   const struct interaction *iact
649     = categoricals_get_interaction_by_subscript (cat, subscript);
650
651   const struct interact_params *iap = df_to_iap (cat, subscript);
652
653   double result = 1.0;
654   int dfp = 1;
655   for (int v = 0; v < iact->n_vars; ++v)
656     {
657       const struct variable *var = iact->vars[v];
658
659       const union value *val = case_data (c, var);
660       const int width = var_get_width (var);
661
662       const unsigned int hash = value_hash (val, width, 0);
663       const struct value_node *valn
664         = lookup_value (&iap->varnodes[v]->valmap, val, hash, width);
665
666       const int df = hmap_count (&iap->varnodes[v]->valmap) - 1;
667       const int dfpn = dfp * df;
668
669       if (effects_coding && valn->index == df)
670         result = -result;
671       else
672         {
673           /* Translate subscript into an index for the individual variable. */
674           const int index = ((subscript - iap->base_df) % dfpn) / dfp;
675           if (valn->index != index)
676             return 0.0;
677         }
678       dfp = dfpn;
679     }
680
681   return result;
682 }
683
684
685 /* Returns unity if the value in case C at SUBSCRIPT is equal to the category
686    for that subscript. */
687 double
688 categoricals_get_dummy_code_for_case (const struct categoricals *cat,
689                                       int subscript, const struct ccase *c)
690 {
691   return categoricals_get_code_for_case (cat, subscript, c, false);
692 }
693
694 /* Returns unity if the value in case C at SUBSCRIPT is equal to the category
695    for that subscript.
696    Else if it is the last category, return -1.
697    Otherwise return 0.
698  */
699 double
700 categoricals_get_effects_code_for_case (const struct categoricals *cat,
701                                         int subscript, const struct ccase *c)
702 {
703   return categoricals_get_code_for_case (cat, subscript, c, true);
704 }
705
706 /* Return a case containing the set of values corresponding to
707    the Nth Category of the IACTth interaction */
708 const struct ccase *
709 categoricals_get_case_by_category_real (const struct categoricals *cat,
710                                         int iact, int n)
711 {
712   const struct interact_params *iap = &cat->iap[iact];
713   return n < hmap_count (&iap->ivmap) ? iap->ivs[n]->ccase : NULL;
714 }
715
716 /* Return a the user data corresponding to the Nth Category of the IACTth
717    interaction. */
718 void *
719 categoricals_get_user_data_by_category_real (const struct categoricals *cat,
720                                              int iact, int n)
721 {
722   const struct interact_params *iap = &cat->iap[iact];
723   return n < hmap_count (&iap->ivmap) ? iap->ivs[n]->user_data : NULL;
724 }
725
726 int
727 categoricals_get_value_index_by_category_real (const struct categoricals *cat,
728                                                int iact_idx, int cat_idx,
729                                                int var_idx)
730 {
731   const struct interact_params *iap = &cat->iap[iact_idx];
732   const struct interaction_value *ivn = iap->ivs[cat_idx];
733   const struct variable *var = iap->iact->vars[var_idx];
734   const struct variable_node *vn = iap->varnodes[var_idx];
735   const union value *val = case_data (ivn->ccase, var);
736   int width = var_get_width (var);
737   unsigned int hash = value_hash (val, width, 0);
738   return lookup_value (&vn->valmap, val, hash, width)->index;
739 }
740
741 /* Return a case containing the set of values corresponding to CAT_INDEX. */
742 const struct ccase *
743 categoricals_get_case_by_category (const struct categoricals *cat,
744                                    int cat_index)
745 {
746   const struct interact_params *iap = cat_index_to_iap (cat, cat_index);
747   const struct interaction_value *vn = iap->ivs[cat_index - iap->base_cats];
748   return vn->ccase;
749 }
750
751 void *
752 categoricals_get_user_data_by_category (const struct categoricals *cat,
753                                         int cat_index)
754 {
755   const struct interact_params *iap = cat_index_to_iap (cat, cat_index);
756   const struct interaction_value *iv = iap->ivs[cat_index - iap->base_cats];
757   return iv->user_data;
758 }
759 \f
760 void
761 categoricals_set_payload (struct categoricals *cat, const struct payload *p,
762                           const void *aux1, void *aux2)
763 {
764   cat->payload = p;
765   cat->aux1 = aux1;
766   cat->aux2 = aux2;
767 }