Barchart.c: Change type of flag from int to bool
[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               s->width = var_get_width (var[pidx]);
140               value_init (&s->val, s->width);
141               value_copy (&s->val, &src->values[pidx], s->width);
142               ds_init_empty (&s->label);
143               var_append_value_name (var[pidx], &s->val, &s->label);
144
145               hmap_insert (&bar->primaries, &s->node, hash);
146             }
147         }
148
149       bar->n_pcats = hmap_count (&bar->primaries);
150     }
151
152   if (n_vars > 1)
153     {
154       hmap_init (&bar->secondaries);
155       int idx = 0;
156       /* Iterate the categories, and create a hash table of secondary categories */
157       for (i = 0; i < n_cats; i++)
158         {
159           struct freq *src = cats[i];
160
161           struct category *foo;
162           int flag = 0;
163           size_t hash = value_hash (&src->values[sidx], var_get_width (var[sidx]), 0);
164           HMAP_FOR_EACH_WITH_HASH (foo, struct category, node, hash, &bar->secondaries)
165             {
166               if (value_equal (&foo->val, &src->values[sidx], var_get_width (var[sidx])))
167                 {
168                   flag = 1;
169                   break;
170                 }
171             }
172       
173           if (!flag) 
174             {
175               struct category *s = xzalloc (sizeof *s);
176               s->idx = idx++;
177               s->width = var_get_width (var[sidx]);
178               value_init (&s->val, s->width);
179               value_copy (&s->val, &src->values[sidx], var_get_width (var[sidx]));
180               ds_init_empty (&s->label);
181               var_append_value_name (var[sidx], &s->val, &s->label);
182
183               hmap_insert (&bar->secondaries, &s->node, hash);
184               bar->ss = xrealloc (bar->ss, idx * sizeof *bar->ss);
185               bar->ss[idx - 1] = s;
186             }
187         }
188
189       int n_category = hmap_count (&bar->secondaries);
190
191       sort (bar->ss, n_category, sizeof *bar->ss,
192             compare_category_3way, bar);
193     }
194     
195
196   /* Deep copy.  Not necessary for cmd line, but essential for the GUI,
197      since an expose callback will access these structs which may not
198      exist.
199    */
200   bar->cats = xcalloc (n_cats, sizeof *bar->cats);
201
202   bar->widths[0] = var_get_width (bar->var[0]);
203   if (n_vars > 1)
204     bar->widths[1] = var_get_width (bar->var[1]);
205
206   {
207     struct hmap level2table;
208     hmap_init (&level2table);
209     int x = 0;
210   
211     for (i = 0; i < n_cats; i++)
212       {
213         struct freq *c = cats[i];
214
215         struct freq *foo;
216         bool flag = false;
217         size_t hash = hash_freq_2level_ptr (&c, bar);
218         HMAP_FOR_EACH_WITH_HASH (foo, struct freq, node, hash, &level2table)
219           {
220             if (0 == compare_freq_2level_ptr_3way (&foo, &c, bar))
221               {
222                 foo->count += c->count;
223                 
224                 if (foo->count > bar->largest)
225                   bar->largest = foo->count;
226                 
227                 flag = true;
228                 break;
229               }
230           }
231         
232         if (!flag) 
233           {
234             struct freq *aggregated_freq = freq_clone (c, n_vars, bar->widths); 
235             hmap_insert (&level2table, &aggregated_freq->node, hash);
236             
237             if (c->count > bar->largest)
238               bar->largest = aggregated_freq->count;
239             
240             bar->cats[x++] = aggregated_freq;
241           }
242       }
243
244     bar->n_nzcats = hmap_count (&level2table);
245     hmap_destroy (&level2table);
246   }
247
248   sort (bar->cats, bar->n_nzcats, sizeof *bar->cats,
249         compare_freq_2level_ptr_3way, bar);
250
251   return &bar->chart_item;
252 }
253
254 static void
255 destroy_cat_map (struct hmap *m)
256 {
257   struct category *foo = NULL;
258   struct category *next = NULL;
259   HMAP_FOR_EACH_SAFE (foo, next, struct category, node, m)
260     {
261       value_destroy (&foo->val, foo->width);
262
263       ds_destroy (&foo->label);
264       free (foo);
265     }
266
267   hmap_destroy (m);
268 }
269
270 static void
271 barchart_destroy (struct chart_item *chart_item)
272 {
273   struct barchart *bar = to_barchart (chart_item);
274
275   int i;
276
277   destroy_cat_map (&bar->primaries);
278   if (bar->ss)
279     {
280       destroy_cat_map (&bar->secondaries);
281     }
282
283   for (i = 0; i < bar->n_nzcats; i++)
284     {
285       freq_destroy (bar->cats[i], bar->n_vars, bar->widths);
286     }
287   
288   free (bar->cats);
289   free (bar->ylabel);
290   free (bar->ss);
291   free (bar);
292 }
293
294 const struct chart_item_class barchart_class =
295   {
296     barchart_destroy
297   };