Commit a14bd8de authored by Andreas J. Guelzow's avatar Andreas J. Guelzow Committed by Andreas J. Guelzow

make undoable

2002-04-05  Andreas J. Guelzow <aguelzow@taliesin.ca>

	* dialog-random-generator.c : make undoable

2002-04-05  Andreas J. Guelzow <aguelzow@taliesin.ca>

	* random-generator.[ch] : make undoable
parent 3575f4a1
......@@ -53,7 +53,6 @@ Long term breakage
- Duplicate sheet.
- Remove sheet. This raises some serious design issues.
- Freeze panes.
- Random Generator Tool.
- Goal seek.
- Tabulate.
......
......@@ -9,6 +9,7 @@ Andreas:
* Plug leaks.
* Add preference dialog.
* Improve treeview cell renderers.
* Make Random Generator Tool undoable.
Jody:
* Add password dialog to support encrypted input.
......
2002-04-05 Andreas J. Guelzow <aguelzow@taliesin.ca>
* dialog-random-generator.c : make undoable
2002-04-03 Andreas J. Guelzow <aguelzow@taliesin.ca>
* dialog-cell-sort.c : use setup_range_from_value
......
......@@ -362,92 +362,74 @@ dialog_random_realized (GtkWidget *widget, RandomToolState *state)
static void
random_tool_ok_clicked_cb (GtkWidget *button, RandomToolState *state)
{
data_analysis_output_t *dao;
tools_data_random_t *data;
data_analysis_output_t dao;
char *text;
gint vars, count, err;
random_tool_t param;
gint err;
if (state->base.warning_dialog != NULL)
gtk_widget_destroy (state->base.warning_dialog);
data = g_new0 (tools_data_random_t, 1);
dao = parse_output ((GenericToolState *)state, NULL);
parse_output ((GenericToolState *)state, &dao);
data->wbcg = state->base.wbcg;
err = entry_to_int (GTK_ENTRY (state->vars_entry), &vars, FALSE);
err = entry_to_int (GTK_ENTRY (state->count_entry), &count, FALSE);
err = entry_to_int (GTK_ENTRY (state->vars_entry), &data->n_vars, FALSE);
err = entry_to_int (GTK_ENTRY (state->count_entry), &data->count, FALSE);
state->distribution = combo_get_distribution (state->distribution_combo);
data->distribution = state->distribution =
combo_get_distribution (state->distribution_combo);
switch (state->distribution) {
case NormalDistribution:
err = entry_to_float (GTK_ENTRY (state->par1_entry), &param.normal.mean, TRUE);
err = entry_to_float (GTK_ENTRY (state->par2_entry), &param.normal.stdev, TRUE);
err = entry_to_float (GTK_ENTRY (state->par1_entry),
&data->param.normal.mean, TRUE);
err = entry_to_float (GTK_ENTRY (state->par2_entry),
&data->param.normal.stdev, TRUE);
break;
case BernoulliDistribution:
err = entry_to_float (GTK_ENTRY (state->par1_entry), &param.bernoulli.p, TRUE);
err = entry_to_float (GTK_ENTRY (state->par1_entry),
&data->param.bernoulli.p, TRUE);
break;
case PoissonDistribution:
err = entry_to_float (GTK_ENTRY (state->par1_entry), &param.poisson.lambda, TRUE);
err = entry_to_float (GTK_ENTRY (state->par1_entry),
&data->param.poisson.lambda, TRUE);
break;
case ExponentialDistribution:
err = entry_to_float (GTK_ENTRY (state->par1_entry), &param.exponential.b, TRUE);
err = entry_to_float (GTK_ENTRY (state->par1_entry),
&data->param.exponential.b, TRUE);
break;
case BinomialDistribution:
err = entry_to_float (GTK_ENTRY (state->par1_entry), &param.binomial.p, TRUE);
err = entry_to_int (GTK_ENTRY (state->par2_entry), &param.binomial.trials, TRUE);
err = entry_to_float (GTK_ENTRY (state->par1_entry),
&data->param.binomial.p, TRUE);
err = entry_to_int (GTK_ENTRY (state->par2_entry),
&data->param.binomial.trials, TRUE);
break;
case NegativeBinomialDistribution:
err = entry_to_float (GTK_ENTRY (state->par1_entry), &param.negbinom.p, TRUE);
err = entry_to_int (GTK_ENTRY (state->par2_entry), &param.negbinom.f, TRUE);
err = entry_to_float (GTK_ENTRY (state->par1_entry),
&data->param.negbinom.p, TRUE);
err = entry_to_int (GTK_ENTRY (state->par2_entry),
&data->param.negbinom.f, TRUE);
break;
case DiscreteDistribution:
param.discrete.range = gnm_expr_entry_parse_as_value (
data->param.discrete.range = gnm_expr_entry_parse_as_value (
GNUMERIC_EXPR_ENTRY (state->par1_expr_entry), state->base.sheet);
break;
case UniformDistribution:
default:
err = entry_to_float (GTK_ENTRY (state->par1_entry),
&param.uniform.lower_limit, TRUE);
&data->param.uniform.lower_limit, TRUE);
err = entry_to_float (GTK_ENTRY (state->par2_entry),
&param.uniform.upper_limit, TRUE);
&data->param.uniform.upper_limit, TRUE);
break;
}
err = random_tool (WORKBOOK_CONTROL (state->base.wbcg), state->base.sheet,
vars, count, state->distribution, &param,&dao);
switch (err) {
case 0:
if (button == state->base.ok_button) {
if (state->distribution_accel) {
gtk_window_remove_accel_group (GTK_WINDOW (state->base.dialog),
state->distribution_accel);
state->distribution_accel = NULL;
}
gtk_widget_destroy (state->base.dialog);
if (!cmd_analysis_tool (WORKBOOK_CONTROL (state->base.wbcg), state->base.sheet,
dao, data, tool_random_engine) &&
(button == state->base.ok_button)) {
if (state->distribution_accel) {
gtk_window_remove_accel_group (GTK_WINDOW (state->base.dialog),
state->distribution_accel);
state->distribution_accel = NULL;
}
break;
case 1: /* non-numeric probability (DiscreteDistribution) */
error_in_entry ((GenericToolState *) state, GTK_WIDGET (state->par1_expr_entry),
_("The probability input range contains a non-numeric value.\n"
"All probabilities must be non-negative numbers."));
break;
case 2: /* probabilities are all zero (DiscreteDistribution) */
error_in_entry ((GenericToolState *) state, GTK_WIDGET (state->par1_expr_entry),
_("The probabilities may not all be 0!"));
break;
case 3: /* negative probability (DiscreteDistribution) */
error_in_entry ((GenericToolState *) state, GTK_WIDGET (state->par1_expr_entry),
_("The probability input range contains a negative number.\n"
"All probabilities must be non-negative!"));
break;
case 4: /* value is empty (DiscreteDistribution) */
error_in_entry ((GenericToolState *) state, GTK_WIDGET (state->par1_expr_entry),
_("None of the values in the value range may be empty!"));
break;
default:
text = g_strdup_printf (_("An unexpected error has occurred: %d."), err);
gnumeric_notice (state->base.wbcg, GTK_MESSAGE_ERROR, text);
g_free (text);
break;
gtk_widget_destroy (state->base.dialog);
}
return;
}
......
2002-04-05 Andreas J. Guelzow <aguelzow@taliesin.ca>
* random-generator.[ch] : make undoable
2002-03-31 Andreas J. Guelzow <aguelzow@taliesin.ca>
* analysis-tools.c : adjust includes
......
......@@ -6,8 +6,8 @@
* Andreas J. Guelzow <aguelzow@taliesin.ca>
*
* (C) Copyright 2000, 2001 by Jukka-Pekka Iivonen <jiivonen@hutcs.cs.hut.fi>
* (C) Copyright 2002 by Andreas J. Guelzow <aguelzow@taliesin.ca>
*
* Modified 2001 to use range_* functions of mathfunc.h
*
* 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
......@@ -40,6 +40,7 @@
#include "sheet-style.h"
#include "workbook.h"
#include "format.h"
#include "gui-util.h"
#include "sheet-object-cell-comment.h"
#include <libgnome/gnome-i18n.h>
......@@ -55,194 +56,294 @@
*
**/
int
random_tool (WorkbookControl *wbc, Sheet *sheet, int vars, int count,
random_distribution_t distribution,
random_tool_t *param, data_analysis_output_t *dao)
typedef struct {
gint n;
Value **values;
gnum_float *cumul_p;
} discrete_random_tool_local_t;
static void
tool_random_engine_run_discrete_clear_continuity (discrete_random_tool_local_t **continuity)
{
if (distribution != DiscreteDistribution)
dao_prepare_output (wbc, dao, _("Random"));
switch (distribution) {
case DiscreteDistribution: {
Value *range = param->discrete.range;
int n = range->v_range.cell.b.row - range->v_range.cell.a.row + 1;
gnum_float *prob = g_new (gnum_float, n);
gnum_float *cumul_p = g_new (gnum_float, n);
Value **values = g_new0 (Value *, n);
gnum_float cumprob = 0;
int j = 0;
int i;
int err = 0;
for (i = range->v_range.cell.a.row;
i <= range->v_range.cell.b.row;
i++, j++) {
Value *v;
gnum_float thisprob;
Cell *cell = sheet_cell_get (range->v_range.cell.a.sheet,
range->v_range.cell.a.col + 1, i);
if (cell == NULL ||
(v = cell->value) == NULL ||
!VALUE_IS_NUMBER (v)) {
err = 1;
goto random_tool_discrete_out;
}
if ((thisprob = value_get_as_float (v)) < 0) {
err = 3;
goto random_tool_discrete_out;
}
discrete_random_tool_local_t *data = *continuity;
gint i;
prob[j] = thisprob;
cumprob += thisprob;
cumul_p[j] = cumprob;
for (i = 0; i < data->n; i++)
if (data->values[i])
value_release (data->values[i]);
g_free (data->cumul_p);
g_free (data->values);
g_free (data);
*continuity = NULL;
}
cell = sheet_cell_get (range->v_range.cell.a.sheet,
range->v_range.cell.a.col, i);
static gboolean
tool_random_engine_run_discrete_last_check (data_analysis_output_t *dao,
tools_data_random_t *info,
discrete_random_tool_t *param,
discrete_random_tool_local_t **continuity)
{
discrete_random_tool_local_t *data;
Value *range = param->range;
gnum_float cumprob = 0;
int j = 0;
int i;
if (cell == NULL || cell->value == NULL) {
err = 4;
goto random_tool_discrete_out;
}
data = *continuity = g_new0 (discrete_random_tool_local_t, 1);
data->n = range->v_range.cell.b.row - range->v_range.cell.a.row + 1;
data->cumul_p = g_new (gnum_float, data->n);
data->values = g_new0 (Value *, data->n);
values[j] = value_duplicate (cell->value);
for (i = range->v_range.cell.a.row;
i <= range->v_range.cell.b.row;
i++, j++) {
Value *v;
gnum_float thisprob;
Cell *cell = sheet_cell_get (range->v_range.cell.a.sheet,
range->v_range.cell.a.col + 1, i);
if (cell == NULL ||
(v = cell->value) == NULL ||
!VALUE_IS_NUMBER (v)) {
gnumeric_notice (info->wbcg, GTK_MESSAGE_ERROR,
_("The probability input range contains a "
"non-numeric value.\n"
"All probabilities must be non-negative numbers."));
goto random_tool_discrete_out;
}
if (cumprob == 0) {
err = 2;
if ((thisprob = value_get_as_float (v)) < 0) {
gnumeric_notice (info->wbcg, GTK_MESSAGE_ERROR,
_("The probability input range contains "
"a negative number.\n"
"All probabilities must be non-negative!"));
goto random_tool_discrete_out;
}
cumprob += thisprob;
data->cumul_p[j] = cumprob;
cell = sheet_cell_get (range->v_range.cell.a.sheet,
range->v_range.cell.a.col, i);
if (cell == NULL || cell->value == NULL) {
gnumeric_notice (info->wbcg, GTK_MESSAGE_ERROR,
_("None of the values in the value "
"range may be empty!"));
goto random_tool_discrete_out;
}
data->values[j] = value_duplicate (cell->value);
}
if (cumprob != 0) {
/* Rescale... */
for (i = 0; i < n; i++) {
prob[i] /= cumprob;
cumul_p[i] /= cumprob;
for (i = 0; i < data->n; i++) {
data->cumul_p[i] /= cumprob;
}
return FALSE;
}
gnumeric_notice (info->wbcg, GTK_MESSAGE_ERROR,
_("The probabilities may not all be 0!"));
dao_prepare_output (wbc, dao, _("Random"));
for (i = 0; i < vars; i++) {
int k;
for (k = 0; k < count; k++) {
int j;
gnum_float x = random_01 ();
random_tool_discrete_out:
tool_random_engine_run_discrete_clear_continuity (continuity);
return TRUE;
}
for (j = 0; cumul_p[j] < x; j++)
;
static gboolean
tool_random_engine_run_discrete (data_analysis_output_t *dao,
tools_data_random_t *info,
discrete_random_tool_t *param,
discrete_random_tool_local_t **continuity)
{
gint i;
discrete_random_tool_local_t *data = *continuity;
dao_set_cell_value (dao, i, k, value_duplicate (values[j]));
}
for (i = 0; i < info->n_vars; i++) {
int k;
for (k = 0; k < info->count; k++) {
int j;
gnum_float x = random_01 ();
for (j = 0; data->cumul_p[j] < x; j++)
;
dao_set_cell_value (dao, i, k, value_duplicate (data->values[j]));
}
random_tool_discrete_out:
for (i = 0; i < n; i++)
if (values[i])
value_release (values[i]);
g_free (prob);
g_free (cumul_p);
g_free (values);
value_release (range);
if (err)
return err;
break;
}
tool_random_engine_run_discrete_clear_continuity (continuity);
return FALSE;
}
case NormalDistribution: {
int i, n;
for (i = 0; i < vars; i++) {
for (n = 0; n < count; n++) {
gnum_float v;
v = param->normal.stdev * random_normal () + param->normal.mean;
dao_set_cell_float (dao, i, n, v);
}
static gboolean
tool_random_engine_run_uniform (data_analysis_output_t *dao,
tools_data_random_t *info,
uniform_random_tool_t *param)
{
int i, n;
gnum_float range = param->upper_limit - param->lower_limit;
for (i = 0; i < info->n_vars; i++) {
for (n = 0; n < info->count; n++) {
gnum_float v;
v = range * random_01 () + param->lower_limit;
dao_set_cell_float (dao, i, n, v);
}
break;
}
return FALSE;
}
case BernoulliDistribution: {
int i, n;
for (i = 0; i < vars; i++) {
for (n = 0; n < count; n++) {
gnum_float tmp = random_bernoulli (param->bernoulli.p);
dao_set_cell_int (dao, i, n, (int)tmp);
}
static gboolean
tool_random_engine_run_normal (data_analysis_output_t *dao,
tools_data_random_t *info,
normal_random_tool_t *param)
{
int i, n;
for (i = 0; i < info->n_vars; i++) {
for (n = 0; n < info->count; n++) {
gnum_float v;
v = param->stdev * random_normal () + param->mean;
dao_set_cell_float (dao, i, n, v);
}
break;
}
return FALSE;
}
case UniformDistribution: {
int i, n;
gnum_float range = param->uniform.upper_limit - param->uniform.lower_limit;
for (i = 0; i < vars; i++) {
for (n = 0; n < count; n++) {
gnum_float v;
v = range * random_01 () + param->uniform.lower_limit;
dao_set_cell_float (dao, i, n, v);
static gboolean
tool_random_engine_run_bernoulli (data_analysis_output_t *dao,
tools_data_random_t *info,
bernoulli_random_tool_t *param)
{
int i, n;
for (i = 0; i < info->n_vars; i++) {
for (n = 0; n < info->count; n++) {
gnum_float tmp = random_bernoulli (param->p);
dao_set_cell_int (dao, i, n, (int)tmp);
}
}
break;
}
return FALSE;
}
case PoissonDistribution: {
int i, n;
for (i = 0; i < vars; i++) {
for (n = 0; n < count; n++) {
gnum_float v;
v = random_poisson (param->poisson.lambda);
dao_set_cell_float (dao, i, n, v);
}
static gboolean
tool_random_engine_run_binomial (data_analysis_output_t *dao,
tools_data_random_t *info,
binomial_random_tool_t *param)
{
int i, n;
for (i = 0; i < info->n_vars; i++) {
for (n = 0; n < info->count; n++) {
gnum_float v;
v = random_binomial (param->p,
param->trials);
dao_set_cell_float (dao, i, n, v);
}
break;
}
}
return FALSE;
}
case ExponentialDistribution: {
int i, n;
for (i = 0; i < vars; i++) {
for (n = 0; n < count; n++) {
gnum_float v;
v = random_exponential (param->exponential.b);
dao_set_cell_float (dao, i, n, v);
}
static gboolean
tool_random_engine_run_negbinom (data_analysis_output_t *dao,
tools_data_random_t *info,
negbinom_random_tool_t *param)
{
int i, n;
for (i = 0; i < info->n_vars; i++) {
for (n = 0; n < info->count; n++) {
gnum_float v;
v = random_negbinom (param->p,
param->f);
dao_set_cell_float (dao, i, n, v);
}
break;
}
return FALSE;
}
case BinomialDistribution: {
int i, n;
for (i = 0; i < vars; i++) {
for (n = 0; n < count; n++) {
gnum_float v;
v = random_binomial (param->binomial.p,
param->binomial.trials);
dao_set_cell_float (dao, i, n, v);
}
static gboolean
tool_random_engine_run_poisson (data_analysis_output_t *dao,
tools_data_random_t *info,
poisson_random_tool_t *param)
{
int i, n;
for (i = 0; i < info->n_vars; i++) {
for (n = 0; n < info->count; n++) {
gnum_float v;
v = random_poisson (param->lambda);
dao_set_cell_float (dao, i, n, v);
}
break;
}
return FALSE;
}
case NegativeBinomialDistribution: {
int i, n;
for (i = 0; i < vars; i++) {
for (n = 0; n < count; n++) {
gnum_float v;
v = random_negbinom (param->negbinom.p,
param->negbinom.f);
dao_set_cell_float (dao, i, n, v);
}
static gboolean
tool_random_engine_run_exponential (data_analysis_output_t *dao,
tools_data_random_t *info,
exponential_random_tool_t *param)
{
int i, n;
for (i = 0; i < info->n_vars; i++) {
for (n = 0; n < info->count; n++) {
gnum_float v;
v = random_exponential (param->b);
dao_set_cell_float (dao, i, n, v);
}
break;
}
}
return FALSE;
}
gboolean
tool_random_engine (data_analysis_output_t *dao, gpointer specs,
analysis_tool_engine_t selector, gpointer result)
{
tools_data_random_t *info = specs;
switch (selector) {
case TOOL_ENGINE_UPDATE_DESCRIPTOR:
return (dao_command_descriptor (dao, _("Random Numbers (%s)"), result)
== NULL);
case TOOL_ENGINE_UPDATE_DAO:
dao_adjust (dao, info->n_vars, info->count);
return FALSE;
case TOOL_ENGINE_CLEAN_UP:
if (info->distribution == DiscreteDistribution &&
info->param.discrete.range != NULL) {
value_release (info->param.discrete.range);
info->param.discrete.range = NULL;
}
return FALSE;
case TOOL_ENGINE_LAST_VALIDITY_CHECK:
if (info->distribution == DiscreteDistribution)
return tool_random_engine_run_discrete_last_check
(dao, specs, &info->param.discrete, result);
return FALSE;
case TOOL_ENGINE_PREPARE_OUTPUT_RANGE:
dao_prepare_output (NULL, dao, _("Random Numbers"));
return FALSE;
case TOOL_ENGINE_FORMAT_OUTPUT_RANGE:
return dao_format_output (dao, _("Random Numbers"));
case TOOL_ENGINE_PERFORM_CALC:
default:
printf (_("Not implemented yet.\n"));
break;
switch (info->distribution) {
case DiscreteDistribution:
return tool_random_engine_run_discrete
(dao, specs, &info->param.discrete, result);
case NormalDistribution:
return tool_random_engine_run_normal (dao, specs, &info->param.normal);
case BernoulliDistribution:
return tool_random_engine_run_bernoulli
(dao, specs, &info->param.bernoulli);
case UniformDistribution:
return tool_random_engine_run_uniform (dao, specs, &info->param.uniform);
case PoissonDistribution:
return tool_random_engine_run_poisson(dao, specs, &info->param.poisson);
case ExponentialDistribution:
return tool_random_engine_run_exponential
(dao, specs, &info->param.exponential);
case BinomialDistribution:
return tool_random_engine_run_binomial (dao, specs, &info->param.binomial);
case NegativeBinomialDistribution:
return tool_random_engine_run_negbinom (dao, specs, &info->param.negbinom);
}