Commit ccd33788 authored by Jukka-Pekka Iivonen's avatar Jukka-Pekka Iivonen Committed by jpekka

Added FORECAST() and INTERCEPT().

1999-06-29  Jukka-Pekka Iivonen  <iivonen@iki.fi>

	* src/fn-stat.c: Added FORECAST() and INTERCEPT().

	* src/fn-math.c: SUMIF() can now take an optional argument
 	(actual_range).

	* src/fn-information.c: Made N() return 0 if the given string is
	non-number, not an error.
parent c4b27568
......@@ -6,7 +6,10 @@ Miguel:
Jukka:
* Added the following functions:
- FORECAST() and INTERCEPT().
* And fixed these functions:
- SUMIF() and N().
Morten:
......
1999-06-29 Jukka-Pekka Iivonen <iivonen@iki.fi>
* src/fn-stat.c: Added FORECAST() and INTERCEPT().
* src/fn-math.c: SUMIF() can now take an optional argument
(actual_range).
* src/fn-information.c: Made N() return 0 if the given string is
non-number, not an error.
1999-06-28 Michael Meeks <michael@edenproject.org>
* src/main.c (gnumeric_main): removed xbase_init.
......
1999-06-29 Jukka-Pekka Iivonen <iivonen@iki.fi>
* src/fn-stat.c: Added FORECAST() and INTERCEPT().
* src/fn-math.c: SUMIF() can now take an optional argument
(actual_range).
* src/fn-information.c: Made N() return 0 if the given string is
non-number, not an error.
1999-06-28 Michael Meeks <michael@edenproject.org>
* src/main.c (gnumeric_main): removed xbase_init.
......
1999-06-29 Jukka-Pekka Iivonen <iivonen@iki.fi>
* src/fn-stat.c: Added FORECAST() and INTERCEPT().
* src/fn-math.c: SUMIF() can now take an optional argument
(actual_range).
* src/fn-information.c: Made N() return 0 if the given string is
non-number, not an error.
1999-06-28 Michael Meeks <michael@edenproject.org>
* src/main.c (gnumeric_main): removed xbase_init.
......
1999-06-29 Jukka-Pekka Iivonen <iivonen@iki.fi>
* src/fn-stat.c: Added FORECAST() and INTERCEPT().
* src/fn-math.c: SUMIF() can now take an optional argument
(actual_range).
* src/fn-information.c: Made N() return 0 if the given string is
non-number, not an error.
1999-06-28 Michael Meeks <michael@edenproject.org>
* src/main.c (gnumeric_main): removed xbase_init.
......
......@@ -11,8 +11,8 @@ Gnumeric Spread Sheet task list
* Even More Functions
FORECAST, FTEST, GROWTH, INTERCEPT, LINEST, LOGEST, PERCENTILE,
QUARTILE, TREND, TTEST, ERROR.TYPE.
GROWTH, LINEST, LOGEST, PERCENTILE, MMULT, MDETERM,
QUARTILE, TREND, ERROR.TYPE.
(This is not a claim that these are all.)
......
......@@ -418,7 +418,8 @@ static char *help_n = {
"@SYNTAX=N()\n"
"@DESCRIPTION="
"N Returns a value converted to a number. "
"N Returns a value converted to a number. Strings containing "
"text are converted to the zero value. "
"\n"
"@SEEALSO=")
};
......@@ -440,7 +441,7 @@ gnumeric_n (FunctionEvalInfo *ei, Value **argv)
if (format_match (str, &v, &format))
return value_new_float (v);
else
return function_error (ei, gnumeric_err_NUM);
return value_new_float (0);
}
......
......@@ -116,6 +116,10 @@ typedef struct {
criteria_test_fun_t fun;
Value *test_value;
int num;
int total_num;
gboolean actual_range;
float_t sum;
GSList *current;
} math_criteria_t;
static int
......@@ -124,7 +128,9 @@ callback_function_criteria (Sheet *sheet, int col, int row,
{
math_criteria_t *mm = user_data;
Value *v;
gpointer n;
mm->total_num++;
if (cell == NULL || cell->value == NULL)
return TRUE;
......@@ -143,7 +149,12 @@ callback_function_criteria (Sheet *sheet, int col, int row,
}
if (mm->fun(v, mm->test_value)) {
mm->list = g_slist_append (mm->list, v);
if (mm->actual_range) {
n = g_new (int, 1);
*((int *) n) = mm->total_num;
mm->list = g_slist_append (mm->list, n);
} else
mm->list = g_slist_append (mm->list, v);
mm->num++;
} else
value_release(v);
......@@ -489,6 +500,7 @@ gnumeric_countif (FunctionEvalInfo *ei, Value **argv)
items.num = 0;
items.list = NULL;
items.actual_range = FALSE;
if ((!VALUE_IS_NUMBER(argv[1]) && argv[1]->type != VALUE_STRING)
|| (range->type != VALUE_CELLRANGE))
......@@ -531,21 +543,63 @@ gnumeric_countif (FunctionEvalInfo *ei, Value **argv)
static char *help_sumif = {
N_("@FUNCTION=SUMIF\n"
"@SYNTAX=SUMIF(range,criteria)\n"
"@SYNTAX=SUMIF(range,criteria[,actual_range])\n"
"@DESCRIPTION="
"SUMIF function sums the values in the given range that meet "
"the given criteria. "
"the given criteria. If @actual_range is given, SUMIF sums "
"the values in the @actual_range whose corresponding components "
"in @range meet the given criteria. "
"\n"
"@SEEALSO=COUNTIF,SUM")
};
static int
callback_function_sumif (Sheet *sheet, int col, int row,
Cell *cell, void *user_data)
{
math_criteria_t *mm = user_data;
float_t v;
int num;
mm->total_num++;
if (cell == NULL || cell->value == NULL)
return TRUE;
switch (cell->value->type) {
case VALUE_INTEGER:
v = cell->value->v.v_int;
break;
case VALUE_FLOAT:
v = cell->value->v.v_float;
break;
case VALUE_STRING:
v = 0;
break;
default:
return TRUE;
}
if (mm->current == NULL)
return TRUE;
num = *((int*) mm->current->data);
if (mm->total_num == num) {
mm->sum += v;
g_free(mm->current->data);
mm->current=mm->current->next;
}
return TRUE;
}
static Value *
gnumeric_sumif (FunctionEvalInfo *ei, Value **argv)
{
Value *range = argv[0];
Value *actual_range = argv[2];
Value *tmpvalue = NULL;
math_criteria_t items;
......@@ -554,6 +608,7 @@ gnumeric_sumif (FunctionEvalInfo *ei, Value **argv)
GSList *list;
items.num = 0;
items.total_num = 0;
items.list = NULL;
if ((!VALUE_IS_NUMBER(argv[1]) && argv[1]->type != VALUE_STRING)
......@@ -569,6 +624,11 @@ gnumeric_sumif (FunctionEvalInfo *ei, Value **argv)
tmpvalue = items.test_value;
}
if (actual_range != NULL)
items.actual_range = TRUE;
else
items.actual_range = FALSE;
ret = sheet_cell_foreach_range (
range->v.cell_range.cell_a.sheet, TRUE,
range->v.cell_range.cell_a.col,
......@@ -584,19 +644,34 @@ gnumeric_sumif (FunctionEvalInfo *ei, Value **argv)
if (ret == FALSE)
return function_error (ei, gnumeric_err_VALUE);
list = items.list;
sum = 0;
if (actual_range == NULL) {
list = items.list;
sum = 0;
while (list != NULL) {
Value *v = list->data;
while (list != NULL) {
Value *v = list->data;
if (v != NULL)
sum += value_get_as_float (v);
value_release (v);
list = list->next;
if (v != NULL)
sum += value_get_as_float (v);
value_release (v);
list = list->next;
}
} else {
items.current = items.list;
items.sum = items.total_num = 0;
ret = sheet_cell_foreach_range (
actual_range->v.cell_range.cell_a.sheet, TRUE,
actual_range->v.cell_range.cell_a.col,
actual_range->v.cell_range.cell_a.row,
actual_range->v.cell_range.cell_b.col,
actual_range->v.cell_range.cell_b.row,
callback_function_sumif,
&items);
sum = items.sum;
}
g_slist_free(items.list);
g_slist_free(items.list);
return value_new_float (sum);
}
......@@ -2406,7 +2481,7 @@ void math_functions_init()
gnumeric_sum);
function_add_nodes (cat, "suma", 0, "number1,number2,...", &help_suma,
gnumeric_suma);
function_add_args (cat, "sumif", "r?", "range,criteria", &help_sumif, gnumeric_sumif);
function_add_args (cat, "sumif", "r?|r", "range,criteria[,actual_range]", &help_sumif, gnumeric_sumif);
function_add_nodes (cat, "sumproduct", 0, "range1,range2,...",
&help_sumproduct, gnumeric_sumproduct);
function_add_nodes (cat, "sumsq", 0, "number", &help_sumsq,
......
......@@ -3397,6 +3397,224 @@ gnumeric_ttest (FunctionEvalInfo *ei, Value *argv [])
}
}
static char *help_forecast = {
N_("@FUNCTION=FORECAST\n"
"@SYNTAX=FORECAST(x,known_y's,known_x's)\n"
"@DESCRIPTION="
"FORECAST function estimates a future value according to "
"existing values using simple linear regression. The estimated "
"future value is a y-value for a given x-value (@x). "
"\n"
"If known_x or known_y contains no data entries or different "
"number of data entries, FORECAST returns #N/A! error. "
"If the variance of the known_x is zero, FORECAST returns #DIV/0 "
"error. "
"\n"
"@SEEALSO=INTERCEPT,TREND")
};
typedef struct {
float_t sum_x;
float_t sum_y;
float_t sqrsum_x;
float_t sqrsum_y;
float_t sum_xy;
gboolean first; /* Is this the first variable */
GSList *entries;
GSList *current;
int n;
} stat_lrstat_t;
static int
callback_function_lrstat (const EvalPosition *ep, Value *value,
ErrorMessage *error, void *closure)
{
stat_lrstat_t *mm = closure;
float_t x;
if (VALUE_IS_NUMBER (value))
x = value_get_as_float (value);
else
x = 0;
if (mm->first) {
gpointer p = g_new(float_t, 1);
*((float_t *) p) = x;
mm->entries = g_slist_append(mm->entries, p);
mm->sum_x += x;
mm->sqrsum_x += x*x;
mm->n++;
} else {
if (mm->current == NULL) {
error_message_set (error, gnumeric_err_VALUE);
return FALSE;
}
mm->sum_y += x;
mm->sqrsum_y += x*x;
mm->sum_xy += *((float_t *) mm->current->data) * x;
g_free(mm->current->data);
mm->current = mm->current->next;
}
return TRUE;
}
static void
init_lrstat_closure(stat_lrstat_t *cl)
{
cl->first = TRUE;
cl->entries = NULL;
cl->sum_x = cl->sum_y = cl->sum_xy = cl->sqrsum_x = cl->sqrsum_y = 0;
cl->n = 0;
}
static Value *
gnumeric_forecast (FunctionEvalInfo *ei, Value *argv [])
{
GSList *current;
ExprTree *tree;
GList *expr_node_list;
stat_lrstat_t cl;
EvalPosition ep;
float_t x, a, b, mean_x, mean_y, tmp;
x = value_get_as_float(argv[0]);
init_lrstat_closure(&cl);
tree = g_new(ExprTree, 1);
tree->u.constant = argv[1];
tree->oper = OPER_CONSTANT;
expr_node_list = g_list_append(NULL, tree);
if (!function_iterate_argument_values
(eval_pos_init (&ep, argv[1]->v.cell_range.cell_a.sheet,
argv[1]->v.cell_range.cell_a.col,
argv[1]->v.cell_range.cell_a.row),
callback_function_lrstat, &cl, expr_node_list,
ei->error, TRUE))
return function_error (ei, gnumeric_err_NA);
g_free(tree);
g_list_free(expr_node_list);
cl.first = FALSE;
cl.current = cl.entries;
tree = g_new(ExprTree, 1);
tree->u.constant = argv[2];
tree->oper = OPER_CONSTANT;
expr_node_list = g_list_append(NULL, tree);
if (!function_iterate_argument_values
(eval_pos_init (&ep, argv[2]->v.cell_range.cell_a.sheet,
argv[2]->v.cell_range.cell_a.col,
argv[2]->v.cell_range.cell_a.row),
callback_function_lrstat, &cl, expr_node_list,
ei->error, TRUE))
return function_error (ei, gnumeric_err_NA);
g_free(tree);
g_list_free(expr_node_list);
if (cl.n < 1)
return function_error (ei, gnumeric_err_NA);
mean_x = cl.sum_x / cl.n;
mean_y = cl.sum_y / cl.n;
tmp = cl.n*cl.sqrsum_y - cl.sum_y*cl.sum_y;
if (tmp == 0)
return function_error (ei, gnumeric_err_DIV0);
b = (cl.n*cl.sum_xy - cl.sum_x*cl.sum_y) / tmp;
a = mean_x - b * mean_y;
return value_new_float (a + b*x);
}
static char *help_intercept = {
N_("@FUNCTION=INTERCEPT\n"
"@SYNTAX=INTERCEPT(known_y's,known_x's)\n"
"@DESCRIPTION="
"INTERCEPT function calculates the point where the linear "
"regression line intersects the y-axis. "
"\n"
"If known_x or known_y contains no data entries or different "
"number of data entries, INTERCEPT returns #N/A! error. "
"If the variance of the known_x is zero, INTERCEPT returns #DIV/0 "
"error. "
"\n"
"@SEEALSO=FORECAST,TREND")
};
static Value *
gnumeric_intercept (FunctionEvalInfo *ei, Value *argv [])
{
GSList *current;
ExprTree *tree;
GList *expr_node_list;
stat_lrstat_t cl;
EvalPosition ep;
float_t a, b, mean_x, mean_y, tmp;
init_lrstat_closure(&cl);
tree = g_new(ExprTree, 1);
tree->u.constant = argv[0];
tree->oper = OPER_CONSTANT;
expr_node_list = g_list_append(NULL, tree);
if (!function_iterate_argument_values
(eval_pos_init (&ep, argv[0]->v.cell_range.cell_a.sheet,
argv[0]->v.cell_range.cell_a.col,
argv[0]->v.cell_range.cell_a.row),
callback_function_lrstat, &cl, expr_node_list,
ei->error, TRUE))
return function_error (ei, gnumeric_err_NA);
g_free(tree);
g_list_free(expr_node_list);
cl.first = FALSE;
cl.current = cl.entries;
tree = g_new(ExprTree, 1);
tree->u.constant = argv[1];
tree->oper = OPER_CONSTANT;
expr_node_list = g_list_append(NULL, tree);
if (!function_iterate_argument_values
(eval_pos_init (&ep, argv[1]->v.cell_range.cell_a.sheet,
argv[1]->v.cell_range.cell_a.col,
argv[1]->v.cell_range.cell_a.row),
callback_function_lrstat, &cl, expr_node_list,
ei->error, TRUE))
return function_error (ei, gnumeric_err_NA);
g_free(tree);
g_list_free(expr_node_list);
if (cl.n < 1)
return function_error (ei, gnumeric_err_NA);
mean_x = cl.sum_x / cl.n;
mean_y = cl.sum_y / cl.n;
tmp = cl.n*cl.sqrsum_y - cl.sum_y*cl.sum_y;
if (tmp == 0)
return function_error (ei, gnumeric_err_DIV0);
b = (cl.n*cl.sum_xy - cl.sum_x*cl.sum_y) / tmp;
a = mean_x - b * mean_y;
return value_new_float (a);
}
void stat_functions_init()
{
FunctionCategory *cat = function_get_category (_("Statistics"));
......@@ -3447,6 +3665,8 @@ void stat_functions_init()
gnumeric_fisher);
function_add_args (cat, "fisherinv", "f", "", &help_fisherinv,
gnumeric_fisherinv);
function_add_args (cat, "forecast", "frr", "", &help_forecast,
gnumeric_forecast);
function_add_args (cat, "ftest", "rr", "arr1,arr2", &help_ftest,
gnumeric_ftest);
function_add_args (cat, "gammaln", "f", "number", &help_gammaln,
......@@ -3461,6 +3681,8 @@ void stat_functions_init()
gnumeric_harmean);
function_add_args (cat, "hypgeomdist", "ffff", "x,n,M,N", &help_hypgeomdist,
gnumeric_hypgeomdist);
function_add_args (cat, "intercept", "rr", "", &help_intercept,
gnumeric_intercept);
function_add_nodes (cat, "kurt", 0, "", &help_kurt,
gnumeric_kurt);
function_add_nodes (cat, "kurtp", 0, "", &help_kurtp,
......
......@@ -418,7 +418,8 @@ static char *help_n = {
"@SYNTAX=N()\n"
"@DESCRIPTION="
"N Returns a value converted to a number. "
"N Returns a value converted to a number. Strings containing "
"text are converted to the zero value. "
"\n"
"@SEEALSO=")
};
......@@ -440,7 +441,7 @@ gnumeric_n (FunctionEvalInfo *ei, Value **argv)
if (format_match (str, &v, &format))
return value_new_float (v);
else
return function_error (ei, gnumeric_err_NUM);
return value_new_float (0);
}
......
......@@ -116,6 +116,10 @@ typedef struct {
criteria_test_fun_t fun;
Value *test_value;
int num;
int total_num;
gboolean actual_range;
float_t sum;
GSList *current;
} math_criteria_t;
static int
......@@ -124,7 +128,9 @@ callback_function_criteria (Sheet *sheet, int col, int row,
{
math_criteria_t *mm = user_data;
Value *v;
gpointer n;
mm->total_num++;
if (cell == NULL || cell->value == NULL)
return TRUE;
......@@ -143,7 +149,12 @@ callback_function_criteria (Sheet *sheet, int col, int row,
}
if (mm->fun(v, mm->test_value)) {
mm->list = g_slist_append (mm->list, v);
if (mm->actual_range) {
n = g_new (int, 1);
*((int *) n) = mm->total_num;
mm->list = g_slist_append (mm->list, n);
} else
mm->list = g_slist_append (mm->list, v);
mm->num++;
} else
value_release(v);
......@@ -489,6 +500,7 @@ gnumeric_countif (FunctionEvalInfo *ei, Value **argv)
items.num = 0;
items.list = NULL;
items.actual_range = FALSE;
if ((!VALUE_IS_NUMBER(argv[1]) && argv[1]->type != VALUE_STRING)
|| (range->type != VALUE_CELLRANGE))
......@@ -531,21 +543,63 @@ gnumeric_countif (FunctionEvalInfo *ei, Value **argv)
static char *help_sumif = {
N_("@FUNCTION=SUMIF\n"
"@SYNTAX=SUMIF(range,criteria)\n"
"@SYNTAX=SUMIF(range,criteria[,actual_range])\n"
"@DESCRIPTION="
"SUMIF function sums the values in the given range that meet "
"the given criteria. "
"the given criteria. If @actual_range is given, SUMIF sums "
"the values in the @actual_range whose corresponding components "
"in @range meet the given criteria. "
"\n"
"@SEEALSO=COUNTIF,SUM")
};
static int
callback_function_sumif (Sheet *sheet, int col, int row,
Cell *cell, void *user_data)
{
math_criteria_t *mm = user_data;
float_t v;
int num;
mm->total_num++;
if (cell == NULL || cell->value == NULL)
return TRUE;
switch (cell->value->type) {
case VALUE_INTEGER:
v = cell->value->v.v_int;
break;
case VALUE_FLOAT:
v = cell->value->v.v_float;
break;
case VALUE_STRING:
v = 0;
break;
default:
return TRUE;
}
if (mm->current == NULL)
return TRUE;
num = *((int*) mm->current->data);
if (mm->total_num == num) {
mm->sum += v;
g_free(mm->current->data);
mm->current=mm->current->next;
}
return TRUE;
}
static Value *
gnumeric_sumif (FunctionEvalInfo *ei, Value **argv)
{
Value *range = argv[0];
Value *actual_range = argv[2];
Value *tmpvalue = NULL;
math_criteria_t items;
......@@ -554,6 +608,7 @@ gnumeric_sumif (FunctionEvalInfo *ei, Value **argv)
GSList *list;
items.num = 0;
items.total_num = 0;
items.list = NULL;
if ((!VALUE_IS_NUMBER(argv[1]) && argv[1]->type != VALUE_STRING)
......@@ -569,6 +624,11 @@ gnumeric_sumif (FunctionEvalInfo *ei, Value **argv)
tmpvalue = items.test_value;
}
if (actual_range != NULL)
items.actual_range = TRUE;
else
items.actual_range = FALSE;
ret = sheet_cell_foreach_range (
range->v.cell_range.cell_a.sheet, TRUE,
range->v.cell_range.cell_a.col,
......@@ -584,19 +644,34 @@ gnumeric_sumif (FunctionEvalInfo *ei, Value **argv)
if (ret == FALSE)
return function_error (ei, gnumeric_err_VALUE);
list = items.list;
sum = 0;
if (actual_range == NULL) {
list = items.list;
sum = 0;
while (list != NULL) {
Value *v = list->data;
while (list != NULL) {
Value *v = list->data;
if (v != NULL)
sum += value_get_as_float (v);
value_release (v);
list = list->next;
if (v != NULL)
sum += value_get_as_float (v);
value_release (v);
list = list->next;
}
} else {
items.current = items.list;
items.sum = items.total_num = 0;
ret = sheet_cell_foreach_range (
actual_range->v.cell_range.cell_a.sheet, TRUE,
actual_range->v.cell_range.cell_a.col,
actual_range->v.cell_range.cell_a.row,
actual_range->v.cell_range.cell_b.col,
actual_range->v.cell_range.cell_b.row,
callback_function_sumif,
&items);
sum = items.sum;
}
g_slist_free(items.list);
g_slist_free(items.list);
return value_new_float (sum);
}
......@@ -2406,7 +2481,7 @@ void math_functions_init()
gnumeric_sum);
function_add_nodes (cat, "suma", 0, "number1,number2,...", &help_suma,
gnumeric_suma);
function_add_args (cat, "sumif", "r?", "range,criteria", &help_sumif, gnumeric_sumif);
function_add_args (cat, "sumif", "r?|r", "range,criteria[,actual_range]", &help_sumif, gnumeric_sumif);
function_add_nodes (cat, "sumproduct", 0, "range1,range2,...",
&help_sumproduct, gnumeric_sumproduct);
function_add_nodes (cat, "sumsq", 0, "number", &help_sumsq,
......
......@@ -3397,6 +3397,224 @@ gnumeric_ttest (FunctionEvalInfo *ei, Value *argv [])
}
}
static char *help_forecast = {
N_("@FUNCTION=FORECAST\n"
"@SYNTAX=FORECAST(x,known_y's,known_x's)\n"
"@DESCRIPTION="
"FORECAST function estimates a future value according to "
"existing values using simple linear regression. The estimated "
"future value is a y-value for a given x-value (@x). "
"\n"
"If known_x or known_y contains no data entries or different "
"number of data entries, FORECAST returns #N/A! error. "