Commit cec403d7 authored by Morten Welinder's avatar Morten Welinder

Funcs: add COUNTIFS and fix COUNTIF.

parent c82f0f27
2016-08-08 Morten Welinder <terra@gnome.org> 2016-08-08 Morten Welinder <terra@gnome.org>
* src/criteria.c (criteria_inspect_values): Add flag for string
coercion. Only the equality test wants that.
* src/value.c (criteria_inspect_values): Floats don't match * src/value.c (criteria_inspect_values): Floats don't match
errors. errors.
......
...@@ -8,16 +8,12 @@ Jean: ...@@ -8,16 +8,12 @@ Jean:
Morten: Morten:
* Avoid gnome-common dependency. * Avoid gnome-common dependency.
* New function CONCAT. * New text functions CONCAT, TEXTJOIN.
* New function TEXTJOIN. * New selection functions IFS, SWITCH.
* New function IFS. * New aggregation functions SUMIFS, AVERAGEIFS, MINIFS, MAXIFS, COUNTIFS.
* New function SWITCH.
* New function SUMIFS.
* New function AVERAGEIFS.
* New function MINIFS.
* New function MAXIFS.
* Fix criteria function issue with errors in the selector area. * Fix criteria function issue with errors in the selector area.
* Fix corner case for MINA and MAXA. * Fix corner case for MINA and MAXA.
* Fix criteria matching of numbers against strings.
-------------------------------------------------------------------------- --------------------------------------------------------------------------
Gnumeric 1.12.31 Gnumeric 1.12.31
......
...@@ -3440,6 +3440,15 @@ The depreciation coefficient used is: ...@@ -3440,6 +3440,15 @@ The depreciation coefficient used is:
@EXCEL=This function is Excel compatible. @EXCEL=This function is Excel compatible.
@SEEALSO=COUNT,SUMIF @SEEALSO=COUNT,SUMIF
@CATEGORY=Mathematics
@FUNCTION=COUNTIFS
@SHORTDESC=count of the cells meeting the given @{criteria}
@SYNTAX=COUNTIFS(range,criteria,…)
@ARGUMENTDESCRIPTION=@{range}: cell area
@{criteria}: condition for a cell to be counted
@EXCEL=This function is Excel compatible.
@SEEALSO=COUNT,SUMIF
@CATEGORY=Mathematics @CATEGORY=Mathematics
@FUNCTION=CSC @FUNCTION=CSC
@SHORTDESC=the cosecant of @{x} @SHORTDESC=the cosecant of @{x}
......
...@@ -11261,6 +11261,39 @@ ...@@ -11261,6 +11261,39 @@
</para> </para>
</refsect1> </refsect1>
</refentry> </refentry>
<refentry id="gnumeric-function-COUNTIFS">
<refmeta>
<refentrytitle>
<function>COUNTIFS</function>
</refentrytitle>
</refmeta>
<refnamediv>
<refname>
<function>COUNTIFS</function>
</refname>
<refpurpose>
count of the cells meeting the given <parameter>criteria</parameter>
</refpurpose>
</refnamediv>
<refsynopsisdiv>
<synopsis><function>COUNTIFS</function>(<parameter>range</parameter>,<parameter>criteria</parameter>,<parameter/>…)</synopsis>
</refsynopsisdiv>
<refsect1>
<title>Arguments</title>
<para><parameter>range</parameter>: cell area</para>
<para><parameter>criteria</parameter>: condition for a cell to be counted</para>
</refsect1>
<refsect1>
<title>Microsoft Excel Compatibility</title>
<para>This function is Excel compatible.</para>
</refsect1>
<refsect1>
<title>See also</title>
<para><link linkend="gnumeric-function-COUNT"><function>COUNT</function></link>,
<link linkend="gnumeric-function-SUMIF"><function>SUMIF</function></link>.
</para>
</refsect1>
</refentry>
<refentry id="gnumeric-function-CSC"> <refentry id="gnumeric-function-CSC">
<refmeta> <refmeta>
<refentrytitle> <refentrytitle>
......
...@@ -57,88 +57,10 @@ GNM_PLUGIN_MODULE_HEADER; ...@@ -57,88 +57,10 @@ GNM_PLUGIN_MODULE_HEADER;
/***************************************************************************/ /***************************************************************************/
static GnmValue *
ifs_func (GPtrArray *data, GPtrArray *crits, GnmValue const *vals,
float_range_function_t fun, GnmStdError err,
GnmEvalPos const *ep, CollectFlags flags)
{
int sx, sy, x, y;
unsigned ui, N = 0, nalloc = 0;
gnm_float *xs = NULL;
GnmValue *res = NULL;
gnm_float fres;
g_return_val_if_fail (data->len == crits->len, NULL);
if (flags & ~(COLLECT_IGNORE_STRINGS |
COLLECT_IGNORE_BOOLS |
COLLECT_IGNORE_BLANKS |
COLLECT_IGNORE_ERRORS)) {
g_warning ("unsupported flags in ifs_func %x", flags);
}
sx = value_area_get_width (vals, ep);
sy = value_area_get_height (vals, ep);
for (ui = 0; ui < data->len; ui++) {
GnmValue const *datai = g_ptr_array_index (data, ui);
if (value_area_get_width (datai, ep) != sx ||
value_area_get_height (datai, ep) != sy)
return value_new_error_VALUE (ep);
}
for (y = 0; y < sy; y++) {
for (x = 0; x < sy; x++) {
GnmValue const *v;
gboolean match = TRUE;
for (ui = 0; match && ui < crits->len; ui++) {
GnmCriteria *crit = g_ptr_array_index (crits, ui);
GnmValue const *datai = g_ptr_array_index (data, ui);
v = value_area_get_x_y (datai, x, y, ep);
match = crit->fun (v, crit);
}
if (!match)
continue;
// Match. Maybe collect the data point.
v = value_area_get_x_y (vals, x, y, ep);
if ((flags & COLLECT_IGNORE_STRINGS) && VALUE_IS_STRING (v))
continue;
if ((flags & COLLECT_IGNORE_BOOLS) && VALUE_IS_BOOLEAN (v))
continue;
if ((flags & COLLECT_IGNORE_BLANKS) && VALUE_IS_EMPTY (v))
continue;
if ((flags & COLLECT_IGNORE_ERRORS) && VALUE_IS_ERROR (v))
continue;
if (VALUE_IS_ERROR (v)) {
res = value_dup (v);
goto out;
}
if (N >= nalloc) {
nalloc = (2 * nalloc) + 100;
xs = g_renew (gnm_float, xs, nalloc);
}
xs[N++] = value_get_as_float (v);
}
}
if (fun (xs, N, &fres)) {
res = value_new_error_std (ep, err);
} else
res = value_new_float (fres);
out:
g_free (xs);
return res;
}
static GnmValue * static GnmValue *
oldstyle_if_func (GnmFuncEvalInfo *ei, GnmValue const * const *argv, oldstyle_if_func (GnmFuncEvalInfo *ei, GnmValue const * const *argv,
float_range_function_t fun, GnmStdError err) float_range_function_t fun, GnmStdError err,
CollectFlags flags)
{ {
GPtrArray *crits = g_ptr_array_new_with_free_func ((GDestroyNotify)free_criteria); GPtrArray *crits = g_ptr_array_new_with_free_func ((GDestroyNotify)free_criteria);
GPtrArray *data = g_ptr_array_new (); GPtrArray *data = g_ptr_array_new ();
...@@ -171,11 +93,9 @@ oldstyle_if_func (GnmFuncEvalInfo *ei, GnmValue const * const *argv, ...@@ -171,11 +93,9 @@ oldstyle_if_func (GnmFuncEvalInfo *ei, GnmValue const * const *argv,
insanity = FALSE; insanity = FALSE;
} }
res = ifs_func (data, crits, vals, res = gnm_ifs_func (data, crits, vals,
fun, err, ei->pos, fun, err, ei->pos,
COLLECT_IGNORE_STRINGS | flags);
COLLECT_IGNORE_BLANKS |
COLLECT_IGNORE_BOOLS);
out: out:
g_ptr_array_free (data, TRUE); g_ptr_array_free (data, TRUE);
...@@ -186,7 +106,8 @@ out: ...@@ -186,7 +106,8 @@ out:
static GnmValue * static GnmValue *
newstyle_if_func (GnmFuncEvalInfo *ei, int argc, GnmExprConstPtr const *argv, newstyle_if_func (GnmFuncEvalInfo *ei, int argc, GnmExprConstPtr const *argv,
float_range_function_t fun, GnmStdError err) float_range_function_t fun, GnmStdError err,
gboolean no_data)
{ {
GPtrArray *crits = g_ptr_array_new_with_free_func ((GDestroyNotify)free_criteria); GPtrArray *crits = g_ptr_array_new_with_free_func ((GDestroyNotify)free_criteria);
GPtrArray *data = g_ptr_array_new_with_free_func ((GDestroyNotify)value_release); GPtrArray *data = g_ptr_array_new_with_free_func ((GDestroyNotify)value_release);
...@@ -195,25 +116,28 @@ newstyle_if_func (GnmFuncEvalInfo *ei, int argc, GnmExprConstPtr const *argv, ...@@ -195,25 +116,28 @@ newstyle_if_func (GnmFuncEvalInfo *ei, int argc, GnmExprConstPtr const *argv,
GnmValue *res; GnmValue *res;
GnmValue *vals = NULL; GnmValue *vals = NULL;
int i; int i;
int cstart = no_data ? 0 : 1;
if ((argc & 1) == 0) { if ((argc - cstart) & 1) {
res = value_new_error_VALUE (ei->pos); res = value_new_error_VALUE (ei->pos);
goto out; goto out;
} }
vals = gnm_expr_eval (argv[0], ei->pos, if (!no_data) {
GNM_EXPR_EVAL_PERMIT_NON_SCALAR | vals = gnm_expr_eval (argv[0], ei->pos,
GNM_EXPR_EVAL_WANT_REF); GNM_EXPR_EVAL_PERMIT_NON_SCALAR |
if (VALUE_IS_ERROR (vals)) { GNM_EXPR_EVAL_WANT_REF);
res = value_dup (vals); if (VALUE_IS_ERROR (vals)) {
goto out; res = value_dup (vals);
} goto out;
if (!VALUE_IS_CELLRANGE (vals)) { }
res = value_new_error_VALUE (ei->pos); if (!VALUE_IS_CELLRANGE (vals)) {
goto out; res = value_new_error_VALUE (ei->pos);
goto out;
}
} }
for (i = 1; i + 1 < argc; i += 2) { for (i = cstart; i + 1 < argc; i += 2) {
GnmValue *area, *crit; GnmValue *area, *crit;
area = gnm_expr_eval (argv[i], ei->pos, area = gnm_expr_eval (argv[i], ei->pos,
...@@ -223,6 +147,8 @@ newstyle_if_func (GnmFuncEvalInfo *ei, int argc, GnmExprConstPtr const *argv, ...@@ -223,6 +147,8 @@ newstyle_if_func (GnmFuncEvalInfo *ei, int argc, GnmExprConstPtr const *argv,
res = area; res = area;
goto out; goto out;
} }
if (no_data && !vals)
vals = value_dup (area);
g_ptr_array_add (data, area); g_ptr_array_add (data, area);
crit = gnm_expr_eval (argv[i + 1], ei->pos, crit = gnm_expr_eval (argv[i + 1], ei->pos,
...@@ -236,11 +162,19 @@ newstyle_if_func (GnmFuncEvalInfo *ei, int argc, GnmExprConstPtr const *argv, ...@@ -236,11 +162,19 @@ newstyle_if_func (GnmFuncEvalInfo *ei, int argc, GnmExprConstPtr const *argv,
value_release (crit); value_release (crit);
} }
res = ifs_func (data, crits, vals, if (!vals) {
fun, err, ei->pos, // COUNTIFS with no arguments.
COLLECT_IGNORE_STRINGS | res = value_new_error_VALUE (ei->pos);
COLLECT_IGNORE_BLANKS | goto out;
COLLECT_IGNORE_BOOLS); }
res = gnm_ifs_func (data, crits, vals,
fun, err, ei->pos,
(no_data
? 0
: COLLECT_IGNORE_STRINGS |
COLLECT_IGNORE_BLANKS |
COLLECT_IGNORE_BOOLS));
out: out:
g_ptr_array_free (data, TRUE); g_ptr_array_free (data, TRUE);
...@@ -698,66 +632,36 @@ static GnmFuncHelp const help_countif[] = { ...@@ -698,66 +632,36 @@ static GnmFuncHelp const help_countif[] = {
{ GNM_FUNC_HELP_END} { GNM_FUNC_HELP_END}
}; };
typedef struct {
GnmCriteria *crit;
int count;
} CountIfClosure;
static GnmValue * static GnmValue *
cb_countif (GnmCellIter const *iter, CountIfClosure *res) gnumeric_countif (GnmFuncEvalInfo *ei, GnmValue const * const *argv)
{ {
GnmCell *cell = iter->cell; GnmValue const * argv3[3];
GnmValue *v;
if (cell) {
gnm_cell_eval (cell);
v = cell->value;
} else
v = value_new_empty (); /* Never released */
if (!VALUE_IS_EMPTY (v) && !VALUE_IS_NUMBER (v) && !VALUE_IS_STRING (v)) argv3[0] = argv[0];
return NULL; argv3[1] = argv[1];
argv3[2] = NULL;
if (!res->crit->fun (v, res->crit)) return oldstyle_if_func (ei, argv3, gnm_range_count, GNM_ERROR_DIV0,
return NULL; 0);
}
res->count++; /***************************************************************************/
return NULL; static GnmFuncHelp const help_countifs[] = {
} { GNM_FUNC_HELP_NAME, F_("COUNTIFS:count of the cells meeting the given @{criteria}")},
{ GNM_FUNC_HELP_ARG, F_("range:cell area")},
{ GNM_FUNC_HELP_ARG, F_("criteria:condition for a cell to be counted")},
{ GNM_FUNC_HELP_EXCEL, F_("This function is Excel compatible.") },
{ GNM_FUNC_HELP_SEEALSO, "COUNT,SUMIF"},
{ GNM_FUNC_HELP_END}
};
static GnmValue * static GnmValue *
gnumeric_countif (GnmFuncEvalInfo *ei, GnmValue const * const *argv) gnumeric_countifs (GnmFuncEvalInfo *ei, int argc, GnmExprConstPtr const *argv)
{ {
GnmValueRange const *r = &argv[0]->v_range; return newstyle_if_func (ei, argc, argv,
Sheet *sheet; gnm_range_count, GNM_ERROR_DIV0,
GnmValue *problem; TRUE);
CountIfClosure res;
GODateConventions const *date_conv =
workbook_date_conv (ei->pos->sheet->workbook);
/* XL has some limitations on @range that we currently emulate, but do
* not need to.
* 1) @range must be a range, arrays are not supported
* 2) @range can not be 3d */
if (!VALUE_IS_CELLRANGE (argv[0]) ||
((sheet = eval_sheet (r->cell.a.sheet, ei->pos->sheet)) != r->cell.b.sheet &&
r->cell.b.sheet != NULL) ||
(!VALUE_IS_NUMBER (argv[1]) && !VALUE_IS_STRING (argv[1])))
return value_new_error_VALUE (ei->pos);
res.count = 0;
res.crit = parse_criteria (argv[1], date_conv, TRUE);
problem = sheet_foreach_cell_in_range
(sheet, res.crit->iter_flags,
r->cell.a.col, r->cell.a.row, r->cell.b.col, r->cell.b.row,
(CellIterFunc) &cb_countif, &res);
free_criteria (res.crit);
if (NULL != problem)
return value_new_error_VALUE (ei->pos);
return value_new_int (res.count);
} }
/***************************************************************************/ /***************************************************************************/
...@@ -780,7 +684,10 @@ static GnmFuncHelp const help_sumif[] = { ...@@ -780,7 +684,10 @@ static GnmFuncHelp const help_sumif[] = {
static GnmValue * static GnmValue *
gnumeric_sumif (GnmFuncEvalInfo *ei, GnmValue const * const *argv) gnumeric_sumif (GnmFuncEvalInfo *ei, GnmValue const * const *argv)
{ {
return oldstyle_if_func (ei, argv, gnm_range_sum, GNM_ERROR_DIV0); return oldstyle_if_func (ei, argv, gnm_range_sum, GNM_ERROR_DIV0,
COLLECT_IGNORE_STRINGS |
COLLECT_IGNORE_BLANKS |
COLLECT_IGNORE_BOOLS);
} }
/***************************************************************************/ /***************************************************************************/
...@@ -798,7 +705,9 @@ static GnmFuncHelp const help_sumifs[] = { ...@@ -798,7 +705,9 @@ static GnmFuncHelp const help_sumifs[] = {
static GnmValue * static GnmValue *
gnumeric_sumifs (GnmFuncEvalInfo *ei, int argc, GnmExprConstPtr const *argv) gnumeric_sumifs (GnmFuncEvalInfo *ei, int argc, GnmExprConstPtr const *argv)
{ {
return newstyle_if_func (ei, argc, argv, gnm_range_sum, GNM_ERROR_DIV0); return newstyle_if_func (ei, argc, argv,
gnm_range_sum, GNM_ERROR_DIV0,
FALSE);
} }
/***************************************************************************/ /***************************************************************************/
...@@ -816,7 +725,10 @@ static GnmFuncHelp const help_averageif[] = { ...@@ -816,7 +725,10 @@ static GnmFuncHelp const help_averageif[] = {
static GnmValue * static GnmValue *
gnumeric_averageif (GnmFuncEvalInfo *ei, GnmValue const * const *argv) gnumeric_averageif (GnmFuncEvalInfo *ei, GnmValue const * const *argv)
{ {
return oldstyle_if_func (ei, argv, gnm_range_average, GNM_ERROR_DIV0); return oldstyle_if_func (ei, argv, gnm_range_average, GNM_ERROR_DIV0,
COLLECT_IGNORE_STRINGS |
COLLECT_IGNORE_BLANKS |
COLLECT_IGNORE_BOOLS);
} }
/***************************************************************************/ /***************************************************************************/
...@@ -835,7 +747,8 @@ static GnmValue * ...@@ -835,7 +747,8 @@ static GnmValue *
gnumeric_averageifs (GnmFuncEvalInfo *ei, int argc, GnmExprConstPtr const *argv) gnumeric_averageifs (GnmFuncEvalInfo *ei, int argc, GnmExprConstPtr const *argv)
{ {
return newstyle_if_func (ei, argc, argv, return newstyle_if_func (ei, argc, argv,
gnm_range_average, GNM_ERROR_DIV0); gnm_range_average, GNM_ERROR_DIV0,
FALSE);
} }
/***************************************************************************/ /***************************************************************************/
...@@ -854,7 +767,8 @@ static GnmValue * ...@@ -854,7 +767,8 @@ static GnmValue *
gnumeric_minifs (GnmFuncEvalInfo *ei, int argc, GnmExprConstPtr const *argv) gnumeric_minifs (GnmFuncEvalInfo *ei, int argc, GnmExprConstPtr const *argv)
{ {
return newstyle_if_func (ei, argc, argv, return newstyle_if_func (ei, argc, argv,
gnm_range_min, GNM_ERROR_DIV0); gnm_range_min, GNM_ERROR_DIV0,
FALSE);
} }
/***************************************************************************/ /***************************************************************************/
...@@ -873,7 +787,8 @@ static GnmValue * ...@@ -873,7 +787,8 @@ static GnmValue *
gnumeric_maxifs (GnmFuncEvalInfo *ei, int argc, GnmExprConstPtr const *argv) gnumeric_maxifs (GnmFuncEvalInfo *ei, int argc, GnmExprConstPtr const *argv)
{ {
return newstyle_if_func (ei, argc, argv, return newstyle_if_func (ei, argc, argv,
gnm_range_max, GNM_ERROR_DIV0); gnm_range_max, GNM_ERROR_DIV0,
FALSE);
} }
/***************************************************************************/ /***************************************************************************/
...@@ -3490,10 +3405,13 @@ GnmFuncDescriptor const math_functions[] = { ...@@ -3490,10 +3405,13 @@ GnmFuncDescriptor const math_functions[] = {
gnumeric_coth, NULL, NULL, NULL, gnumeric_coth, NULL, NULL, NULL,
GNM_FUNC_SIMPLE, GNM_FUNC_IMPL_STATUS_COMPLETE, GNM_FUNC_TEST_STATUS_NO_TESTSUITE }, GNM_FUNC_SIMPLE, GNM_FUNC_IMPL_STATUS_COMPLETE, GNM_FUNC_TEST_STATUS_NO_TESTSUITE },
/* MS Excel puts this in statistical */
{ "countif", "rS", help_countif, { "countif", "rS", help_countif,
gnumeric_countif, NULL, NULL, NULL, gnumeric_countif, NULL, NULL, NULL,
GNM_FUNC_SIMPLE, GNM_FUNC_IMPL_STATUS_COMPLETE, GNM_FUNC_TEST_STATUS_BASIC }, GNM_FUNC_SIMPLE, GNM_FUNC_IMPL_STATUS_COMPLETE, GNM_FUNC_TEST_STATUS_BASIC },
{ "countifs", NULL, help_countifs,
NULL, gnumeric_countifs, NULL, NULL,
GNM_FUNC_SIMPLE,
GNM_FUNC_IMPL_STATUS_COMPLETE, GNM_FUNC_TEST_STATUS_NO_TESTSUITE },
{ "ceil", "f", help_ceil, { "ceil", "f", help_ceil,
gnumeric_ceil, NULL, NULL, NULL, gnumeric_ceil, NULL, NULL, NULL,
......
...@@ -39,6 +39,7 @@ ...@@ -39,6 +39,7 @@
<function name="coth"/> <function name="coth"/>
<function name="cotpi"/> <function name="cotpi"/>
<function name="countif"/> <function name="countif"/>
<function name="countifs"/>
<function name="csc"/> <function name="csc"/>
<function name="csch"/> <function name="csch"/>
<function name="degrees"/> <function name="degrees"/>
......
...@@ -4,9 +4,8 @@ typedef enum { CRIT_NULL, CRIT_FLOAT, CRIT_WRONGTYPE, CRIT_STRING } CritType; ...@@ -4,9 +4,8 @@ typedef enum { CRIT_NULL, CRIT_FLOAT, CRIT_WRONGTYPE, CRIT_STRING } CritType;
static CritType static CritType
criteria_inspect_values (GnmValue const *x, gnm_float *xr, gnm_float *yr, criteria_inspect_values (GnmValue const *x, gnm_float *xr, gnm_float *yr,
GnmCriteria *crit) GnmCriteria *crit, gboolean coerce_to_float)
{ {
GnmValue *vx;
GnmValue const *y = crit->x; GnmValue const *y = crit->x;
if (x == NULL || y == NULL) if (x == NULL || y == NULL)
...@@ -34,7 +33,8 @@ criteria_inspect_values (GnmValue const *x, gnm_float *xr, gnm_float *yr, ...@@ -34,7 +33,8 @@ criteria_inspect_values (GnmValue const *x, gnm_float *xr, gnm_float *yr,
g_warning ("This should not happen. Please report."); g_warning ("This should not happen. Please report.");
return CRIT_WRONGTYPE; return CRIT_WRONGTYPE;
case VALUE_FLOAT: case VALUE_FLOAT: {
GnmValue *vx;
*yr = value_get_as_float (y); *yr = value_get_as_float (y);
if (VALUE_IS_BOOLEAN (x) || VALUE_IS_ERROR (x)) if (VALUE_IS_BOOLEAN (x) || VALUE_IS_ERROR (x))
...@@ -44,6 +44,9 @@ criteria_inspect_values (GnmValue const *x, gnm_float *xr, gnm_float *yr, ...@@ -44,6 +44,9 @@ criteria_inspect_values (GnmValue const *x, gnm_float *xr, gnm_float *yr,
return CRIT_FLOAT; return CRIT_FLOAT;
} }
if (!coerce_to_float)
return CRIT_WRONGTYPE;
vx = format_match (value_peek_string (x), NULL, crit->date_conv); vx = format_match (value_peek_string (x), NULL, crit->date_conv);
if (VALUE_IS_EMPTY (vx) || if (VALUE_IS_EMPTY (vx) ||
VALUE_IS_BOOLEAN (y) != VALUE_IS_BOOLEAN (vx)) { VALUE_IS_BOOLEAN (y) != VALUE_IS_BOOLEAN (vx)) {
...@@ -55,6 +58,7 @@ criteria_inspect_values (GnmValue const *x, gnm_float *xr, gnm_float *yr, ...@@ -55,6 +58,7 @@ criteria_inspect_values (GnmValue const *x, gnm_float *xr, gnm_float *yr,
value_release (vx); value_release (vx);
return CRIT_FLOAT; return CRIT_FLOAT;
} }
}
} }
...@@ -64,7 +68,7 @@ criteria_test_equal (GnmValue const *x, GnmCriteria *crit) ...@@ -64,7 +68,7 @@ criteria_test_equal (GnmValue const *x, GnmCriteria *crit)
gnm_float xf, yf; gnm_float xf, yf;
GnmValue const *y = crit->x; GnmValue const *y = crit->x;
switch (criteria_inspect_values (x, &xf, &yf, crit)) { switch (criteria_inspect_values (x, &xf, &yf, crit, TRUE)) {
default: default:
g_assert_not_reached (); g_assert_not_reached ();
case CRIT_NULL: case CRIT_NULL:
...@@ -84,7 +88,7 @@ criteria_test_unequal (GnmValue const *x, GnmCriteria *crit) ...@@ -84,7 +88,7 @@ criteria_test_unequal (GnmValue const *x, GnmCriteria *crit)
{ {
gnm_float xf, yf; gnm_float xf, yf;
switch (criteria_inspect_values (x, &xf, &yf, crit)) { switch (criteria_inspect_values (x, &xf, &yf, crit, FALSE)) {
default: default:
g_assert_not_reached (); g_assert_not_reached ();
case CRIT_NULL: case CRIT_NULL:
...@@ -105,7 +109,7 @@ criteria_test_less (GnmValue const *x, GnmCriteria *crit) ...@@ -105,7 +109,7 @@ criteria_test_less (GnmValue const *x, GnmCriteria *crit)
gnm_float xf, yf; gnm_float xf, yf;
GnmValue const *y = crit->x; GnmValue const *y = crit->x;
switch (criteria_inspect_values (x, &xf, &yf, crit)) { switch (criteria_inspect_values (x, &xf, &yf, crit, FALSE)) {
default: default:
g_assert_not_reached (); g_assert_not_reached ();
case CRIT_NULL: case CRIT_NULL:
...@@ -125,7 +129,7 @@ criteria_test_greater (GnmValue const *x, GnmCriteria *crit) ...@@ -125,7 +129,7 @@ criteria_test_greater (GnmValue const *x, GnmCriteria *crit)
gnm_float xf, yf; gnm_float xf, yf;
GnmValue const *y = crit->x; GnmValue const *y = crit->x;
switch (criteria_inspect_values (x, &xf, &yf, crit)) { switch (criteria_inspect_values (x, &xf, &yf, crit, FALSE)) {
default: default:
g_assert_not_reached (); g_assert_not_reached ();
case CRIT_NULL: case CRIT_NULL:
...@@ -145,7 +149,7 @@ criteria_test_less_or_equal (GnmValue const *x, GnmCriteria *crit) ...@@ -145,7 +149,7 @@ criteria_test_less_or_equal (GnmValue const *x, GnmCriteria *crit)
gnm_float xf, yf; gnm_float xf, yf;
GnmValue const *y = crit->x; GnmValue const *y = crit->x;
switch (criteria_inspect_values (x, &xf, &yf, crit)) { switch (criteria_inspect_values (x, &xf, &yf, crit, FALSE)) {
default: default:
g_assert_not_reached (); g_assert_not_reached ();
case CRIT_NULL: case CRIT_NULL:
...@@ -165,7 +169,7 @@ criteria_test_greater_or_equal (GnmValue const *x, GnmCriteria *crit) ...@@ -165,7 +169,7 @@ criteria_test_greater_or_equal (GnmValue const *x, GnmCriteria *crit)
gnm_float xf, yf; gnm_float xf, yf;
GnmValue const *y = crit->x; GnmValue const *y = crit->x;
switch (criteria_inspect_values (x, &xf, &yf, crit)) { switch (criteria_inspect_values (x, &xf, &yf, crit, FALSE)) {
default: default:
g_assert_not_reached (); g_assert_not_reached ();
case CRIT_NULL: case CRIT_NULL:
...@@ -569,3 +573,84 @@ filter_row: ...@@ -569,3 +573,84 @@ filter_row:
} }
/****************************************************************************/ /****************************************************************************/
GnmValue *
gnm_ifs_func (GPtrArray *data, GPtrArray *crits, GnmValue const *vals,
float_range_function_t fun, GnmStdError err,
GnmEvalPos const *ep, CollectFlags flags)
{
int sx, sy, x, y;
unsigned ui, N = 0, nalloc = 0;
gnm_float *xs = NULL;
GnmValue *res = NULL;
gnm_float fres;
g_return_val_if_fail (data->len == crits->len, NULL);
if (flags & ~(COLLECT_IGNORE_STRINGS |
COLLECT_IGNORE_BOOLS |
COLLECT_IGNORE_BLANKS |
COLLECT_IGNORE_ERRORS)) {
g_warning ("unsupported flags in gnm_ifs_func %x", flags);
}
sx = value_area_get_width (vals, ep);
sy = value_area_get_height (vals, ep);
for (ui = 0; ui < data->len; ui++) {
GnmValue const *datai = g_ptr_array_index (data, ui);
if (value_area_get_width (datai, ep) != sx ||
value_area_get_height (datai, ep) != sy)
return value_new_error_VALUE (ep);
}
for (y = 0; y < sy; y++) {
for (x = 0; x < sx; x++) {
GnmValue const *v;
gboolean match = TRUE;
for (ui = 0; match && ui < crits->len; ui++) {
GnmCriteria *crit = g_ptr_array_index (crits, ui);
GnmValue const *datai = g_ptr_array_index (data, ui);
v = value_area_get_x_y (datai, x, y, ep);
match = crit->fun (v, crit);
}
if (!match)
continue;
// Match. Maybe collect the data point.
v = value_area_get_x_y (vals, x, y, ep);
if ((flags & COLLECT_IGNORE_STRINGS) && VALUE_IS_STRING (v))
continue;
if ((flags & COLLECT_IGNORE_BOOLS) && VALUE_IS_BOOLEAN (v))
continue;