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