+static void
+run_barchart (struct graph *cmd, struct casereader *input)
+{
+ struct casegrouper *grouper;
+ struct casereader *group;
+ double ccc = 0.0;
+
+ if (cmd->missing_pw == false)
+ input = casereader_create_filter_missing (input,
+ cmd->dep_vars,
+ cmd->n_dep_vars,
+ cmd->dep_excl,
+ NULL,
+ NULL);
+
+
+ input = sort_execute (input, &cmd->ordering);
+
+ struct freq **cells = NULL;
+ int n_cells = 0;
+
+ struct hmap columns = HMAP_INITIALIZER (columns);
+ assert (cmd->n_by_vars <= 2);
+ for (grouper = casegrouper_create_vars (input, cmd->by_var,
+ cmd->n_by_vars);
+ casegrouper_get_next_group (grouper, &group);
+ casereader_destroy (group))
+ {
+ int v;
+ struct ccase *c = casereader_peek (group, 0);
+
+ /* Deal with missing values in the categorical variables */
+ for (v = 0; v < cmd->n_by_vars; ++v)
+ {
+ if (var_is_value_missing (cmd->by_var[v],
+ case_data (c, cmd->by_var[v]))
+ & cmd->fctr_excl)
+ break;
+ }
+
+ if (v < cmd->n_by_vars)
+ {
+ case_unref (c);
+ continue;
+ }
+
+ cells = xrealloc (cells, sizeof (*cells) * ++n_cells);
+ cells[n_cells - 1] = xzalloc (sizeof (**cells)
+ + sizeof (union value)
+ * (cmd->n_by_vars - 1));
+
+ if (ag_func[cmd->agr].cumulative && n_cells >= 2)
+ cells[n_cells - 1]->count = cells[n_cells - 2]->count;
+ else
+ cells[n_cells - 1]->count = 0;
+ if (ag_func[cmd->agr].pre)
+ cells[n_cells - 1]->count = ag_func[cmd->agr].pre();
+
+ if (cmd->n_by_vars > 1)
+ {
+ const union value *vv = case_data (c, cmd->by_var[1]);
+ const double weight = dict_get_case_weight (cmd->dict, c, NULL);
+ int v1_width = var_get_width (cmd->by_var[1]);
+ size_t hash = value_hash (vv, v1_width, 0);
+
+ struct freq *fcol = NULL;
+ HMAP_FOR_EACH_WITH_HASH (fcol, struct freq, node, hash, &columns)
+ if (value_equal (vv, &fcol->values[0], v1_width))
+ break;
+
+ if (fcol)
+ fcol->count += weight;
+ else
+ {
+ fcol = xzalloc (sizeof *fcol);
+ fcol->count = weight;
+ value_clone (&fcol->values[0], vv, v1_width);
+ hmap_insert (&columns, &fcol->node, hash);
+ }
+ }
+
+ for (v = 0; v < cmd->n_by_vars; ++v)
+ {
+ value_clone (&cells[n_cells - 1]->values[v],
+ case_data (c, cmd->by_var[v]),
+ var_get_width (cmd->by_var[v]));
+ }
+ case_unref (c);
+
+ double cc = 0;
+ for (;(c = casereader_read (group)) != NULL; case_unref (c))
+ {
+ const double weight = dict_get_case_weight (cmd->dict,c,NULL);
+ const double x = (cmd->n_dep_vars > 0)
+ ? case_num (c, cmd->dep_vars[0]) : SYSMIS;
+
+ cc += weight;
+
+ cells[n_cells - 1]->count
+ = ag_func[cmd->agr].calc (cells[n_cells - 1]->count, x, weight);
+ }
+
+ if (ag_func[cmd->agr].post)
+ cells[n_cells - 1]->count
+ = ag_func[cmd->agr].post (cells[n_cells - 1]->count, cc);
+
+ ccc += cc;
+ }
+
+ casegrouper_destroy (grouper);
+
+ for (int i = 0; i < n_cells; ++i)
+ {
+ if (ag_func[cmd->agr].ppost)
+ {
+ struct freq *cell = cells[i];
+ if (cmd->n_by_vars > 1)
+ {
+ const union value *vv = &cell->values[1];
+
+ int v1_width = var_get_width (cmd->by_var[1]);
+ size_t hash = value_hash (vv, v1_width, 0);
+
+ struct freq *fcol = NULL;
+ HMAP_FOR_EACH_WITH_HASH (fcol, struct freq, node, hash, &columns)
+ if (value_equal (vv, &fcol->values[0], v1_width))
+ break;
+
+ cell->count = ag_func[cmd->agr].ppost (cell->count, fcol->count);
+ }
+ else
+ cell->count = ag_func[cmd->agr].ppost (cell->count, ccc);
+ }
+ }
+
+ if (cmd->n_by_vars > 1)
+ {
+ struct freq *col_cell;
+ struct freq *next;
+ HMAP_FOR_EACH_SAFE (col_cell, next, struct freq, node, &columns)
+ {
+
+ value_destroy (col_cell->values, var_get_width (cmd->by_var[1]));
+ free (col_cell);
+ }
+ }
+ hmap_destroy (&columns);
+
+ {
+ struct string label;
+ ds_init_empty (&label);
+
+ if (cmd->n_dep_vars > 0)
+ ds_put_format (&label, _("%s of %s"),
+ ag_func[cmd->agr].description,
+ var_get_name (cmd->dep_vars[0]));
+ else
+ ds_put_cstr (&label,
+ ag_func[cmd->agr].description);
+
+ chart_submit (barchart_create (cmd->by_var, cmd->n_by_vars,
+ ds_cstr (&label), false,
+ cells, n_cells));
+
+ ds_destroy (&label);
+ }
+
+ for (int i = 0; i < n_cells; ++i)
+ free (cells[i]);
+
+ free (cells);
+}
+
+