[gnumeric] Funcs: add COUNTIFS and fix COUNTIF.



commit cec403d756c1b848e348a9804772bc2a55effaf2
Author: Morten Welinder <terra gnome org>
Date:   Mon Aug 8 13:43:52 2016 -0400

    Funcs: add COUNTIFS and fix COUNTIF.

 ChangeLog                      |    3 +
 NEWS                           |   12 +--
 doc/C/func.defs                |    9 ++
 doc/C/functions.xml            |   33 ++++++
 plugins/fn-math/functions.c    |  240 +++++++++++++---------------------------
 plugins/fn-math/plugin.xml.in  |    1 +
 samples/excel12/countif.xlsx   |  Bin 12921 -> 13231 bytes
 samples/excel12/ifs-funcs.xlsx |  Bin 8878 -> 8850 bytes
 src/criteria.c                 |  103 ++++++++++++++++--
 src/criteria.h                 |    5 +
 10 files changed, 228 insertions(+), 178 deletions(-)
---
diff --git a/ChangeLog b/ChangeLog
index 2d10abf..11e47ee 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,8 @@
 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
        errors.
 
diff --git a/NEWS b/NEWS
index 60240cc..2483ee3 100644
--- a/NEWS
+++ b/NEWS
@@ -8,16 +8,12 @@ Jean:
 
 Morten:
        * Avoid gnome-common dependency.
-       * New function CONCAT.
-       * New function TEXTJOIN.
-       * New function IFS.
-       * New function SWITCH.
-       * New function SUMIFS.
-       * New function AVERAGEIFS.
-       * New function MINIFS.
-       * New function MAXIFS.
+       * New text functions CONCAT, TEXTJOIN.
+       * New selection functions IFS, SWITCH.
+       * New aggregation functions SUMIFS, AVERAGEIFS, MINIFS, MAXIFS, COUNTIFS.
        * Fix criteria function issue with errors in the selector area.
        * Fix corner case for MINA and MAXA.
+       * Fix criteria matching of numbers against strings.
 
 --------------------------------------------------------------------------
 Gnumeric 1.12.31
diff --git a/doc/C/func.defs b/doc/C/func.defs
index 5c2eff2..70267e5 100644
--- a/doc/C/func.defs
+++ b/doc/C/func.defs
@@ -3441,6 +3441,15 @@ The depreciation coefficient used is:
 @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
 @FUNCTION=CSC
 @SHORTDESC=the cosecant of @{x}
 @SYNTAX=CSC(x)
diff --git a/doc/C/functions.xml b/doc/C/functions.xml
index 92ff0d4..6f44e4a 100644
--- a/doc/C/functions.xml
+++ b/doc/C/functions.xml
@@ -11261,6 +11261,39 @@
       </para>
       </refsect1>
     </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">
       <refmeta>
         <refentrytitle>
diff --git a/plugins/fn-math/functions.c b/plugins/fn-math/functions.c
index 3ae56cc..56b81c1 100644
--- a/plugins/fn-math/functions.c
+++ b/plugins/fn-math/functions.c
@@ -58,87 +58,9 @@ 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 *
 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 *data = g_ptr_array_new ();
@@ -171,11 +93,9 @@ oldstyle_if_func (GnmFuncEvalInfo *ei, GnmValue const * const *argv,
                insanity = FALSE;
        }
 
-       res = ifs_func (data, crits, vals,
-                       fun, err, ei->pos,
-                       COLLECT_IGNORE_STRINGS |
-                       COLLECT_IGNORE_BLANKS |
-                       COLLECT_IGNORE_BOOLS);
+       res = gnm_ifs_func (data, crits, vals,
+                           fun, err, ei->pos,
+                           flags);
 
 out:
        g_ptr_array_free (data, TRUE);
@@ -186,7 +106,8 @@ out:
 
 static GnmValue *
 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 *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,
        GnmValue *res;
        GnmValue *vals = NULL;
        int i;
+       int cstart = no_data ? 0 : 1;
 
-       if ((argc & 1) == 0) {
+       if ((argc - cstart) & 1) {
                res = value_new_error_VALUE (ei->pos);
                goto out;
        }
 
-       vals = gnm_expr_eval (argv[0], ei->pos,
-                             GNM_EXPR_EVAL_PERMIT_NON_SCALAR |
-                             GNM_EXPR_EVAL_WANT_REF);
-       if (VALUE_IS_ERROR (vals)) {
-               res = value_dup (vals);
-               goto out;
-       }
-       if (!VALUE_IS_CELLRANGE (vals)) {
-               res = value_new_error_VALUE (ei->pos);
-               goto out;
+       if (!no_data) {
+               vals = gnm_expr_eval (argv[0], ei->pos,
+                                     GNM_EXPR_EVAL_PERMIT_NON_SCALAR |
+                                     GNM_EXPR_EVAL_WANT_REF);
+               if (VALUE_IS_ERROR (vals)) {
+                       res = value_dup (vals);
+                       goto out;
+               }
+               if (!VALUE_IS_CELLRANGE (vals)) {
+                       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;
 
                area = gnm_expr_eval (argv[i], ei->pos,
@@ -223,6 +147,8 @@ newstyle_if_func (GnmFuncEvalInfo *ei, int argc, GnmExprConstPtr const *argv,
                        res = area;
                        goto out;
                }
+               if (no_data && !vals)
+                       vals = value_dup (area);
                g_ptr_array_add (data, area);
 
                crit = gnm_expr_eval (argv[i + 1], ei->pos,
@@ -236,11 +162,19 @@ newstyle_if_func (GnmFuncEvalInfo *ei, int argc, GnmExprConstPtr const *argv,
                value_release (crit);
        }
 
-       res = ifs_func (data, crits, vals,
-                       fun, err, ei->pos,
-                       COLLECT_IGNORE_STRINGS |
-                       COLLECT_IGNORE_BLANKS |
-                       COLLECT_IGNORE_BOOLS);
+       if (!vals) {
+               // COUNTIFS with no arguments.
+               res = value_new_error_VALUE (ei->pos);
+               goto out;
+       }
+
+       res = gnm_ifs_func (data, crits, vals,
+                           fun, err, ei->pos,
+                           (no_data
+                            ? 0
+                            : COLLECT_IGNORE_STRINGS |
+                            COLLECT_IGNORE_BLANKS |
+                            COLLECT_IGNORE_BOOLS));
 
 out:
        g_ptr_array_free (data, TRUE);
@@ -698,66 +632,36 @@ static GnmFuncHelp const help_countif[] = {
        { GNM_FUNC_HELP_END}
 };
 
-typedef struct {
-       GnmCriteria *crit;
-       int count;
-} CountIfClosure;
-
 static GnmValue *
-cb_countif (GnmCellIter const *iter, CountIfClosure *res)
+gnumeric_countif (GnmFuncEvalInfo *ei, GnmValue const * const *argv)
 {
-       GnmCell *cell = iter->cell;
-       GnmValue *v;
-
-       if (cell) {
-               gnm_cell_eval (cell);
-               v = cell->value;
-       } else
-               v = value_new_empty ();  /* Never released */
+       GnmValue const * argv3[3];
 
-       if (!VALUE_IS_EMPTY (v) && !VALUE_IS_NUMBER (v) && !VALUE_IS_STRING (v))
-               return NULL;
+       argv3[0] = argv[0];
+       argv3[1] = argv[1];
+       argv3[2] = NULL;
 
-       if (!res->crit->fun (v, res->crit))
-               return NULL;
+       return oldstyle_if_func (ei, argv3, gnm_range_count, GNM_ERROR_DIV0,
+                                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 *
-gnumeric_countif (GnmFuncEvalInfo *ei, GnmValue const * const *argv)
+gnumeric_countifs (GnmFuncEvalInfo *ei, int argc, GnmExprConstPtr const *argv)
 {
-       GnmValueRange const *r = &argv[0]->v_range;
-       Sheet           *sheet;
-       GnmValue        *problem;
-       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);
+       return newstyle_if_func (ei, argc, argv,
+                                gnm_range_count, GNM_ERROR_DIV0,
+                                TRUE);
 }
 
 /***************************************************************************/
@@ -780,7 +684,10 @@ static GnmFuncHelp const help_sumif[] = {
 static GnmValue *
 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[] = {
 static GnmValue *
 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[] = {
 static GnmValue *
 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 *
 gnumeric_averageifs (GnmFuncEvalInfo *ei, int argc, GnmExprConstPtr const *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 *
 gnumeric_minifs (GnmFuncEvalInfo *ei, int argc, GnmExprConstPtr const *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 *
 gnumeric_maxifs (GnmFuncEvalInfo *ei, int argc, GnmExprConstPtr const *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[] = {
          gnumeric_coth, NULL, NULL, NULL,
          GNM_FUNC_SIMPLE, GNM_FUNC_IMPL_STATUS_COMPLETE, GNM_FUNC_TEST_STATUS_NO_TESTSUITE },
 
-/* MS Excel puts this in statistical */
        { "countif", "rS",  help_countif,
          gnumeric_countif, NULL, NULL, NULL,
          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,
          gnumeric_ceil, NULL, NULL, NULL,
diff --git a/plugins/fn-math/plugin.xml.in b/plugins/fn-math/plugin.xml.in
index 90089c9..9dccaef 100644
--- a/plugins/fn-math/plugin.xml.in
+++ b/plugins/fn-math/plugin.xml.in
@@ -39,6 +39,7 @@
                                <function name="coth"/>
                                <function name="cotpi"/>
                                <function name="countif"/>
+                               <function name="countifs"/>
                                <function name="csc"/>
                                <function name="csch"/>
                                <function name="degrees"/>
diff --git a/samples/excel12/countif.xlsx b/samples/excel12/countif.xlsx
index abbc254..aac753c 100644
Binary files a/samples/excel12/countif.xlsx and b/samples/excel12/countif.xlsx differ
diff --git a/samples/excel12/ifs-funcs.xlsx b/samples/excel12/ifs-funcs.xlsx
index 083021c..7488efa 100644
Binary files a/samples/excel12/ifs-funcs.xlsx and b/samples/excel12/ifs-funcs.xlsx differ
diff --git a/src/criteria.c b/src/criteria.c
index 0a8acaf..e4d600f 100644
--- a/src/criteria.c
+++ b/src/criteria.c
@@ -4,9 +4,8 @@ typedef enum { CRIT_NULL, CRIT_FLOAT, CRIT_WRONGTYPE, CRIT_STRING } CritType;
 
 static CritType
 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;
 
        if (x == NULL || y == NULL)
@@ -34,7 +33,8 @@ criteria_inspect_values (GnmValue const *x, gnm_float *xr, gnm_float *yr,
                g_warning ("This should not happen.  Please report.");
                return CRIT_WRONGTYPE;
 
-       case VALUE_FLOAT:
+       case VALUE_FLOAT: {
+               GnmValue *vx;
                *yr = value_get_as_float (y);
 
                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,
                        return CRIT_FLOAT;
                }
 
+               if (!coerce_to_float)
+                       return CRIT_WRONGTYPE;
+
                vx = format_match (value_peek_string (x), NULL, crit->date_conv);
                if (VALUE_IS_EMPTY (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,
                value_release (vx);
                return CRIT_FLOAT;
        }
+       }
 }
 
 
@@ -64,7 +68,7 @@ criteria_test_equal (GnmValue const *x, GnmCriteria *crit)
        gnm_float xf, yf;
        GnmValue const *y = crit->x;
 
-       switch (criteria_inspect_values (x, &xf, &yf, crit)) {
+       switch (criteria_inspect_values (x, &xf, &yf, crit, TRUE)) {
        default:
                g_assert_not_reached ();
        case CRIT_NULL:
@@ -84,7 +88,7 @@ criteria_test_unequal (GnmValue const *x, GnmCriteria *crit)
 {
        gnm_float xf, yf;
 
-       switch (criteria_inspect_values (x, &xf, &yf, crit)) {
+       switch (criteria_inspect_values (x, &xf, &yf, crit, FALSE)) {
        default:
                g_assert_not_reached ();
        case CRIT_NULL:
@@ -105,7 +109,7 @@ criteria_test_less (GnmValue const *x, GnmCriteria *crit)
        gnm_float xf, yf;
        GnmValue const *y = crit->x;
 
-       switch (criteria_inspect_values (x, &xf, &yf, crit)) {
+       switch (criteria_inspect_values (x, &xf, &yf, crit, FALSE)) {
        default:
                g_assert_not_reached ();
        case CRIT_NULL:
@@ -125,7 +129,7 @@ criteria_test_greater (GnmValue const *x, GnmCriteria *crit)
        gnm_float xf, yf;
        GnmValue const *y = crit->x;
 
-       switch (criteria_inspect_values (x, &xf, &yf, crit)) {
+       switch (criteria_inspect_values (x, &xf, &yf, crit, FALSE)) {
        default:
                g_assert_not_reached ();
        case CRIT_NULL:
@@ -145,7 +149,7 @@ criteria_test_less_or_equal (GnmValue const *x, GnmCriteria *crit)
        gnm_float xf, yf;
        GnmValue const *y = crit->x;
 
-       switch (criteria_inspect_values (x, &xf, &yf, crit)) {
+       switch (criteria_inspect_values (x, &xf, &yf, crit, FALSE)) {
        default:
                g_assert_not_reached ();
        case CRIT_NULL:
@@ -165,7 +169,7 @@ criteria_test_greater_or_equal (GnmValue const *x, GnmCriteria *crit)
        gnm_float xf, yf;
        GnmValue const *y = crit->x;
 
-       switch (criteria_inspect_values (x, &xf, &yf, crit)) {
+       switch (criteria_inspect_values (x, &xf, &yf, crit, FALSE)) {
        default:
                g_assert_not_reached ();
        case CRIT_NULL:
@@ -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;
+                       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;
+}
+
+/****************************************************************************/
diff --git a/src/criteria.h b/src/criteria.h
index caab0ae..9589158 100644
--- a/src/criteria.h
+++ b/src/criteria.h
@@ -9,6 +9,7 @@
 #include <cell.h>
 #include <gutils.h>
 #include <workbook.h>
+#include <collect.h>
 #include <goffice/goffice.h>
 
 #include <string.h>
@@ -48,6 +49,10 @@ GSList *parse_database_criteria (GnmEvalPos const *ep,
 int     find_column_of_field   (GnmEvalPos const *ep,
                                 GnmValue const *database, GnmValue const *field);
 
+GnmValue *gnm_ifs_func (GPtrArray *data, GPtrArray *crits, GnmValue const *vals,
+                       float_range_function_t fun, GnmStdError err,
+                       GnmEvalPos const *ep, CollectFlags flags);
+
 
 G_END_DECLS
 


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]