1.12.42
[gnumeric.git] / src / dialogs / dialog-simulation.c
blob6a188948b374786b25d1e02fd3fba13fb179315d
1 /*
2 * dialog-simulation.c:
4 * Authors:
5 * Jukka-Pekka Iivonen <jiivonen@hutcs.cs.hut.fi>
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, see <https://www.gnu.org/licenses/>.
19 **/
20 #include <gnumeric-config.h>
21 #include <glib/gi18n-lib.h>
22 #include <gnumeric.h>
23 #include <dialogs/dialogs.h>
24 #include <dialogs/help.h>
26 #include <sheet.h>
27 #include <cell.h>
28 #include <ranges.h>
29 #include <gui-util.h>
30 #include <dialogs/tool-dialogs.h>
31 #include <dialogs/dao-gui-utils.h>
32 #include <value.h>
33 #include <wbc-gtk.h>
35 #include <widgets/gnm-expr-entry.h>
36 #include <widgets/gnm-dao.h>
37 #include <tools/simulation.h>
39 #include <string.h>
41 #define SIMULATION_KEY "simulation-dialog"
43 typedef GnmGenericToolState SimulationState;
45 static GtkTextBuffer *results_buffer;
46 static int results_sim_index;
47 static simulation_t *current_sim;
49 /**
50 * simulation_update_sensitivity_cb:
51 * @dummy:
52 * @state:
54 * Update the dialog widgets sensitivity
55 **/
56 static void
57 simulation_update_sensitivity_cb (G_GNUC_UNUSED GtkWidget *dummy,
58 SimulationState *state)
60 GnmValue *input_range = NULL;
61 GnmValue *output_vars = NULL;
63 input_range = gnm_expr_entry_parse_as_value (
64 GNM_EXPR_ENTRY (state->input_entry), state->sheet);
65 if (input_range == NULL) {
66 gtk_label_set_text (GTK_LABEL (state->warning),
67 _("The input variable range is invalid."));
68 gtk_widget_set_sensitive (state->ok_button, FALSE);
69 return;
70 } else
71 value_release (input_range);
73 output_vars = gnm_expr_entry_parse_as_value
74 (state->input_entry_2, state->sheet);
75 if (output_vars == NULL) {
76 gtk_label_set_text (GTK_LABEL (state->warning),
77 _("The output variable range is invalid."));
78 gtk_widget_set_sensitive (state->ok_button, FALSE);
79 return;
80 } else
81 value_release (output_vars);
83 if (!gnm_dao_is_ready (GNM_DAO (state->gdao))) {
84 gtk_label_set_text (GTK_LABEL (state->warning),
85 _("The output range is invalid."));
86 gtk_widget_set_sensitive (state->ok_button, FALSE);
87 return;
90 gtk_label_set_text (GTK_LABEL (state->warning), "");
91 gtk_widget_set_sensitive (state->ok_button, TRUE);
92 return;
95 static gboolean
96 prepare_ranges (simulation_t *sim)
98 int i, n, base_col, base_row;
100 if (!VALUE_IS_CELLRANGE (sim->inputs) ||
101 !VALUE_IS_CELLRANGE (sim->outputs))
102 return TRUE;
104 sim->ref_inputs = gnm_rangeref_dup (value_get_rangeref (sim->inputs));
105 sim->ref_outputs = gnm_rangeref_dup (value_get_rangeref (sim->outputs));
107 sim->n_input_vars =
108 (abs (sim->ref_inputs->a.col - sim->ref_inputs->b.col) + 1) *
109 (abs (sim->ref_inputs->a.row - sim->ref_inputs->b.row) + 1);
110 sim->n_output_vars =
111 (abs (sim->ref_outputs->a.col - sim->ref_outputs->b.col) + 1) *
112 (abs (sim->ref_outputs->a.row - sim->ref_outputs->b.row) + 1);
113 sim->n_vars = sim->n_input_vars + sim->n_output_vars;
115 /* Get the intput cells into a list. */
116 sim->list_inputs = NULL;
117 base_col = MIN (sim->ref_inputs->a.col, sim->ref_inputs->b.col);
118 base_row = MIN (sim->ref_inputs->a.row, sim->ref_inputs->b.row);
119 for (i = base_col;
120 i <= MAX (sim->ref_inputs->a.col, sim->ref_inputs->b.col); i++) {
121 for (n = base_row;
122 n<= MAX (sim->ref_inputs->a.row, sim->ref_inputs->b.row);
123 n++) {
124 GnmCell *cell = sheet_cell_fetch (sim->ref_inputs->a.sheet, i, n);
125 sim->list_inputs = g_slist_append (sim->list_inputs, cell);
129 /* Get the output cells into a list. */
130 sim->list_outputs = NULL;
131 base_col = MIN (sim->ref_outputs->a.col, sim->ref_outputs->b.col);
132 base_row = MIN (sim->ref_outputs->a.row, sim->ref_outputs->b.row);
133 for (i = base_col;
134 i <= MAX (sim->ref_outputs->a.col, sim->ref_outputs->b.col); i++) {
135 for (n = base_row;
136 n<= MAX (sim->ref_outputs->a.row, sim->ref_outputs->b.row);
137 n++) {
138 GnmCell *cell = sheet_cell_fetch (sim->ref_outputs->a.sheet, i, n);
139 sim->list_outputs = g_slist_append (sim->list_outputs, cell);
143 return FALSE;
146 static void
147 update_log (SimulationState *state, simulation_t *sim)
149 char const *txt [6] = {
150 _("Simulations"), _("Iterations"), _("# Input variables"),
151 _("# Output variables"), _("Runtime"), _("Run on")
153 GtkTreeIter iter;
154 GtkListStore *store;
155 GtkTreePath *path;
156 GtkWidget *view;
157 GString *buf;
158 int i;
160 view = go_gtk_builder_get_widget (state->gui, "last-run-view");
162 store = gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_STRING);
164 for (i = 0; i < 6; i++) {
165 buf = g_string_new (NULL);
166 switch (i) {
167 case 0:
168 g_string_append_printf (buf, "%d",
169 sim->last_round -
170 sim->first_round + 1);
171 break;
172 case 1:
173 g_string_append_printf (buf, "%d", sim->n_iterations);
174 break;
175 case 2:
176 g_string_append_printf (buf, "%d", sim->n_input_vars);
177 break;
178 case 3:
179 g_string_append_printf (buf, "%d", sim->n_output_vars);
180 break;
181 case 4:
182 g_string_append_printf (buf, "%.2" GNM_FORMAT_g,
183 sim->end.tv_sec -
184 sim->start.tv_sec +
185 (sim->end.tv_usec -
186 sim->start.tv_usec) /
187 (gnm_float) G_USEC_PER_SEC);
188 break;
189 case 5:
190 dao_append_date (buf);
191 break;
192 default:
193 g_string_append_printf (buf, "Error");
194 break;
197 gtk_list_store_append (store, &iter);
198 gtk_list_store_set (store, &iter, 0, txt [i], 1, buf->str, -1);
199 g_string_free (buf, FALSE);
202 path = gtk_tree_path_new_from_string ("0");
203 if (gtk_tree_model_get_iter (GTK_TREE_MODEL (store), &iter, path)) {
204 ; /* Do something */
205 } else {
206 g_warning ("Did not get a valid iterator");
208 gtk_tree_path_free (path);
210 gtk_tree_view_append_column
211 (GTK_TREE_VIEW (view),
212 gtk_tree_view_column_new_with_attributes
213 (_("Name"),
214 gtk_cell_renderer_text_new (), "text", 0, NULL));
215 gtk_tree_view_append_column
216 (GTK_TREE_VIEW (view),
217 gtk_tree_view_column_new_with_attributes
218 (_("Value"),
219 gtk_cell_renderer_text_new (), "text", 1, NULL));
220 gtk_tree_view_set_model (GTK_TREE_VIEW (view), GTK_TREE_MODEL (store));
221 g_object_unref (store);
224 static void
225 update_results_view (simulation_t *sim)
227 GString *buf;
228 int i;
230 buf = g_string_new (NULL);
232 g_string_append_printf (buf, "Simulation #%d\n\n", results_sim_index + 1);
233 g_string_append_printf (buf, "%-20s %10s %10s %10s\n", _("Variable"),
234 _("Min"), _("Average"), _("Max"));
235 for (i = 0; i < sim->n_vars; i++)
236 g_string_append_printf (buf, "%-20s %10" GNM_FORMAT_g " %10"
237 GNM_FORMAT_G " %10" GNM_FORMAT_g "\n",
238 sim->cellnames [i],
239 sim->stats [results_sim_index]->min [i],
240 sim->stats [results_sim_index]->mean [i],
241 sim->stats [results_sim_index]->max [i]);
243 gtk_text_buffer_set_text (results_buffer, buf->str, strlen (buf->str));
244 g_string_free (buf, FALSE);
247 static void
248 prev_button_cb (G_GNUC_UNUSED GtkWidget *button,
249 SimulationState *state)
251 GtkWidget *w;
253 if (results_sim_index > current_sim->first_round)
254 --results_sim_index;
256 if (results_sim_index == current_sim->first_round) {
257 w = go_gtk_builder_get_widget (state->gui, "prev-button");
258 gtk_widget_set_sensitive (w, FALSE);
261 w = go_gtk_builder_get_widget (state->gui, "next-button");
262 gtk_widget_set_sensitive (w, TRUE);
263 update_results_view (current_sim);
266 static void
267 next_button_cb (G_GNUC_UNUSED GtkWidget *button,
268 SimulationState *state)
270 GtkWidget *w;
272 if (results_sim_index < current_sim->last_round)
273 ++results_sim_index;
275 if (results_sim_index == current_sim->last_round) {
276 w = go_gtk_builder_get_widget (state->gui, "next-button");
277 gtk_widget_set_sensitive (w, FALSE);
280 w = go_gtk_builder_get_widget (state->gui, "prev-button");
281 gtk_widget_set_sensitive (w, TRUE);
282 update_results_view (current_sim);
286 * simulation_ok_clicked_cb:
287 * @button:
288 * @state:
290 * Retrieve the information from the dialog and call the advanced_filter.
291 * Note that we assume that the ok_button is only active if the entry fields
292 * contain sensible data.
294 static void
295 simulation_ok_clicked_cb (G_GNUC_UNUSED GtkWidget *button,
296 SimulationState *state)
298 data_analysis_output_t dao;
299 GtkWidget *w;
300 gchar const *err;
301 static simulation_t sim;
303 simulation_tool_destroy (current_sim);
305 sim.inputs = gnm_expr_entry_parse_as_value
306 (GNM_EXPR_ENTRY (state->input_entry), state->sheet);
308 sim.outputs = gnm_expr_entry_parse_as_value
309 (state->input_entry_2, state->sheet);
311 parse_output ((GnmGenericToolState *) state, &dao);
313 if (prepare_ranges (&sim)) {
314 err = (gchar *) N_("Invalid variable range was given");
315 goto out;
318 w = go_gtk_builder_get_widget (state->gui, "iterations");
319 sim.n_iterations = gtk_spin_button_get_value (GTK_SPIN_BUTTON (w));
321 w = go_gtk_builder_get_widget (state->gui, "first_round");
322 sim.first_round = gtk_spin_button_get_value (GTK_SPIN_BUTTON (w)) - 1;
324 w = go_gtk_builder_get_widget (state->gui, "last_round");
325 sim.last_round = gtk_spin_button_get_value (GTK_SPIN_BUTTON (w)) - 1;
327 if (sim.first_round > sim.last_round) {
328 err = (gchar *) N_("First round number should be less than or "
329 "equal to the number of the last round."
331 goto out;
334 current_sim = &sim;
336 w = go_gtk_builder_get_widget (state->gui, "max-time");
337 sim.max_time = gtk_spin_button_get_value (GTK_SPIN_BUTTON (w)) - 1;
339 g_get_current_time (&sim.start);
340 err = simulation_tool (GNM_WBC (state->wbcg),
341 &dao, &sim);
342 g_get_current_time (&sim.end);
344 if (err == NULL) {
345 results_sim_index = sim.first_round;
346 update_log (state, &sim);
347 update_results_view (&sim);
349 if (sim.last_round > results_sim_index) {
350 w = go_gtk_builder_get_widget (state->gui, "next-button");
351 gtk_widget_set_sensitive (w, TRUE);
354 out:
355 value_release (sim.inputs);
356 value_release (sim.outputs);
358 if (err != NULL)
359 error_in_entry ((GnmGenericToolState *) state,
360 GTK_WIDGET (state->input_entry_2), _(err));
361 return;
366 * cb_tool_close_clicked:
367 * @button:
368 * @state:
370 * Close (destroy) the dialog
372 static void
373 cb_tool_cancel_clicked (G_GNUC_UNUSED GtkWidget *button,
374 GnmGenericToolState *state)
376 simulation_tool_destroy (current_sim);
377 gtk_widget_destroy (state->dialog);
380 static void
381 init_results_view (SimulationState *state)
383 GtkTextView *view;
384 GtkTextTagTable *tag_table;
386 tag_table = gtk_text_tag_table_new ();
387 results_buffer = gtk_text_buffer_new (tag_table);
388 view = GTK_TEXT_VIEW (go_gtk_builder_get_widget (state->gui,
389 "results-view"));
390 gtk_text_view_set_buffer (view, results_buffer);
394 * dialog_simulation:
395 * @wbcg:
396 * @sheet:
398 * Show the dialog (guru).
401 void
402 dialog_simulation (WBCGtk *wbcg, G_GNUC_UNUSED Sheet *sheet)
404 SimulationState *state;
405 WorkbookControl *wbc;
406 GtkWidget *w;
408 g_return_if_fail (wbcg != NULL);
410 wbc = GNM_WBC (wbcg);
412 /* Only pop up one copy per workbook */
413 if (gnm_dialog_raise_if_exists (wbcg, SIMULATION_KEY))
414 return;
416 state = g_new (SimulationState, 1);
417 if (dialog_tool_init (state, wbcg, wb_control_cur_sheet (wbc),
418 GNUMERIC_HELP_LINK_SIMULATION,
419 "res:ui/simulation.ui", "Simulation",
420 _("Could not create the Simulation dialog."),
421 SIMULATION_KEY,
422 G_CALLBACK (simulation_ok_clicked_cb),
423 G_CALLBACK (cb_tool_cancel_clicked),
424 G_CALLBACK (simulation_update_sensitivity_cb),
426 return;
428 init_results_view (state);
429 current_sim = NULL;
431 w = go_gtk_builder_get_widget (state->gui, "prev-button");
432 gtk_widget_set_sensitive (w, FALSE);
433 g_signal_connect_after (G_OBJECT (w), "clicked",
434 G_CALLBACK (prev_button_cb), state);
435 w = go_gtk_builder_get_widget (state->gui, "next-button");
436 g_signal_connect_after (G_OBJECT (w), "clicked",
437 G_CALLBACK (next_button_cb), state);
438 gtk_widget_set_sensitive (w, FALSE);
439 w = go_gtk_builder_get_widget (state->gui, "min-button");
440 gtk_widget_set_sensitive (w, FALSE);
441 gtk_widget_hide (w);
442 w = go_gtk_builder_get_widget (state->gui, "max-button");
443 gtk_widget_set_sensitive (w, FALSE);
444 gtk_widget_hide (w);
446 gnm_dialog_setup_destroy_handlers (GTK_DIALOG (state->dialog), wbcg,
447 GNM_DIALOG_DESTROY_SHEET_REMOVED);
449 gnm_dao_set_put (GNM_DAO (state->gdao), FALSE, FALSE);
450 simulation_update_sensitivity_cb (NULL, state);
451 tool_load_selection ((GnmGenericToolState *)state, TRUE);
452 return;