1 /*
2  * exp-deriv.c : Expression derivation
3  *
4  * Copyright (C) 2016 Morten Welinder (terra@gnome.org)
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License as
8  * published by the Free Software Foundation; either version 2 of the
9  * License, or (at your option) version 3.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301
19  * USA
20  */
21 #include <gnumeric-config.h>
22 #include <glib/gi18n-lib.h>
23 #include <gnumeric.h>
24 #include <expr-deriv.h>
25 #include <expr-impl.h>
26 #include <func.h>
27 #include <value.h>
28 #include <sheet.h>
29 #include <workbook.h>
30 #include <cell.h>
31 #include <gutils.h>
32 
33 /* ------------------------------------------------------------------------- */
34 
35 struct GnmExprDeriv_ {
36 	unsigned ref_count;
37 	GnmEvalPos var;
38 };
39 
40 /**
41  * gnm_expr_deriv_info_new:
42  *
43  * Returns: (transfer full): A new #GnmExprDeriv.
44  */
45 GnmExprDeriv *
gnm_expr_deriv_info_new(void)46 gnm_expr_deriv_info_new (void)
47 {
48 	GnmExprDeriv *res = g_new0 (GnmExprDeriv, 1);
49 	res->ref_count = 1;
50 	return res;
51 }
52 
53 /**
54  * gnm_expr_deriv_info_unref:
55  * @deriv: (transfer full) (nullable): #GnmExprDeriv
56  */
57 void
gnm_expr_deriv_info_unref(GnmExprDeriv * deriv)58 gnm_expr_deriv_info_unref (GnmExprDeriv *deriv)
59 {
60 	if (!deriv || deriv->ref_count-- > 1)
61 		return;
62 	g_free (deriv);
63 }
64 
65 /**
66  * gnm_expr_deriv_info_ref:
67  * @deriv: (transfer none) (nullable): #GnmExprDeriv
68  *
69  * Returns: (transfer full) (nullable): a new reference to @deriv.
70  */
71 GnmExprDeriv *
gnm_expr_deriv_info_ref(GnmExprDeriv * deriv)72 gnm_expr_deriv_info_ref (GnmExprDeriv *deriv)
73 {
74 	if (deriv)
75 		deriv->ref_count++;
76 	return deriv;
77 }
78 
79 GType
gnm_expr_deriv_info_get_type(void)80 gnm_expr_deriv_info_get_type (void)
81 {
82 	static GType t = 0;
83 
84 	if (t == 0) {
85 		t = g_boxed_type_register_static ("GnmExprDeriv",
86 			 (GBoxedCopyFunc)gnm_expr_deriv_info_ref,
87 			 (GBoxedFreeFunc)gnm_expr_deriv_info_unref);
88 	}
89 	return t;
90 }
91 
92 /**
93  * gnm_expr_deriv_info_set_var:
94  * @deriv: #GnmExprDeriv
95  * @var: (transfer none): location of variable
96  */
97 void
gnm_expr_deriv_info_set_var(GnmExprDeriv * deriv,GnmEvalPos const * var)98 gnm_expr_deriv_info_set_var (GnmExprDeriv *deriv, GnmEvalPos const *var)
99 {
100 	deriv->var = *var;
101 }
102 
103 /* ------------------------------------------------------------------------- */
104 
105 static GnmExpr const *
gnm_value_deriv(GnmValue const * v)106 gnm_value_deriv (GnmValue const *v)
107 {
108 	if (VALUE_IS_NUMBER (v))
109 		return gnm_expr_new_constant (value_new_float (0));
110 	else
111 		return NULL;
112 }
113 
114 static GnmExpr const *madd (GnmExpr const *l, gboolean copyl, GnmExpr const *r, gboolean copyr);
115 static GnmExpr const *msub (GnmExpr const *l, gboolean copyl, GnmExpr const *r, gboolean copyr);
116 static GnmExpr const *mmul (GnmExpr const *l, gboolean copyl, GnmExpr const *r, gboolean copyr);
117 static GnmExpr const *mdiv (GnmExpr const *l, gboolean copyl, GnmExpr const *r, gboolean copyr);
118 static GnmExpr const *mneg (GnmExpr const *l, gboolean copyl);
119 static GnmExpr const *optimize_sum (GnmExpr const *e);
120 
121 
122 
123 
124 static gboolean
is_any_const(GnmExpr const * e,gnm_float * c)125 is_any_const (GnmExpr const *e, gnm_float *c)
126 {
127 	GnmValue const *v = gnm_expr_get_constant (e);
128 	if (v && VALUE_IS_FLOAT (v)) {
129 		if (c) *c = value_get_as_float (v);
130 		return TRUE;
131 	} else
132 		return FALSE;
133 }
134 
135 static gboolean
is_const(GnmExpr const * e,gnm_float c)136 is_const (GnmExpr const *e, gnm_float c)
137 {
138 	GnmValue const *v = gnm_expr_get_constant (e);
139 	return v && VALUE_IS_FLOAT (v) && value_get_as_float (v) == c;
140 }
141 
142 static gboolean
is_neg(GnmExpr const * e)143 is_neg (GnmExpr const *e)
144 {
145 	return (GNM_EXPR_GET_OPER (e) == GNM_EXPR_OP_UNARY_NEG);
146 }
147 
148 static gboolean
is_lcmul(GnmExpr const * e,gnm_float * c)149 is_lcmul (GnmExpr const *e, gnm_float *c)
150 {
151 	return (GNM_EXPR_GET_OPER (e) == GNM_EXPR_OP_MULT &&
152 		is_any_const (e->binary.value_a, c));
153 }
154 
155 
156 // Optimizing constructor for "+".  Takes ownership of "l" and "r"
157 // if the corresponding "copy" argument is false.
158 //
159 // Note, that this plays fast-and-loose with semantics when errors are
160 // involved.
161 static GnmExpr const *
madd(GnmExpr const * l,gboolean copyl,GnmExpr const * r,gboolean copyr)162 madd (GnmExpr const *l, gboolean copyl, GnmExpr const *r, gboolean copyr)
163 {
164 	if (is_const (l, 0)) {
165 		if (!copyl) gnm_expr_free (l);
166 		if (copyr) r = gnm_expr_copy (r);
167 		return r;
168 	}
169 
170 	if (is_const (r, 0)) {
171 		if (!copyr) gnm_expr_free (r);
172 		if (copyl) l = gnm_expr_copy (l);
173 		return l;
174 	}
175 
176 	if (copyl) l = gnm_expr_copy (l);
177 	if (copyr) r = gnm_expr_copy (r);
178 	return gnm_expr_new_binary (l, GNM_EXPR_OP_ADD, r);
179 }
180 
181 // Optimizing constructor for unary "-".  Takes ownership of "l"
182 // if the corresponding "copy" argument is false.
183 //
184 // Note, that this plays fast-and-loose with semantics when errors are
185 // involved.
186 static GnmExpr const *
mneg(GnmExpr const * l,gboolean copyl)187 mneg (GnmExpr const *l, gboolean copyl)
188 {
189 	gnm_float x;
190 	if (is_any_const (l, &x)) {
191 		if (!copyl) gnm_expr_free (l);
192 		return gnm_expr_new_constant (value_new_float (-x));
193 	}
194 
195 	if (is_lcmul (l, &x)) {
196 		GnmExpr const *res = mmul (gnm_expr_new_constant (value_new_float (-x)), 0,
197 					   l->binary.value_b, 1);
198 		if (!copyl) gnm_expr_free (l);
199 		return res;
200 	}
201 
202 	if (copyl) l = gnm_expr_copy (l);
203 	return gnm_expr_new_unary (GNM_EXPR_OP_UNARY_NEG, l);
204 }
205 
206 // Optimizing constructor for "-".  Takes ownership of "l" and "r"
207 // if the corresponding "copy" argument is false.
208 //
209 // Note, that this plays fast-and-loose with semantics when errors are
210 // involved.
211 static GnmExpr const *
msub(GnmExpr const * l,gboolean copyl,GnmExpr const * r,gboolean copyr)212 msub (GnmExpr const *l, gboolean copyl, GnmExpr const *r, gboolean copyr)
213 {
214 	if (is_const (r, 0)) {
215 		if (!copyr) gnm_expr_free (r);
216 		if (copyl) l = gnm_expr_copy (l);
217 		return l;
218 	}
219 
220 	if (is_const (l, 0)) {
221 		if (!copyl) gnm_expr_free (l);
222 		return mneg (r, copyr);
223 	}
224 
225 	if (copyl) l = gnm_expr_copy (l);
226 	if (copyr) r = gnm_expr_copy (r);
227 	return gnm_expr_new_binary (l, GNM_EXPR_OP_SUB, r);
228 }
229 
230 // Optimizing constructor for "*".  Takes ownership of "l" and "r"
231 // if the corresponding "copy" argument is false.
232 //
233 // Note, that this plays fast-and-loose with semantics when errors are
234 // involved.
235 static GnmExpr const *
mmul(GnmExpr const * l,gboolean copyl,GnmExpr const * r,gboolean copyr)236 mmul (GnmExpr const *l, gboolean copyl, GnmExpr const *r, gboolean copyr)
237 {
238 	if (is_const (l, 1) || is_const (r, 0)) {
239 		if (!copyl) gnm_expr_free (l);
240 		if (copyr) r = gnm_expr_copy (r);
241 		return r;
242 	}
243 
244 	if (is_const (l, 0) || is_const (r, 1)) {
245 		if (!copyr) gnm_expr_free (r);
246 		if (copyl) l = gnm_expr_copy (l);
247 		return l;
248 	}
249 
250 	if (is_const (l, -1)) {
251 		if (!copyl) gnm_expr_free (l);
252 		return mneg (r, copyr);
253 	}
254 
255 	if (is_neg (l)) {
256 		GnmExpr const *res = mneg (mmul (l->unary.value, 1, r, copyr), 0);
257 		if (!copyl) gnm_expr_free (l);
258 		return res;
259 	}
260 
261 	if (is_neg (r)) {
262 		GnmExpr const *res = mneg (mmul (l, copyl, r->unary.value, 1), 0);
263 		if (!copyr) gnm_expr_free (r);
264 		return res;
265 	}
266 
267 	if (is_lcmul (l, NULL)) {
268 		GnmExpr const *res = mmul (l->binary.value_a, 1,
269 					   mmul (l->binary.value_b, 1,
270 						 r, copyr), 0);
271 		if (!copyl) gnm_expr_free (l);
272 		return res;
273 	}
274 
275 	if (copyl) l = gnm_expr_copy (l);
276 	if (copyr) r = gnm_expr_copy (r);
277 	return gnm_expr_new_binary (l, GNM_EXPR_OP_MULT, r);
278 }
279 
280 // Optimizing constructor for "/".  Takes ownership of "l" and "r"
281 // if the corresponding "copy" argument is false.
282 //
283 // Note, that this plays fast-and-loose with semantics when errors are
284 // involved.
285 static GnmExpr const *
mdiv(GnmExpr const * l,gboolean copyl,GnmExpr const * r,gboolean copyr)286 mdiv (GnmExpr const *l, gboolean copyl, GnmExpr const *r, gboolean copyr)
287 {
288 	if (is_const (l, 0) || is_const (r, 1)) {
289 		if (!copyr) gnm_expr_free (r);
290 		if (copyl) l = gnm_expr_copy (l);
291 		return l;
292 	}
293 
294 	if (copyl) l = gnm_expr_copy (l);
295 	if (copyr) r = gnm_expr_copy (r);
296 	return gnm_expr_new_binary (l, GNM_EXPR_OP_DIV, r);
297 }
298 
299 // Optimizing constructor for "^".  Takes ownership of "l" and "r"
300 // if the corresponding "copy" argument is false.
301 //
302 // Note, that this plays fast-and-loose with semantics when errors are
303 // involved.
304 static GnmExpr const *
mexp(GnmExpr const * l,gboolean copyl,GnmExpr const * r,gboolean copyr)305 mexp (GnmExpr const *l, gboolean copyl, GnmExpr const *r, gboolean copyr)
306 {
307 	if (is_const (r, 1)) {
308 		if (!copyr) gnm_expr_free (r);
309 		if (copyl) l = gnm_expr_copy (l);
310 		return l;
311 	}
312 
313 	if (copyl) l = gnm_expr_copy (l);
314 	if (copyr) r = gnm_expr_copy (r);
315 	return gnm_expr_new_binary (l, GNM_EXPR_OP_EXP, r);
316 }
317 
318 static GnmExpr const *
msum(GnmExprList * as)319 msum (GnmExprList *as)
320 {
321 	GnmFunc *fsum = gnm_func_lookup_or_add_placeholder ("SUM");
322 	GnmExpr const *res = gnm_expr_new_funcall (fsum, as);
323 	GnmExpr const *opt = optimize_sum (res);
324 
325 	if (opt) {
326 		gnm_expr_free (res);
327 		res = opt;
328 	}
329 
330 	return res;
331 }
332 
333 static GnmExpr const *
optimize_sum(GnmExpr const * e)334 optimize_sum (GnmExpr const *e)
335 {
336 	int argc = e->func.argc;
337 	GnmExprConstPtr *argv = e->func.argv;
338 	gboolean all_neg = (argc > 0);
339 	gboolean all_lcmul = (argc > 0);
340 	gnm_float cl = 0;
341 	int i;
342 
343 	for (i = 0; i < argc; i++) {
344 		GnmExpr const *a = argv[i];
345 		gnm_float x;
346 
347 		all_neg = all_neg && is_neg (a);
348 
349 		all_lcmul = all_lcmul &&
350 			is_lcmul (a, i ? &x : &cl) &&
351 			(i == 0 || cl == x);
352 	}
353 
354 	if (all_neg) {
355 		GnmExprList *as = NULL;
356 		for (i = argc; i-- > 0;) {
357 			GnmExpr const *a = argv[i];
358 			as = g_slist_prepend (as, (gpointer)gnm_expr_copy (a->unary.value));
359 		}
360 		return mneg (msum (as), 0);
361 	}
362 
363 	if (all_lcmul) {
364 		GnmExprList *as = NULL;
365 		for (i = argc; i-- > 0;) {
366 			GnmExpr const *a = argv[i];
367 			as = g_slist_prepend (as, (gpointer)gnm_expr_copy (a->binary.value_b));
368 		}
369 		return mmul (gnm_expr_new_constant (value_new_float (cl)), 0,
370 			     msum (as), 0);
371 	}
372 
373 	return NULL;
374 }
375 
376 static GnmExpr const *
optimize(GnmExpr const * e)377 optimize (GnmExpr const *e)
378 {
379 	GnmExprOp op = GNM_EXPR_GET_OPER (e);
380 
381 	switch (op) {
382 	case GNM_EXPR_OP_FUNCALL: {
383 		GnmFunc *f = gnm_expr_get_func_def (e);
384 		GnmFunc *fsum = gnm_func_lookup_or_add_placeholder ("SUM");
385 
386 		if (f == fsum)
387 			return optimize_sum (e);
388 		return NULL;
389 	}
390 	default:
391 		return NULL;
392 	}
393 }
394 
395 /* ------------------------------------------------------------------------- */
396 
397 struct cb_arg_collect {
398 	GnmExprList *args;
399 	GnmCellRef const *cr0;
400 	GnmEvalPos const *ep;
401 };
402 
403 static GnmValue *
cb_arg_collect(GnmCellIter const * iter,gpointer user_)404 cb_arg_collect (GnmCellIter const *iter, gpointer user_)
405 {
406 	struct cb_arg_collect *user = user_;
407 	GnmCell const *cell = iter->cell;
408 	GnmCellRef cr;
409 	GnmParsePos pp;
410 	gnm_cellref_init (&cr, user->cr0->sheet,
411 			  cell->pos.col, cell->pos.row,
412 			  FALSE);
413 	parse_pos_init_evalpos (&pp, user->ep);
414 	gnm_cellref_set_col_ar (&cr, &pp, user->cr0->col_relative);
415 	gnm_cellref_set_row_ar (&cr, &pp, user->cr0->row_relative);
416 	user->args = gnm_expr_list_prepend
417 		(user->args,
418 		 (gpointer)gnm_expr_new_cellref (&cr));
419 	return NULL;
420 }
421 
422 /**
423  * gnm_expr_deriv_collect:
424  * @expr: expression
425  * @ep: evaluation position
426  * @info: extra information, not currently used
427  *
428  * Returns: (type GSList) (transfer full) (element-type GnmExpr): list of
429  * expressions expanded from @expr
430  */
431 GnmExprList *
gnm_expr_deriv_collect(GnmExpr const * expr,GnmEvalPos const * ep,GnmExprDeriv * info)432 gnm_expr_deriv_collect (GnmExpr const *expr,
433 			GnmEvalPos const *ep,
434 			GnmExprDeriv *info)
435 {
436 	struct cb_arg_collect user;
437 	int i;
438 
439 	user.args = NULL;
440 	user.ep = ep;
441 	for (i = 0; i < expr->func.argc; i++) {
442 		GnmExpr const *e = expr->func.argv[i];
443 		GnmValue const *v = gnm_expr_get_constant (e);
444 
445 		if (!v || !VALUE_IS_CELLRANGE (v)) {
446 			user.args = gnm_expr_list_prepend
447 				(user.args, (gpointer)gnm_expr_copy (e));
448 			continue;
449 		}
450 
451 		user.cr0 = &value_get_rangeref (v)->a;
452 		workbook_foreach_cell_in_range (ep, v,
453 						CELL_ITER_IGNORE_BLANK,
454 						cb_arg_collect,
455 						&user);
456 	}
457 
458 	return g_slist_reverse (user.args);
459 }
460 
461 /* ------------------------------------------------------------------------- */
462 
463 #define MAYBE_FREE(e) do { if (e) gnm_expr_free (e); } while (0)
464 
465 #define COMMON_BINARY_START						\
466 	GnmExpr const *a = expr->binary.value_a; /* Not owned */	\
467 	GnmExpr const *da = gnm_expr_deriv (a, ep, info);		\
468 	GnmExpr const *b = expr->binary.value_b; /* Not owned */	\
469 	GnmExpr const *db = gnm_expr_deriv (b, ep, info);		\
470 	if (!da || !db) {						\
471 		MAYBE_FREE (da);					\
472 		MAYBE_FREE (db);					\
473 		return NULL;						\
474 	} else {
475 
476 #define COMMON_BINARY_END }
477 
478 /**
479  * gnm_expr_deriv:
480  * @expr: #GnmExpr
481  * @ep: position for @expr
482  * @info: Derivative information
483  *
484  * Returns: (transfer full) (nullable): the derivative of @expr with respect
485  * to @info.
486  */
487 GnmExpr const *
gnm_expr_deriv(GnmExpr const * expr,GnmEvalPos const * ep,GnmExprDeriv * info)488 gnm_expr_deriv (GnmExpr const *expr,
489 		GnmEvalPos const *ep,
490 		GnmExprDeriv *info)
491 {
492 	GnmExprOp op = GNM_EXPR_GET_OPER (expr);
493 
494 	switch (op) {
495 	case GNM_EXPR_OP_RANGE_CTOR:
496 	case GNM_EXPR_OP_INTERSECT:
497 	case GNM_EXPR_OP_NAME:
498 	case GNM_EXPR_OP_ARRAY_CORNER:
499 	case GNM_EXPR_OP_ARRAY_ELEM:
500 	case GNM_EXPR_OP_SET:
501 
502 	case GNM_EXPR_OP_EQUAL:
503 	case GNM_EXPR_OP_GT:
504 	case GNM_EXPR_OP_LT:
505 	case GNM_EXPR_OP_GTE:
506 	case GNM_EXPR_OP_LTE:
507 	case GNM_EXPR_OP_NOT_EQUAL:
508 	case GNM_EXPR_OP_CAT:
509 	case GNM_EXPR_OP_PERCENTAGE:
510 		// Bail
511 		return NULL;
512 
513 	case GNM_EXPR_OP_PAREN:
514 	case GNM_EXPR_OP_UNARY_PLUS:
515 		return gnm_expr_deriv (expr->unary.value, ep, info);
516 
517 	case GNM_EXPR_OP_UNARY_NEG: {
518 		GnmExpr const *d = gnm_expr_deriv (expr->unary.value, ep, info);
519 		return d ? mneg (d, 0) : NULL;
520 	}
521 
522 	case GNM_EXPR_OP_ADD: {
523 		COMMON_BINARY_START
524 		return madd (da, 0, db, 0);
525 		COMMON_BINARY_END
526 	}
527 
528 	case GNM_EXPR_OP_SUB: {
529 		COMMON_BINARY_START
530 		return msub (da, 0, db, 0);
531 		COMMON_BINARY_END
532 	}
533 
534 	case GNM_EXPR_OP_MULT: {
535 		COMMON_BINARY_START
536 		GnmExpr const *t1 = mmul (da, 0, b, 1);
537 		GnmExpr const *t2 = mmul (a, 1, db, 0);
538 		return madd (t1, 0, t2, 0);
539 		COMMON_BINARY_END
540 	}
541 
542 	case GNM_EXPR_OP_DIV: {
543 		COMMON_BINARY_START
544 		GnmExpr const *t1 = mmul (da, 0, b, 1);
545 		GnmExpr const *t2 = mmul (a, 1, db, 0);
546 		GnmExpr const *d = msub (t1, 0, t2, 0);
547 		GnmExpr const *n = mmul (b, 1, b, 1);
548 		return mdiv (d, 0, n, 0);
549 		COMMON_BINARY_END
550 	}
551 
552 	case GNM_EXPR_OP_EXP: {
553 		COMMON_BINARY_START
554 		GnmFunc *fln = gnm_func_lookup ("ln", NULL);
555 		gnm_float cb;
556 		if (is_any_const (b, &cb)) {
557 			GnmExpr const *bm1 = gnm_expr_new_constant (value_new_float (cb - 1));
558 			GnmExpr const *t1 = mexp (a, 1, bm1, 0);
559 			gnm_expr_free (db);
560 			return mmul (mmul (b, 1, t1, 0), 0, da, 0);
561 		} else if (fln) {
562 			// a^b = exp(b*log(a))
563 			// (a^b)' = a^b * (a'*b/a + b'*ln(a))
564 			GnmExpr const *t1 = mdiv (mmul (da, 0, b, 1), 0, a, 1);
565 			GnmExpr const *t2 = mmul
566 				(db, 0,
567 				 gnm_expr_new_funcall1 (fln, gnm_expr_copy (a)), 0);
568 			GnmExpr const *s = madd (t1, 0, t2, 0);
569 			return mmul (expr, 1, s, 0);
570 		} else {
571 		        gnm_expr_free (da);
572 			gnm_expr_free (db);
573 			return NULL;
574 		}
575 		COMMON_BINARY_END
576 	}
577 
578 	case GNM_EXPR_OP_FUNCALL: {
579 		GnmFunc *f = gnm_expr_get_func_def (expr);
580 		GnmExpr const *res = gnm_func_derivative (f, expr, ep, info);
581 		GnmExpr const *opt = res ? optimize (res) : NULL;
582 		if (opt) {
583 			gnm_expr_free (res);
584 			res = opt;
585 		}
586 		return res;
587 	}
588 
589 	case GNM_EXPR_OP_CONSTANT:
590 		return gnm_value_deriv (expr->constant.value);
591 
592 	case GNM_EXPR_OP_CELLREF: {
593 		GnmCellRef r;
594 		Sheet *sheet;
595 		GnmCell *cell;
596 		GnmEvalPos ep2;
597 		GnmExpr const *res;
598 		GnmExprTop const *texpr;
599 		GnmExprTop const *texpr2;
600 		GnmExprRelocateInfo rinfo;
601 
602 		gnm_cellref_make_abs (&r, &expr->cellref.ref, ep);
603 		sheet = eval_sheet (r.sheet, ep->sheet);
604 
605 		if (sheet == info->var.sheet &&
606 		    r.col == info->var.eval.col &&
607 		    r.row == info->var.eval.row)
608 			return gnm_expr_new_constant (value_new_float (1));
609 
610 		cell = sheet_cell_get (sheet, r.col, r.row);
611 		if (!cell)
612 			return gnm_expr_new_constant (value_new_float (0));
613 		if (!gnm_cell_has_expr (cell))
614 			return gnm_value_deriv (cell->value);
615 
616 		eval_pos_init_cell (&ep2, cell);
617 		res = gnm_expr_deriv (cell->base.texpr->expr, &ep2, info);
618 		if (!res)
619 			return NULL;
620 
621 		// The just-computed derivative is relative to the wrong
622 		// position.
623 
624 		texpr = gnm_expr_top_new (res);
625 		parse_pos_init_evalpos (&rinfo.pos, &ep2);
626 		rinfo.reloc_type = GNM_EXPR_RELOCATE_MOVE_RANGE;
627 		rinfo.origin.start = rinfo.origin.end = ep2.eval;
628 		rinfo.origin_sheet = ep2.sheet;
629 		rinfo.target_sheet = ep->sheet;
630 		rinfo.col_offset = ep->eval.col - ep2.eval.col;
631 		rinfo.row_offset = ep->eval.row - ep2.eval.row;
632 		texpr2 = gnm_expr_top_relocate (texpr, &rinfo, FALSE);
633 
634 		if (texpr2) {
635 			res = gnm_expr_copy (texpr2->expr);
636 			gnm_expr_top_unref (texpr2);
637 		} else {
638 			res = gnm_expr_copy (texpr->expr);
639 		}
640 		gnm_expr_top_unref (texpr);
641 
642 		return res;
643 	}
644 
645 #ifndef DEBUG_SWITCH_ENUM
646 	default:
647 		g_assert_not_reached ();
648 		break;
649 #endif
650 	}
651 }
652 
653 /* ------------------------------------------------------------------------- */
654 
655 /**
656  * gnm_expr_top_deriv:
657  * @texpr: Expression
658  * @ep: Evaluation position
659  * @info: Derivative information
660  *
661  * Returns: (transfer full) (nullable): The derivative of @texpr with
662  * respect to @info.
663  */
664 GnmExprTop const *
gnm_expr_top_deriv(GnmExprTop const * texpr,GnmEvalPos const * ep,GnmExprDeriv * info)665 gnm_expr_top_deriv (GnmExprTop const *texpr,
666 		    GnmEvalPos const *ep,
667 		    GnmExprDeriv *info)
668 {
669 	GnmExpr const *expr;
670 
671 	g_return_val_if_fail (GNM_IS_EXPR_TOP (texpr), NULL);
672 	g_return_val_if_fail (ep != NULL, NULL);
673 	g_return_val_if_fail (info != NULL, NULL);
674 
675 	expr = gnm_expr_deriv (texpr->expr, ep, info);
676 	if (gnm_debug_flag ("deriv")) {
677 		GnmParsePos pp, ppvar;
678 		char *s;
679 		Sheet *sheet = ep->sheet;
680 		GnmConventions const *convs = sheet_get_conventions (sheet);
681 
682 		parse_pos_init_evalpos (&ppvar, &info->var);
683 		parse_pos_init_evalpos (&pp, ep);
684 
685 		s = gnm_expr_top_as_string (texpr, &pp, convs);
686 		g_printerr ("Derivative of %s with respect to %s:%s",
687 			    s, parsepos_as_string (&ppvar),
688 			    expr ? "\n" : " cannot compute.\n");
689 		g_free (s);
690 		if (expr) {
691 			s = gnm_expr_as_string (expr, &pp, convs);
692 			g_printerr ("%s\n\n", s);
693 			g_free (s);
694 		}
695 	}
696 
697 	return gnm_expr_top_new (expr);
698 }
699 
700 /**
701  * gnm_expr_cell_deriv:
702  * @y: Result cell
703  * @x: Variable cell
704  *
705  * Returns: (transfer full) (nullable): The derivative of cell @y with
706  * respect to cell @x.
707  */
708 GnmExprTop const *
gnm_expr_cell_deriv(GnmCell * y,GnmCell * x)709 gnm_expr_cell_deriv (GnmCell *y, GnmCell *x)
710 {
711 	GnmExprTop const *res;
712 	GnmEvalPos ep, var;
713 	GnmExprDeriv *info;
714 
715 	g_return_val_if_fail (y != NULL, NULL);
716 	g_return_val_if_fail (gnm_cell_has_expr (y), NULL);
717 	g_return_val_if_fail (x != NULL, NULL);
718 
719 	eval_pos_init_cell (&ep, y);
720 
721 	info = gnm_expr_deriv_info_new ();
722 	eval_pos_init_cell (&var, x);
723 	gnm_expr_deriv_info_set_var (info, &var);
724 
725 	res = gnm_expr_top_deriv (y->base.texpr, &ep, info);
726 
727 	gnm_expr_deriv_info_unref (info);
728 
729 	return res;
730 }
731 
732 /**
733  * gnm_expr_cell_deriv_value:
734  * @y: Result cell
735  * @x: Variable cell
736  *
737  * Returns: The derivative of cell @y with respect to cell @x at the
738  * current value of @x.  Returns NaN on error.
739  */
740 gnm_float
gnm_expr_cell_deriv_value(GnmCell * y,GnmCell * x)741 gnm_expr_cell_deriv_value (GnmCell *y, GnmCell *x)
742 {
743 	GnmExprTop const *dydx;
744 	GnmValue *v;
745 	gnm_float res;
746 	GnmEvalPos ep;
747 
748 	g_return_val_if_fail (y != NULL, gnm_nan);
749 	g_return_val_if_fail (x != NULL, gnm_nan);
750 
751 	dydx = gnm_expr_cell_deriv (y, x);
752 	if (!dydx)
753 		return gnm_nan;
754 
755 	eval_pos_init_cell (&ep, y);
756 	v = gnm_expr_top_eval (dydx, &ep, GNM_EXPR_EVAL_SCALAR_NON_EMPTY);
757 	res = VALUE_IS_NUMBER (v) ? value_get_as_float (v) : gnm_nan;
758 
759 	value_release (v);
760 	gnm_expr_top_unref (dydx);
761 
762 	return res;
763 }
764 
765 /* ------------------------------------------------------------------------- */
766 
767 /**
768  * gnm_expr_deriv_chain:
769  * @expr: #GnmExpr for a function call with one argument
770  * @deriv: (transfer full) (nullable): Derivative of @expr's function.
771  * @ep: position for @expr
772  * @info: Derivative information
773  *
774  * Applies the chain rule to @expr.
775  *
776  * Returns: (transfer full) (nullable): the derivative of @expr with respect
777  * to @info.
778  */
779 GnmExpr const *
gnm_expr_deriv_chain(GnmExpr const * expr,GnmExpr const * deriv,GnmEvalPos const * ep,GnmExprDeriv * info)780 gnm_expr_deriv_chain (GnmExpr const *expr,
781 		      GnmExpr const *deriv,
782 		      GnmEvalPos const *ep,
783 		      GnmExprDeriv *info)
784 {
785 	GnmExpr const *deriv2;
786 
787 	if (!deriv)
788 		return NULL;
789 
790 	deriv2 = gnm_expr_deriv (gnm_expr_get_func_arg (expr, 0), ep, info);
791 	if (!deriv2) {
792 		gnm_expr_free (deriv);
793 		return NULL;
794 	}
795 
796 	return mmul (deriv, 0, deriv2, 0);
797 }
798 
799 /* ------------------------------------------------------------------------- */
800 
801 void
gnm_expr_deriv_shutdown_(void)802 gnm_expr_deriv_shutdown_ (void)
803 {
804 }
805 
806 /* ------------------------------------------------------------------------- */
807