[gnumeric] Deps: improve tracking of implicit intersection.
- From: Morten Welinder <mortenw src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnumeric] Deps: improve tracking of implicit intersection.
- Date: Wed, 1 Jul 2020 00:58:08 +0000 (UTC)
commit 2cd3f00e1025e877d74ffc9e27e04d3700b8679d
Author: Morten Welinder <terra gnome org>
Date: Tue Jun 30 20:56:56 2020 -0400
Deps: improve tracking of implicit intersection.
Using =A:A (typically through a name) shouldn't make a sense depend on the
whole column, but just the single cell needed.
NEWS | 1 +
src/dependent.c | 177 ++++++++++++++++++++++++++++++++++++++++----------------
src/expr.c | 31 +++++-----
3 files changed, 145 insertions(+), 64 deletions(-)
---
diff --git a/NEWS b/NEWS
index c11dc850f..b661f0100 100644
--- a/NEWS
+++ b/NEWS
@@ -22,6 +22,7 @@ Morten:
* Speed up really large auto-filters. [#465]
* Fix 2038 problem on quit.
* Improve tests.
+ * Improve dependency tracking for implicit intersection. [#501]
--------------------------------------------------------------------------
Gnumeric 1.12.47
diff --git a/src/dependent.c b/src/dependent.c
index daacde85f..d20eb37b2 100644
--- a/src/dependent.c
+++ b/src/dependent.c
@@ -44,6 +44,13 @@ static gboolean gnm_cell_eval_content (GnmCell *cell);
static void dependent_changed (GnmDependent *dep);
static void dependent_clear_dynamic_deps (GnmDependent *dep);
+typedef enum {
+ DEP_LINK_NON_SCALAR = 1,
+
+ DEP_LINK_LINK = 0x8000,
+ DEP_LINK_UNLINK = 0
+} DepLinkFlags;
+
/* ------------------------------------------------------------------------- */
/*
@@ -944,9 +951,9 @@ unlink_single_dep (GnmDependent *dep, GnmCellPos const *pos, GnmCellRef const *a
static inline GnmDependentFlags
link_unlink_single_dep (GnmDependent *dep, GnmCellPos const *pos,
- GnmCellRef const *a, gboolean qlink)
+ GnmCellRef const *a, DepLinkFlags flags)
{
- return qlink
+ return (flags & DEP_LINK_LINK)
? link_single_dep (dep, pos, a)
: unlink_single_dep (dep, pos, a);
}
@@ -1031,9 +1038,9 @@ unlink_range_dep (GnmDepContainer *deps, GnmDependent *dep,
static inline void
link_unlink_range_dep (GnmDepContainer *deps, GnmDependent *dep,
- GnmRange const *r, gboolean qlink)
+ GnmRange const *r, DepLinkFlags flags)
{
- if (qlink)
+ if (flags & DEP_LINK_LINK)
link_range_dep (deps, dep, r);
else
unlink_range_dep (deps, dep, r);
@@ -1042,7 +1049,7 @@ link_unlink_range_dep (GnmDepContainer *deps, GnmDependent *dep,
static GnmDependentFlags
link_unlink_cellrange_dep (GnmDependent *dep, GnmCellPos const *pos,
GnmCellRef const *a, GnmCellRef const *b,
- gboolean qlink)
+ DepLinkFlags flags)
{
GnmRange range;
GnmDependentFlags flag = DEPENDENT_NO_FLAG;
@@ -1068,20 +1075,111 @@ link_unlink_cellrange_dep (GnmDependent *dep, GnmCellPos const *pos,
Sheet *sheet = g_ptr_array_index (wb->sheets, i);
i++;
link_unlink_range_dep (sheet->deps, dep, &range,
- qlink);
+ flags);
}
flag |= DEPENDENT_HAS_3D;
} else
link_unlink_range_dep (a->sheet->deps, dep, &range,
- qlink);
+ flags);
} else
- link_unlink_range_dep (dep->sheet->deps, dep, &range, qlink);
+ link_unlink_range_dep (dep->sheet->deps, dep, &range, flags);
return flag;
}
static GnmDependentFlags
-link_unlink_expr_dep (GnmEvalPos *ep, GnmExpr const *tree, gboolean qlink)
+link_unlink_expr_dep (GnmEvalPos *ep, GnmExpr const *tree, DepLinkFlags flags);
+
+static GnmDependentFlags
+link_unlink_constant (GnmEvalPos *ep, GnmValue const *v, DepLinkFlags flags)
+{
+ GnmRange r;
+ Sheet *start_sheet, *end_sheet;
+ int col, row;
+ gboolean found = FALSE;
+ GnmCellRef cr;
+
+ if (!VALUE_IS_CELLRANGE (v))
+ return DEPENDENT_NO_FLAG;
+
+ if (!dependent_is_cell (ep->dep))
+ goto everything; // Must figure out semantics first
+
+ if ((flags & DEP_LINK_NON_SCALAR) &&
+ eval_pos_is_array_context (ep))
+ goto everything; // Potential implicit iteration -- bail
+
+ // Inspiration from value_intersection:
+
+ gnm_rangeref_normalize (&v->v_range.cell, ep, &start_sheet, &end_sheet, &r);
+ if (!(start_sheet == end_sheet || end_sheet == NULL))
+ return DEPENDENT_NO_FLAG; // An error
+
+ col = ep->eval.col;
+ row = ep->eval.row;
+
+ if (range_is_singleton (&r)) {
+ col = r.start.col;
+ row = r.start.row;
+ found = TRUE;
+ } else if (r.start.row == r.end.row &&
+ r.start.col <= col && col <= r.end.col) {
+ row = r.start.row;
+ found = TRUE;
+ } else if (r.start.col == r.end.col &&
+ r.start.row <= row && row <= r.end.row) {
+ col = r.start.col;
+ found = TRUE;
+ }
+ if (!found)
+ goto everything;
+
+ cr.col_relative = cr.row_relative = FALSE;
+ cr.sheet = ep->dep->sheet;
+ cr.col = col;
+ cr.row = row;
+
+ return link_unlink_single_dep (ep->dep, dependent_pos (ep->dep),
+ &cr, flags);
+
+everything:
+ return link_unlink_cellrange_dep
+ (ep->dep, dependent_pos (ep->dep),
+ &v->v_range.cell.a,
+ &v->v_range.cell.b,
+ flags);
+}
+
+static GnmDependentFlags
+link_unlink_funcall (GnmEvalPos *ep, GnmExprFunction const *call, DepLinkFlags flags)
+{
+ int i;
+ GnmFuncEvalInfo fei;
+ GnmDependentFlags flag;
+ DepLinkFlags pass = (flags & DEP_LINK_LINK);
+
+ gnm_func_load_if_stub (call->func);
+ fei.pos = ep;
+ fei.func_call = call;
+ flag = gnm_func_link_dep (call->func, &fei, (flags & DEP_LINK_LINK) != 0);
+ if (flag & DEPENDENT_IGNORE_ARGS)
+ return flag; // ??? Mask out DEPENDENT_IGNORE_ARGS?
+
+ for (i = 0; i < call->argc; i++) {
+ char t = gnm_func_get_arg_type (call->func, i);
+ DepLinkFlags extra =
+ (t == 'A' || t == 'r' || t == '?')
+ ? DEP_LINK_NON_SCALAR
+ : 0;
+
+ flag |= link_unlink_expr_dep (ep, call->argv[i], pass | extra);
+ }
+ return flag;
+}
+
+
+static GnmDependentFlags
+link_unlink_expr_dep (GnmEvalPos *ep, GnmExpr const *tree, DepLinkFlags flags)
{
g_return_val_if_fail (tree != NULL, DEPENDENT_NO_FLAG);
@@ -1089,52 +1187,26 @@ link_unlink_expr_dep (GnmEvalPos *ep, GnmExpr const *tree, gboolean qlink)
case GNM_EXPR_OP_RANGE_CTOR: /* See #562363 */
case GNM_EXPR_OP_INTERSECT:
case GNM_EXPR_OP_ANY_BINARY:
- return link_unlink_expr_dep (ep, tree->binary.value_a, qlink) |
- link_unlink_expr_dep (ep, tree->binary.value_b, qlink);
+ return link_unlink_expr_dep (ep, tree->binary.value_a, flags) |
+ link_unlink_expr_dep (ep, tree->binary.value_b, flags);
case GNM_EXPR_OP_ANY_UNARY:
- return link_unlink_expr_dep (ep, tree->unary.value, qlink);
+ return link_unlink_expr_dep (ep, tree->unary.value, flags);
case GNM_EXPR_OP_CELLREF:
- return link_unlink_single_dep (ep->dep, dependent_pos (ep->dep), &tree->cellref.ref, qlink);
+ return link_unlink_single_dep (ep->dep, dependent_pos (ep->dep), &tree->cellref.ref, flags);
case GNM_EXPR_OP_CONSTANT:
- /* TODO: pass in eval flags so that we can use implicit
- * intersection
- */
- if (VALUE_IS_CELLRANGE (tree->constant.value))
- return link_unlink_cellrange_dep
- (ep->dep, dependent_pos (ep->dep),
- &tree->constant.value->v_range.cell.a,
- &tree->constant.value->v_range.cell.b,
- qlink);
- return DEPENDENT_NO_FLAG;
-
- /*
- * FIXME: needs to be taught implicit intersection +
- * more cunning handling of argument type matching.
- */
- case GNM_EXPR_OP_FUNCALL: {
- int i;
- GnmFuncEvalInfo fei;
- GnmDependentFlags flag;
+ return link_unlink_constant (ep, tree->constant.value, flags);
- gnm_func_load_if_stub (tree->func.func);
- fei.pos = ep;
- fei.func_call = &tree->func;
- flag = gnm_func_link_dep (tree->func.func, &fei, qlink);
-
- if (!(flag & DEPENDENT_IGNORE_ARGS))
- for (i = 0; i < tree->func.argc; i++)
- flag |= link_unlink_expr_dep (ep, tree->func.argv[i], qlink);
- return flag;
- }
+ case GNM_EXPR_OP_FUNCALL:
+ return link_unlink_funcall (ep, &tree->func, flags);
case GNM_EXPR_OP_NAME:
- if (qlink)
+ if (flags & DEP_LINK_LINK)
expr_name_add_dep (tree->name.name, ep->dep);
else
expr_name_remove_dep (tree->name.name, ep->dep);
if (expr_name_is_active (tree->name.name))
- return link_unlink_expr_dep (ep, tree->name.name->texpr->expr, qlink) |
DEPENDENT_USES_NAME;
+ return link_unlink_expr_dep (ep, tree->name.name->texpr->expr, flags) |
DEPENDENT_USES_NAME;
return DEPENDENT_USES_NAME;
case GNM_EXPR_OP_ARRAY_ELEM: {
@@ -1150,7 +1222,7 @@ link_unlink_expr_dep (GnmEvalPos *ep, GnmExpr const *tree, gboolean qlink)
a.col = pos->col - tree->array_elem.x;
a.row = pos->row - tree->array_elem.y;
- return link_unlink_single_dep (ep->dep, pos, &a, qlink);
+ return link_unlink_single_dep (ep->dep, pos, &a, flags);
}
case GNM_EXPR_OP_ARRAY_CORNER: {
@@ -1160,15 +1232,20 @@ link_unlink_expr_dep (GnmEvalPos *ep, GnmExpr const *tree, gboolean qlink)
GnmEvalPos pos = *ep;
pos.array_texpr = cell->base.texpr;
/* Corner cell depends on the contents of the expr */
- return link_unlink_expr_dep (&pos, tree->array_corner.expr, qlink);
+ return link_unlink_expr_dep (&pos, tree->array_corner.expr,
+ flags | DEP_LINK_NON_SCALAR);
}
case GNM_EXPR_OP_SET: {
int i;
GnmDependentFlags res = DEPENDENT_NO_FLAG;
- for (i = 0; i < tree->set.argc; i++)
- res |= link_unlink_expr_dep (ep, tree->set.argv[i], qlink);
+ // gnm_expr_eval ignores arguments unless non-scalar
+ if (flags & DEP_LINK_NON_SCALAR) {
+ for (i = 0; i < tree->set.argc; i++)
+ res |= link_unlink_expr_dep (ep, tree->set.argv[i], flags);
+ }
+
return res;
}
#ifndef DEBUG_SWITCH_ENUM
@@ -1540,7 +1617,7 @@ dependent_link (GnmDependent *dep)
sheet->deps->tail = dep;
dep->flags |= DEPENDENT_IS_LINKED |
link_unlink_expr_dep (eval_pos_init_dep (&ep, dep),
- dep->texpr->expr, TRUE);
+ dep->texpr->expr, DEP_LINK_LINK);
if (dep->flags & DEPENDENT_HAS_3D)
workbook_link_3d_dep (dep);
@@ -1565,7 +1642,7 @@ dependent_unlink (GnmDependent *dep)
g_return_if_fail (IS_SHEET (dep->sheet));
link_unlink_expr_dep (eval_pos_init_dep (&ep, dep),
- dep->texpr->expr, FALSE);
+ dep->texpr->expr, DEP_LINK_UNLINK);
contain = dep->sheet->deps;
if (contain != NULL) {
if (contain->head == dep)
diff --git a/src/expr.c b/src/expr.c
index 1331533d3..03e3986d2 100644
--- a/src/expr.c
+++ b/src/expr.c
@@ -848,6 +848,8 @@ handle_empty (GnmValue *res, GnmExprEvalFlags flags)
*
* Always release the value passed in.
*
+ * NOTE: This should match link_unlink_constant.
+ *
* Return value:
* If the intersection succeeded return a duplicate of the value
* at the intersection point. This value needs to be freed.
@@ -883,20 +885,21 @@ value_intersection (GnmValue *v, GnmEvalPos const *pos)
col = r.start.col;
row = r.start.row;
found = TRUE;
- } else if (r.start.row == r.end.row) {
- if (r.start.col <= col && col <= r.end.col) {
- row = r.start.row;
- found = TRUE;
- } else if (r.start.col == r.end.col) {
- col = r.start.col;
- row = r.start.row;
- found = TRUE;
- }
- } else if (r.start.col == r.end.col) {
- if (r.start.row <= row && row <= r.end.row) {
- col = r.start.col;
- found = TRUE;
- }
+ } else if (range_is_singleton (&r)) {
+ // A single cell
+ col = r.start.col;
+ row = r.start.row;
+ found = TRUE;
+ } else if (r.start.row == r.end.row &&
+ r.start.col <= col && col <= r.end.col) {
+ // A horizontal sliver
+ row = r.start.row;
+ found = TRUE;
+ } else if (r.start.col == r.end.col &&
+ r.start.row <= row && row <= r.end.row) {
+ // A vertical sliver
+ col = r.start.col;
+ found = TRUE;
}
if (found) {
GnmCell *cell = sheet_cell_get (
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]