GnmFunc: make this a GObject.
[gnumeric.git] / src / tools / analysis-histogram.c
blob12f1b2d42135ee4998c4700887d5e1d98f6d4a7d
1 /*
2 * analysis-histogram.c:
4 * This is a complete reimplementation of the histogram tool in 2008
6 * Author:
7 * Andreas J. Guelzow <aguelzow@pyrshep.ca>
9 * (C) Copyright 2008 by Andreas J. Guelzow <aguelzow@pyrshep.ca>
12 * This program is free software; you can redistribute it and/or modify
13 * it under the terms of the GNU General Public License as published by
14 * the Free Software Foundation; either version 2 of the License, or
15 * (at your option) any later version.
17 * This program is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU General Public License for more details.
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, see <https://www.gnu.org/licenses/>.
26 #include <gnumeric-config.h>
27 #include <glib/gi18n-lib.h>
28 #include <gnumeric.h>
29 #include <tools/analysis-histogram.h>
30 #include <tools/analysis-tools.h>
31 #include <value.h>
32 #include <ranges.h>
33 #include <expr.h>
34 #include <func.h>
35 #include <numbers.h>
36 #include <sheet-object-graph.h>
37 #include <goffice/goffice.h>
38 #include <sheet.h>
40 static GnmExpr const *
41 make_hist_expr (analysis_tools_data_histogram_t *info,
42 int col, GnmValue *val,
43 gboolean fromminf, gboolean topinf,
44 data_analysis_output_t *dao)
46 GnmExpr const *expr;
47 GnmExpr const *expr_data;
48 GnmExpr const *expr_if_to, *expr_if_from;
49 GnmExprOp from, to;
50 GnmFunc *fd_if = gnm_func_lookup_or_add_placeholder ("IF");
51 GnmFunc *fd_sum = gnm_func_lookup_or_add_placeholder ("SUM");
52 GnmFunc *fd_count = info->percentage ?
53 gnm_func_lookup_or_add_placeholder (info->only_numbers ? "COUNT" : "COUNTA") : NULL;
54 GnmFunc *fd_isnumber = gnm_func_lookup_or_add_placeholder (info->only_numbers ? "ISNUMBER" : "ISBLANK");
55 gint to_col = (info->cumulative) ? 0 : 1;
57 if (info->bin_type & bintype_no_inf_upper) {
58 from = GNM_EXPR_OP_LT;
59 to = GNM_EXPR_OP_GTE;
60 } else {
61 from = GNM_EXPR_OP_LTE;
62 to = GNM_EXPR_OP_GT;
65 expr_data = gnm_expr_new_constant (value_dup (val));
66 if (topinf)
67 expr_if_to = gnm_expr_new_constant (value_new_int (1));
68 else
69 expr_if_to = gnm_expr_new_funcall3
70 (fd_if,
71 gnm_expr_new_binary
72 (gnm_expr_copy (expr_data),
73 to, make_cellref (- (col-to_col), 0)),
74 gnm_expr_new_constant (value_new_int (0)),
75 gnm_expr_new_constant (value_new_int (1)));
77 if (info->cumulative)
78 expr = expr_if_to;
79 else {
80 GnmExpr const *one = gnm_expr_new_constant (value_new_int (1));
81 if (fromminf)
82 expr_if_from = one;
83 else
84 expr_if_from = gnm_expr_new_funcall3
85 (fd_if,
86 gnm_expr_new_binary
87 (gnm_expr_copy (expr_data),
88 from, make_cellref (- col, 0)),
89 gnm_expr_new_constant (value_new_int (0)),
90 one);
91 expr = gnm_expr_new_binary (expr_if_from,
92 GNM_EXPR_OP_MULT,
93 expr_if_to);
96 if (info->only_numbers)
97 expr = gnm_expr_new_binary (expr,
98 GNM_EXPR_OP_MULT,
99 gnm_expr_new_funcall3
100 (fd_if,gnm_expr_new_funcall1
101 (fd_isnumber, gnm_expr_copy (expr_data)),
102 gnm_expr_new_constant (value_new_int (1)),
103 gnm_expr_new_constant (value_new_int (0))));
104 else
105 expr = gnm_expr_new_binary (expr,
106 GNM_EXPR_OP_MULT,
107 gnm_expr_new_funcall3
108 (fd_if,gnm_expr_new_funcall1
109 (fd_isnumber, gnm_expr_copy (expr_data)),
110 gnm_expr_new_constant (value_new_int (0)),
111 gnm_expr_new_constant (value_new_int (1))));
114 expr = gnm_expr_new_funcall1 (fd_sum, expr);
116 if (info->percentage)
117 expr = gnm_expr_new_binary (expr,
118 GNM_EXPR_OP_DIV,
119 gnm_expr_new_funcall1
120 (fd_count,
121 expr_data));
122 else
123 gnm_expr_free (expr_data);
125 return expr;
128 static gboolean
129 analysis_tool_histogram_engine_run (data_analysis_output_t *dao,
130 analysis_tools_data_histogram_t *info)
132 GnmRange range;
133 gint i, i_limit, i_start, i_end, col;
134 GSList *l;
135 gint to_col = (info->cumulative) ? 0 : 1;
137 GnmExpr const *expr_bin = NULL;
139 GnmFunc *fd_small;
140 GnmFunc *fd_index = NULL;
142 char const *format;
144 fd_small = gnm_func_lookup_or_add_placeholder ("SMALL");
145 gnm_func_inc_usage (fd_small);
147 if (info->base.labels) {
148 fd_index = gnm_func_lookup_or_add_placeholder ("INDEX");
149 gnm_func_inc_usage (fd_index);
153 /* General Info */
155 dao_set_italic (dao, 0, 0, 0, 0);
156 dao_set_cell (dao, 0, 0, _("Histogram"));
158 /* Setting up the bins */
160 if (info->predetermined) {
161 range_init_value (&range, info->bin);
162 i_limit = range_height (&range) * range_width (&range);
163 } else {
164 i_limit = info->n;
167 i_end = i_limit;
168 if (info->bin_type & bintype_p_inf_lower)
169 i_end++;
170 if (info->bin_type & bintype_m_inf_lower)
171 i_end++;
172 dao_set_format (dao, to_col, 1, to_col, 1, "\"\";\"\"");
173 format = (info->bin_type & bintype_no_inf_upper) ?
174 /* translator note: only translate the */
175 /* "to below" and "up to" exclusive of */
176 /* the quotation marks: */
177 _("\"to below\" * General") : _("\"up to\" * General");
178 dao_set_format (dao, to_col, 2, to_col, i_end, format);
180 if (info->bin_type & bintype_m_inf_lower) {
181 dao_set_cell_value (dao, to_col, 1, value_new_float (-GNM_MAX));
182 i_start = 2;
183 } else
184 i_start = 1;
186 if (info->predetermined) {
187 expr_bin = gnm_expr_new_constant (info->bin);
188 for (i = 0; i < i_limit; i++)
189 dao_set_cell_expr (dao, to_col, i_start + i,
190 gnm_expr_new_funcall2 (fd_small,
191 gnm_expr_copy (expr_bin),
192 gnm_expr_new_constant
193 (value_new_int (i + 1))));
194 } else {
195 GnmValue *val = value_dup (info->base.input->data);
196 GnmExpr const *expr_min;
197 GnmExpr const *expr_max;
199 if (info->base.labels)
200 switch (info->base.group_by) {
201 case GROUPED_BY_ROW:
202 val->v_range.cell.a.col++;
203 break;
204 default:
205 val->v_range.cell.a.row++;
206 break;
209 if (info->min_given)
210 dao_set_cell_float (dao, to_col, i_start, info->min);
211 else {
212 GnmFunc *fd_min;
214 fd_min = gnm_func_lookup_or_add_placeholder ("MIN");
215 gnm_func_inc_usage (fd_min);
216 dao_set_cell_expr (dao, to_col, i_start,
217 gnm_expr_new_funcall1
218 (fd_min,
219 gnm_expr_new_constant (value_dup (val))));
220 gnm_func_dec_usage (fd_min);
223 if (info->max_given)
224 dao_set_cell_float (dao, to_col, i_start + i_limit - 1, info->max);
225 else {
226 GnmFunc *fd_max;
228 fd_max = gnm_func_lookup_or_add_placeholder ("MAX");
229 gnm_func_inc_usage (fd_max);
230 dao_set_cell_expr (dao, to_col, i_start + i_limit - 1,
231 gnm_expr_new_funcall1
232 (fd_max,
233 gnm_expr_new_constant (value_dup (val))));
234 gnm_func_dec_usage (fd_max);
237 value_release (val);
239 expr_min = dao_get_cellref (dao, to_col, i_start);
240 expr_max = dao_get_cellref (dao, to_col, i_start + i_limit - 1);
242 for (i = 1; i < i_limit - 1; i++)
243 dao_set_cell_expr (dao, to_col, i_start + i,
244 gnm_expr_new_binary (gnm_expr_copy (expr_min),
245 GNM_EXPR_OP_ADD,
246 gnm_expr_new_binary
247 (gnm_expr_new_constant (value_new_int (i)),
248 GNM_EXPR_OP_MULT,
249 gnm_expr_new_binary
250 (gnm_expr_new_binary
251 (gnm_expr_copy (expr_max),
252 GNM_EXPR_OP_SUB,
253 gnm_expr_copy (expr_min)),
254 GNM_EXPR_OP_DIV,
255 gnm_expr_new_constant (value_new_int (info->n - 1))))));
257 gnm_expr_free (expr_min);
258 gnm_expr_free (expr_max);
261 if (info->bin_type & bintype_p_inf_lower) {
262 dao_set_format (dao, to_col, i_end, to_col, i_end,
263 /* translator note: only translate the */
264 /* "to" and "\xe2\x88\x9e" exclusive of */
265 /* the quotation marks: */
266 _("\"to\" * \"\xe2\x88\x9e\""));
267 dao_set_cell_value (dao, to_col, i_end, value_new_float (GNM_MAX));
270 /* format the lower end of the bins */
272 if (!info->cumulative) {
273 GnmExpr const *expr_cr = make_cellref (1,-1);
275 format = (info->bin_type & bintype_no_inf_upper) ?
276 /* translator note: only translate the */
277 /* "from" and "above" exclusive of */
278 /* the quotation marks: */
279 _("\"from\" * General") : _("\"above\" * General");
280 dao_set_format (dao, 0, 2, 0, i_end, format);
281 if (info->bin_type & bintype_m_inf_lower)
282 dao_set_format (dao, 0, 2, 0, 2,
283 /* translator note: only translate the */
284 /* "from" and "\xe2\x88\x92\xe2\x88\x9e" exclusive of */
285 /* the quotation marks: */
286 _("\"from\" * \"\xe2\x88\x92\xe2\x88\x9e\";"
287 "\"from\" * \"\xe2\x88\x92\xe2\x88\x9e\""));
288 for (i = 2; i <= i_end; i++)
289 dao_set_cell_expr (dao, 0, i, gnm_expr_copy (expr_cr));
291 gnm_expr_free (expr_cr);
294 /* insert formulas for histogram values */
296 for (l = info->base.input, col = to_col + 1; l; col++, l = l->next) {
297 GnmValue *val = l->data;
298 GnmValue *val_c = NULL;
300 dao_set_italic (dao, col, 1, col, 1);
301 if (info->base.labels) {
302 val_c = value_dup (val);
303 switch (info->base.group_by) {
304 case GROUPED_BY_ROW:
305 val->v_range.cell.a.col++;
306 break;
307 default:
308 val->v_range.cell.a.row++;
309 break;
311 dao_set_cell_expr (dao, col, 1,
312 gnm_expr_new_funcall1 (fd_index,
313 gnm_expr_new_constant (val_c)));
314 } else {
315 char const *format;
317 switch (info->base.group_by) {
318 case GROUPED_BY_ROW:
319 format = _("Row %d");
320 break;
321 case GROUPED_BY_COL:
322 format = _("Column %d");
323 break;
324 default:
325 format = _("Area %d");
326 break;
328 dao_set_cell_printf (dao, col, 1, format, col - to_col);
331 if (info->percentage)
332 dao_set_format (dao, col, 2, col, i_end, "0.0%");
334 for (i = 2; i <= i_end; i++) {
335 gboolean fromminf = (i == 2) &&
336 (info->bin_type & bintype_m_inf_lower);
337 gboolean topinf = (i == i_end) &&
338 (info->bin_type & bintype_p_inf_lower);
339 dao_set_cell_array_expr
340 (dao, col, i,
341 make_hist_expr (info, col, val,
342 fromminf, topinf, dao));
347 if (expr_bin != NULL)
348 gnm_expr_free (expr_bin);
350 gnm_func_dec_usage (fd_small);
351 if (fd_index != NULL)
352 gnm_func_dec_usage (fd_index);
354 /* Create Chart if requested */
355 if (info->chart != NO_CHART) {
356 SheetObject *so;
357 GogGraph *graph;
358 GogChart *chart;
359 GogPlot *plot;
360 GogSeries *series;
361 gint limits_start, limits_end, values_start, values_end;
362 GOData *limits;
363 GOData *values;
364 int ct;
366 graph = g_object_new (GOG_TYPE_GRAPH, NULL);
367 chart = GOG_CHART (gog_object_add_by_name (
368 GOG_OBJECT (graph), "Chart", NULL));
370 if (info->chart == HISTOGRAM_CHART) {
371 plot = gog_plot_new_by_name ("GogHistogramPlot");
372 limits_start = i_start;
373 limits_end = i_start + i_limit - 1;
374 values_start = i_start + 1;
375 values_end = i_start + i_limit - 1;
376 } else {
377 plot = gog_plot_new_by_name ("GogBarColPlot");
378 limits_start = 2;
379 limits_end = i_end;
380 values_start = 2;
381 values_end = i_end;
382 if (info->chart == BAR_CHART)
383 go_object_toggle (plot, "horizontal");
386 gog_object_add_by_name (GOG_OBJECT (chart),
387 "Plot", GOG_OBJECT (plot));
389 limits = dao_go_data_vector (dao, to_col, limits_start,
390 to_col, limits_end);
392 for (ct = 1; ct < (col - to_col); ct ++) {
393 g_object_ref (limits);
394 values = dao_go_data_vector (dao, to_col + ct, values_start,
395 to_col + ct, values_end);
397 series = gog_plot_new_series (plot);
398 gog_series_set_dim (series, 0, limits, NULL);
399 gog_series_set_dim (series, 1, values, NULL);
401 g_object_unref (limits);
403 if (info->chart == HISTOGRAM_CHART) {
404 GogObject *axis;
405 axis = gog_object_get_child_by_name (GOG_OBJECT (chart), "X-Axis");
406 go_object_set_property (G_OBJECT (axis), "assigned-format-string-XL",
407 "X-Axis Format", "0.0EE0",
408 NULL, NULL);
413 so = sheet_object_graph_new (graph);
414 g_object_unref (graph);
416 dao_set_sheet_object (dao, 0, 1, so);
419 dao_redraw_respan (dao);
421 return FALSE;
425 static gint
426 calc_length (GnmValue *bin)
428 g_return_val_if_fail (bin != NULL, 0);
429 g_return_val_if_fail (VALUE_IS_CELLRANGE (bin), 0);
431 return ((bin->v_range.cell.b.col - bin->v_range.cell.a.col + 1) *
432 (bin->v_range.cell.b.row - bin->v_range.cell.a.row + 1));
435 gboolean
436 analysis_tool_histogram_engine (G_GNUC_UNUSED GOCmdContext *gcc, data_analysis_output_t *dao, gpointer specs,
437 analysis_tool_engine_t selector, gpointer result)
439 analysis_tools_data_histogram_t *info = specs;
441 switch (selector) {
442 case TOOL_ENGINE_UPDATE_DESCRIPTOR:
443 return (dao_command_descriptor (dao, _("Histogram (%s)"), result)
444 == NULL);
445 case TOOL_ENGINE_UPDATE_DAO:
447 int i, j;
449 prepare_input_range (&info->base.input, info->base.group_by);
451 i = 1 + ((info->predetermined) ? calc_length (info->bin) : info->n);
452 if (info->bin_type & bintype_p_inf_lower)
453 i++;
454 if (info->bin_type & bintype_m_inf_lower)
455 i++;
457 j = g_slist_length (info->base.input) + ((info->cumulative) ? 1 : 2);
459 dao_adjust (dao, j, i);
461 return FALSE;
463 case TOOL_ENGINE_CLEAN_UP:
464 return analysis_tool_generic_clean (specs);
465 case TOOL_ENGINE_LAST_VALIDITY_CHECK:
466 return FALSE;
467 case TOOL_ENGINE_PREPARE_OUTPUT_RANGE:
468 dao_prepare_output (NULL, dao, _("Histogram"));
469 return FALSE;
470 case TOOL_ENGINE_FORMAT_OUTPUT_RANGE:
471 return dao_format_output (dao, _("Histogram"));
472 case TOOL_ENGINE_PERFORM_CALC:
473 default:
474 return analysis_tool_histogram_engine_run (dao, specs);
476 return TRUE;