Barchart.c: Change type of flag from int to bool
[pspp] / src / output / charts / barchart.c
index 13ea3dc2f854346a208681b09c0a8870b94e2b46..fea50b787e2fe6315bf35243567d09a43ff0d8c6 100644 (file)
-/* PSPP - computes sample statistics.
-   Copyright (C) 2004 Free Software Foundation, Inc.
+/* PSPP - a program for statistical analysis.
+   Copyright (C) 2015 Free Software Foundation, Inc.
 
-   This program is free software; you can redistribute it and/or
-   modify it under the terms of the GNU General Public License as
-   published by the Free Software Foundation; either version 2 of the
-   License, or (at your option) any later version.
+   This program is free software: you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation, either version 3 of the License, or
+   (at your option) any later version.
 
-   This program is distributed in the hope that it will be useful, but
-   WITHOUT ANY WARRANTY; without even the implied warranty of
-   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-   General Public License for more details.
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
 
    You should have received a copy of the GNU General Public License
-   along with this program; if not, write to the Free Software
-   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
-   02110-1301, USA. */
+   along with this program.  If not, see <http://www.gnu.org/licenses/>. */
 
+#include <config.h>
 
-#include <stdio.h>
-#include <plot.h>
-#include <stdarg.h>
-#include <math.h>
-#include <output/charts/barchart.h>
-#include <output/chart.h>
-#include <output/charts/plot-chart.h>
+#include "output/charts/barchart.h"
+#include "output/charts/piechart.h"
 
-#define CATAGORIES 6
-#define SUB_CATAGORIES 3
+#include <stdlib.h>
 
-static const    double x_min = 0;
-static const    double x_max = 15.0;
+#include "libpspp/cast.h"
+#include "libpspp/str.h"
+#include "libpspp/array.h"
+#include "output/chart-item-provider.h"
 
-static const char *cat_labels[] = 
-  {
-    "Age",
-    "Intelligence",
-    "Wealth",
-    "Emotional",
-    "cat 5",
-    "cat 6",
-    "cat 7",
-    "cat 8",
-    "cat 9",
-    "cat 10",
-    "cat 11"
-  };
+#include "gl/xalloc.h"
+#include "data/variable.h"
+#include "language/stats/freq.h"
 
 
+static int
+compare_category_3way (const void *a_, const void *b_, const void *bc_)
+{
+  const struct category *const*a = a_;
+  const struct category *const*b = b_;
+  const struct barchart *bc = bc_;
 
+  return value_compare_3way (&(*a)->val, &(*b)->val, var_get_width (bc->var[1]));
+}
 
-/* Subcatagories */
-static const double data1[] =
-{
-  28,83,
-  34,
-  29,13,
-   9,4,
-   3,3,
-   2,0, 
-   1,0,
-   0,
-   1,1
-};
-
-
-static const double data2[] =
+
+static unsigned int
+hash_freq_2level_ptr (const void *a_, const void *bc_)
 {
-  45,13,
-   9,4,
-   3,43,
-   2,0, 
-   1,20,
-   0,0,
-  1,1,
-  0,0
-};
-
-static const double data3[] =
-  {
-    23,18,
-    0, 45,23, 9, 40, 24,4, 8
-  };
+  const struct freq *const *ap = a_;
+  const struct barchart *bc = bc_;
 
+  size_t hash = value_hash (&(*ap)->values[0], bc->widths[0], 0);
 
-static const char subcat_name[]="Gender";
+  if (bc->n_vars > 1)
+    hash = value_hash (&(*ap)->values[1], bc->widths[1], hash);
 
+  return hash;
+}
 
-struct subcat {
-  const double *data;
-  const char *label;
-};
 
-static const struct subcat sub_catagory[SUB_CATAGORIES] = 
-  {
-    {data1, "male"},
-    {data2, "female"},
-    {data3, "47xxy"} 
-  };
+static int
+compare_freq_2level_ptr_3way (const void *a_, const void *b_, const void *bc_)
+{
+  const struct freq *const *ap = a_;
+  const struct freq *const *bp = b_;
+  const struct barchart *bc = bc_;
 
+  const int level0 = value_compare_3way (&(*ap)->values[0], &(*bp)->values[0], bc->widths[0]);
 
+  if (level0 == 0 && bc->n_vars > 1)
+    return value_compare_3way (&(*ap)->values[1], &(*bp)->values[1], bc->widths[1]);
 
-static const    double y_min = 0;
-static const    double y_max = 120.0;
-static const    double y_tick = 20.0;
+  return level0;
+}
 
 
 
-static void write_legend(struct chart *chart) ;
+/* Creates and returns a chart that will render a barchart with
+   the given TITLE and the N_CATS described in CATS. 
 
+   VAR is an array containing the categorical variables, and N_VAR 
+   the number of them. N_VAR must be exactly 1 or 2.
 
-void
-draw_barchart(struct chart *ch, const char *title, 
-             const char *xlabel, const char *ylabel, enum bar_opts opt)
+   CATS are the counts of the values of those variables. N_CATS is the
+   number of distinct values.
+*/
+struct chart_item *
+barchart_create (const struct variable **var, int n_vars,
+                const char *ylabel, 
+                struct freq *const *cats, int n_cats)
 {
-  double d;
+  struct barchart *bar;
   int i;
 
-  double interval_size = fabs(ch->data_right - ch->data_left) / ( CATAGORIES );
-  
-  double bar_width = interval_size / 1.1 ;
-
-  double ordinate_scale = fabs(ch->data_top -  ch->data_bottom) /
-    fabs(y_max - y_min) ; 
+  const int pidx = 0;
+  const int sidx = 1;
 
-  if ( opt != BAR_STACKED ) 
-      bar_width /= SUB_CATAGORIES;
 
-  /* Move to data bottom-left */
-  pl_move_r(ch->lp, ch->data_left, ch->data_bottom);
+  int width = var_get_width (var[pidx]);
 
-  pl_savestate_r(ch->lp);
-  pl_filltype_r(ch->lp,1);
-
-  /* Draw the data */
-  for (i = 0 ; i < CATAGORIES ; ++i ) 
-    {
-      int sc;
-      double ystart=0.0;
-      double x = i * interval_size;
+  assert (n_vars >= 1);
 
-      pl_savestate_r(ch->lp);
+  bar = xzalloc (sizeof *bar);
+  bar->var = var;
+  bar->n_vars = n_vars;
+  bar->n_nzcats = n_cats;
+  chart_item_init (&bar->chart_item, &barchart_class, var_to_string (var[pidx]));
 
-      draw_tick (ch, TICK_ABSCISSA, x + (interval_size/2 ), 
-                cat_labels[i]);
+  bar->largest = -1;
+  bar->ylabel = strdup (ylabel);
 
-      for(sc = 0 ; sc < SUB_CATAGORIES ; ++sc ) 
+    {
+      int idx = 0;
+      hmap_init (&bar->primaries);
+
+      /* 
+        Iterate the categories and create a hash table of the primary categories.
+        We need to do this to find out how many there are and to cache the labels.
+      */
+      for (i = 0; i < n_cats; i++)
        {
-         
-         pl_savestate_r(ch->lp);
-         pl_fillcolorname_r(ch->lp,data_colour[sc]);
-         
-         switch ( opt )
-           {
-           case BAR_GROUPED:
-             pl_fboxrel_r(ch->lp, 
-                          x + (sc * bar_width ), 0,
-                          x + (sc + 1) * bar_width, 
-                            sub_catagory[sc].data[i] * ordinate_scale );
-             break;
-             
-
-           case BAR_STACKED:
+         const struct freq *src = cats[i];
+         size_t hash = value_hash (&src->values[pidx], width, 0);
 
-             pl_fboxrel_r(ch->lp, 
-                          x, ystart, 
-                          x + bar_width, 
-                          ystart + sub_catagory[sc].data[i] * ordinate_scale );
-
-             ystart +=    sub_catagory[sc].data[i] * ordinate_scale ; 
-
-             break;
+         struct category *foo;
+         int flag = 0;
+         HMAP_FOR_EACH_WITH_HASH (foo, struct category, node, hash, &bar->primaries)
+           {
+             if (value_equal (&foo->val, &src->values[pidx], width))
+               {
+                 flag = 1;
+                 break;
+               }
+           }
 
-           default:
-             break;
+         if (!flag) 
+           {
+             struct category *s = xzalloc (sizeof *s);
+             s->idx = idx++;
+             s->width = var_get_width (var[pidx]);
+             value_init (&s->val, s->width);
+             value_copy (&s->val, &src->values[pidx], s->width);
+             ds_init_empty (&s->label);
+             var_append_value_name (var[pidx], &s->val, &s->label);
+
+             hmap_insert (&bar->primaries, &s->node, hash);
            }
-         pl_restorestate_r(ch->lp);
        }
 
-      pl_restorestate_r(ch->lp);
+      bar->n_pcats = hmap_count (&bar->primaries);
     }
-  pl_restorestate_r(ch->lp);
 
-  for ( d = y_min; d <= y_max ; d += y_tick )
+  if (n_vars > 1)
     {
+      hmap_init (&bar->secondaries);
+      int idx = 0;
+      /* Iterate the categories, and create a hash table of secondary categories */
+      for (i = 0; i < n_cats; i++)
+       {
+         struct freq *src = cats[i];
 
-      draw_tick (ch, TICK_ORDINATE,
-                (d - y_min ) * ordinate_scale, "%g", d);
+         struct category *foo;
+         int flag = 0;
+         size_t hash = value_hash (&src->values[sidx], var_get_width (var[sidx]), 0);
+         HMAP_FOR_EACH_WITH_HASH (foo, struct category, node, hash, &bar->secondaries)
+           {
+             if (value_equal (&foo->val, &src->values[sidx], var_get_width (var[sidx])))
+               {
+                 flag = 1;
+                 break;
+               }
+           }
       
-    }
+         if (!flag) 
+           {
+             struct category *s = xzalloc (sizeof *s);
+             s->idx = idx++;
+             s->width = var_get_width (var[sidx]);
+             value_init (&s->val, s->width);
+             value_copy (&s->val, &src->values[sidx], var_get_width (var[sidx]));
+             ds_init_empty (&s->label);
+             var_append_value_name (var[sidx], &s->val, &s->label);
+
+             hmap_insert (&bar->secondaries, &s->node, hash);
+             bar->ss = xrealloc (bar->ss, idx * sizeof *bar->ss);
+             bar->ss[idx - 1] = s;
+           }
+       }
 
-  /* Write the abscissa label */
-  pl_move_r(ch->lp,ch->data_left, ch->abscissa_top);
-  pl_alabel_r(ch->lp,0,'t',xlabel);
+      int n_category = hmap_count (&bar->secondaries);
 
-  /* Write the ordinate label */
-  pl_savestate_r(ch->lp);
-  pl_move_r(ch->lp,ch->data_bottom, ch->ordinate_right);
-  pl_textangle_r(ch->lp,90);
-  pl_alabel_r(ch->lp,0,0,ylabel);
-  pl_restorestate_r(ch->lp);
+      sort (bar->ss, n_category, sizeof *bar->ss,
+           compare_category_3way, bar);
+    }
+    
 
+  /* Deep copy.  Not necessary for cmd line, but essential for the GUI,
+     since an expose callback will access these structs which may not
+     exist.
+   */
+  bar->cats = xcalloc (n_cats, sizeof *bar->cats);
 
-  chart_write_title(ch, title);
+  bar->widths[0] = var_get_width (bar->var[0]);
+  if (n_vars > 1)
+    bar->widths[1] = var_get_width (bar->var[1]);
 
-  write_legend(ch);
+  {
+    struct hmap level2table;
+    hmap_init (&level2table);
+    int x = 0;
   
-
+    for (i = 0; i < n_cats; i++)
+      {
+       struct freq *c = cats[i];
+
+       struct freq *foo;
+       bool flag = false;
+       size_t hash = hash_freq_2level_ptr (&c, bar);
+       HMAP_FOR_EACH_WITH_HASH (foo, struct freq, node, hash, &level2table)
+         {
+           if (0 == compare_freq_2level_ptr_3way (&foo, &c, bar))
+             {
+               foo->count += c->count;
+               
+               if (foo->count > bar->largest)
+                 bar->largest = foo->count;
+               
+               flag = true;
+               break;
+             }
+         }
+       
+       if (!flag) 
+         {
+           struct freq *aggregated_freq = freq_clone (c, n_vars, bar->widths); 
+           hmap_insert (&level2table, &aggregated_freq->node, hash);
+           
+           if (c->count > bar->largest)
+             bar->largest = aggregated_freq->count;
+           
+           bar->cats[x++] = aggregated_freq;
+         }
+      }
+
+    bar->n_nzcats = hmap_count (&level2table);
+    hmap_destroy (&level2table);
+  }
+
+  sort (bar->cats, bar->n_nzcats, sizeof *bar->cats,
+       compare_freq_2level_ptr_3way, bar);
+
+  return &bar->chart_item;
 }
 
-
-
-
-
 static void
-write_legend(struct chart *chart)
+destroy_cat_map (struct hmap *m)
 {
-  int sc;
+  struct category *foo = NULL;
+  struct category *next = NULL;
+  HMAP_FOR_EACH_SAFE (foo, next, struct category, node, m)
+    {
+      value_destroy (&foo->val, foo->width);
 
-  pl_savestate_r(chart->lp);
+      ds_destroy (&foo->label);
+      free (foo);
+    }
 
-  pl_filltype_r(chart->lp,1);
+  hmap_destroy (m);
+}
 
-  pl_move_r(chart->lp, chart->legend_left, 
-           chart->data_bottom + chart->font_size * SUB_CATAGORIES * 1.5);
+static void
+barchart_destroy (struct chart_item *chart_item)
+{
+  struct barchart *bar = to_barchart (chart_item);
 
-  pl_alabel_r(chart->lp,0,'b',subcat_name);
+  int i;
 
-  for (sc = 0 ; sc < SUB_CATAGORIES ; ++sc ) 
+  destroy_cat_map (&bar->primaries);
+  if (bar->ss)
     {
-      pl_fmove_r(chart->lp,
-                chart->legend_left,
-                chart->data_bottom + chart->font_size * sc  * 1.5);
-
-      pl_savestate_r(chart->lp);    
-      pl_fillcolorname_r(chart->lp,data_colour[sc]);
-      pl_fboxrel_r (chart->lp,
-                   0,0,
-                   chart->font_size, chart->font_size);
-      pl_restorestate_r(chart->lp);    
-
-      pl_fmove_r(chart->lp,
-                chart->legend_left + chart->font_size * 1.5,
-                chart->data_bottom + chart->font_size * sc  * 1.5);
-
-      pl_alabel_r(chart->lp,'l','b',sub_catagory[sc].label);
+      destroy_cat_map (&bar->secondaries);
     }
 
-
-  pl_restorestate_r(chart->lp);    
+  for (i = 0; i < bar->n_nzcats; i++)
+    {
+      freq_destroy (bar->cats[i], bar->n_vars, bar->widths);
+    }
+  
+  free (bar->cats);
+  free (bar->ylabel);
+  free (bar->ss);
+  free (bar);
 }
+
+const struct chart_item_class barchart_class =
+  {
+    barchart_destroy
+  };