07a39e818f65193b6ddc6ed4816eab19c3a65dd5
[pspp] / src / output / charts / barchart.c
1 /* PSPP - a program for statistical analysis.
2    Copyright (C) 2015 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 "output/charts/barchart.h"
20 #include "output/charts/piechart.h"
21
22 #include <stdlib.h>
23
24 #include "libpspp/cast.h"
25 #include "libpspp/str.h"
26 #include "libpspp/array.h"
27 #include "output/chart-item-provider.h"
28
29 #include "gl/xalloc.h"
30 #include "data/variable.h"
31 #include "language/stats/freq.h"
32
33
34 static int
35 compare_category_3way (const void *a_, const void *b_, const void *bc_)
36 {
37   const struct category *const*a = a_;
38   const struct category *const*b = b_;
39   const struct barchart *bc = bc_;
40
41   return value_compare_3way (&(*a)->val, &(*b)->val, var_get_width (bc->var[1]));
42 }
43
44
45 static unsigned int
46 hash_freq_2level_ptr (const void *a_, const void *bc_)
47 {
48   const struct freq *const *ap = a_;
49   const struct barchart *bc = bc_;
50
51   size_t hash = value_hash (&(*ap)->values[0], bc->widths[0], 0);
52
53   if (bc->n_vars > 1)
54     hash = value_hash (&(*ap)->values[1], bc->widths[1], hash);
55
56   return hash;
57 }
58
59
60 static int
61 compare_freq_2level_ptr_3way (const void *a_, const void *b_, const void *bc_)
62 {
63   const struct freq *const *ap = a_;
64   const struct freq *const *bp = b_;
65   const struct barchart *bc = bc_;
66
67   const int level0 = value_compare_3way (&(*ap)->values[0], &(*bp)->values[0], bc->widths[0]);
68
69   if (level0 == 0 && bc->n_vars > 1)
70     return value_compare_3way (&(*ap)->values[1], &(*bp)->values[1], bc->widths[1]);
71
72   return level0;
73 }
74
75
76
77 /* Creates and returns a chart that will render a barchart with
78    the given TITLE and the N_CATS described in CATS. 
79
80    VAR is an array containing the categorical variables, and N_VAR 
81    the number of them. N_VAR must be exactly 1 or 2.
82
83    CATS are the counts of the values of those variables. N_CATS is the
84    number of distinct values.
85 */
86 struct chart_item *
87 barchart_create (const struct variable **var, int n_vars,
88                  const char *ylabel, 
89                  struct freq *const *cats, int n_cats)
90 {
91   struct barchart *bar;
92   int i;
93
94   const int pidx = 0;
95   const int sidx = 1;
96
97
98   int width = var_get_width (var[pidx]);
99
100   assert (n_vars >= 1);
101
102   bar = xzalloc (sizeof *bar);
103   bar->var = var;
104   bar->n_vars = n_vars;
105   bar->n_nzcats = n_cats;
106   chart_item_init (&bar->chart_item, &barchart_class, var_to_string (var[pidx]));
107
108   bar->largest = -1;
109   bar->ylabel = strdup (ylabel);
110
111     {
112       int idx = 0;
113       hmap_init (&bar->primaries);
114
115       /* 
116          Iterate the categories and create a hash table of the primary categories.
117          We need to do this to find out how many there are and to cache the labels.
118       */
119       for (i = 0; i < n_cats; i++)
120         {
121           const struct freq *src = cats[i];
122           size_t hash = value_hash (&src->values[pidx], width, 0);
123
124           struct category *foo;
125           int flag = 0;
126           HMAP_FOR_EACH_WITH_HASH (foo, struct category, node, hash, &bar->primaries)
127             {
128               if (value_equal (&foo->val, &src->values[pidx], width))
129                 {
130                   flag = 1;
131                   break;
132                 }
133             }
134
135           if (!flag) 
136             {
137               struct category *s = xzalloc (sizeof *s);
138               s->idx = idx++;
139               value_init (&s->val, var_get_width (var[pidx]));
140               value_copy (&s->val, &src->values[pidx], var_get_width (var[pidx]));
141               ds_init_empty (&s->label);
142               var_append_value_name (var[pidx], &s->val, &s->label);
143
144               hmap_insert (&bar->primaries, &s->node, hash);
145             }
146         }
147
148       bar->n_pcats = hmap_count (&bar->primaries);
149     }
150
151   if (n_vars > 1)
152     {
153       hmap_init (&bar->secondaries);
154       int idx = 0;
155       /* Iterate the categories, and create a hash table of secondary categories */
156       for (i = 0; i < n_cats; i++)
157         {
158           struct freq *src = cats[i];
159
160           struct category *foo;
161           int flag = 0;
162           size_t hash = value_hash (&src->values[sidx], var_get_width (var[sidx]), 0);
163           HMAP_FOR_EACH_WITH_HASH (foo, struct category, node, hash, &bar->secondaries)
164             {
165               if (value_equal (&foo->val, &src->values[sidx], var_get_width (var[sidx])))
166                 {
167                   flag = 1;
168                   break;
169                 }
170             }
171       
172           if (!flag) 
173             {
174               struct category *s = xzalloc (sizeof *s);
175               s->idx = idx++;
176               value_init (&s->val, var_get_width (var[sidx]));
177               value_copy (&s->val, &src->values[sidx], var_get_width (var[sidx]));
178               ds_init_empty (&s->label);
179               var_append_value_name (var[sidx], &s->val, &s->label);
180
181               hmap_insert (&bar->secondaries, &s->node, hash);
182               bar->ss = xrealloc (bar->ss, idx * sizeof *bar->ss);
183               bar->ss[idx - 1] = s;
184             }
185         }
186
187       int n_category = hmap_count (&bar->secondaries);
188
189       sort (bar->ss, n_category, sizeof *bar->ss,
190             compare_category_3way, bar);
191     }
192     
193
194   /* Deep copy.  Not necessary for cmd line, but essential for the GUI,
195      since an expose callback will access these structs which may not
196      exist.
197    */
198   bar->cats = xcalloc (n_cats, sizeof *bar->cats);
199
200   bar->widths[0] = var_get_width (bar->var[0]);
201   if (n_vars > 1)
202     bar->widths[1] = var_get_width (bar->var[1]);
203
204   {
205     struct hmap level2table;
206     hmap_init (&level2table);
207     int x = 0;
208   
209     for (i = 0; i < n_cats; i++)
210       {
211         struct freq *c = cats[i];
212
213         struct freq *foo;
214         int flag = 0;
215         size_t hash = hash_freq_2level_ptr (&c, bar);
216         HMAP_FOR_EACH_WITH_HASH (foo, struct freq, node, hash, &level2table)
217           {
218             if (0 == compare_freq_2level_ptr_3way (&foo, &c, bar))
219               {
220                 foo->count += c->count;
221                 
222                 if (foo->count > bar->largest)
223                   bar->largest = foo->count;
224                 
225                 flag = 1;
226                 break;
227               }
228           }
229         
230         if (!flag) 
231           {
232             struct freq *aggregated_freq = freq_clone (c, n_vars, bar->widths); 
233             hmap_insert (&level2table, &aggregated_freq->node, hash);
234             
235             if (c->count > bar->largest)
236               bar->largest = aggregated_freq->count;
237             
238             bar->cats[x++] = aggregated_freq;
239           }
240       }
241
242     bar->n_nzcats = hmap_count (&level2table);
243     hmap_destroy (&level2table);
244   }
245
246   sort (bar->cats, bar->n_nzcats, sizeof *bar->cats,
247         compare_freq_2level_ptr_3way, bar);
248
249   return &bar->chart_item;
250 }
251
252 static void
253 destroy_cat_map (struct hmap *m)
254 {
255   struct category *foo = NULL;
256   struct category *next = NULL;
257   HMAP_FOR_EACH_SAFE (foo, next, struct category, node, m)
258     {
259       ds_destroy (&foo->label);
260       free (foo);
261     }
262
263   hmap_destroy (m);
264 }
265
266 static void
267 barchart_destroy (struct chart_item *chart_item)
268 {
269   struct barchart *bar = to_barchart (chart_item);
270
271   int i;
272
273   destroy_cat_map (&bar->primaries);
274   if (bar->ss)
275     {
276       destroy_cat_map (&bar->secondaries);
277     }
278
279   for (i = 0; i < bar->n_nzcats; i++)
280     {
281       freq_destroy (bar->cats[i], bar->n_vars, bar->widths);
282     }
283   
284   free (bar->cats);
285   free (bar->ylabel);
286   free (bar->ss);
287   free (bar);
288 }
289
290 const struct chart_item_class barchart_class =
291   {
292     barchart_destroy
293   };