Commit 03f01e38 authored by Morten Welinder's avatar Morten Welinder

Derivative: use a GnmFunc signal instead of ad-hoc handlers.

parent 8c772ce2
......@@ -968,10 +968,10 @@ gnumeric_exp (GnmFuncEvalInfo *ei, GnmValue const * const *argv)
}
static GnmExpr const *
gnumeric_exp_deriv (GnmExpr const *expr, GnmEvalPos const *ep,
GnmExprDeriv *info, gpointer data)
gnumeric_exp_deriv (GnmFunc *func, GnmExpr const *expr, GnmEvalPos const *ep,
GnmExprDeriv *info)
{
return gnm_expr_copy (expr);
return gnm_expr_deriv_chain (expr, gnm_expr_copy (expr), ep, info);
}
/***************************************************************************/
......@@ -1318,9 +1318,11 @@ static GnmExpr const *
gnumeric_ln_deriv (GnmExpr const *expr, GnmEvalPos const *ep,
GnmExprDeriv *info, gpointer data)
{
return gnm_expr_new_binary (gnm_expr_new_constant (value_new_int (1)),
GNM_EXPR_OP_DIV,
gnm_expr_copy (gnm_expr_get_func_arg (expr, 0)));
GnmExpr const *deriv =
gnm_expr_new_binary (gnm_expr_new_constant (value_new_int (1)),
GNM_EXPR_OP_DIV,
gnm_expr_copy (gnm_expr_get_func_arg (expr, 0)));
return gnm_expr_deriv_chain (expr, deriv, ep, info);
}
/***************************************************************************/
......@@ -1750,8 +1752,9 @@ gnumeric_sumsq (GnmFuncEvalInfo *ei, int argc, GnmExprConstPtr const *argv)
}
static GnmExpr const *
gnumeric_sumsq_deriv (GnmExpr const *expr, GnmEvalPos const *ep,
GnmExprDeriv *info, gpointer data)
gnumeric_sumsq_deriv (GnmFunc *func,
GnmExpr const *expr, GnmEvalPos const *ep,
GnmExprDeriv *info)
{
GnmExprList *l, *args = gnm_expr_deriv_collect (expr, ep, info);
GnmExpr const *res;
......@@ -3742,24 +3745,15 @@ GnmFuncDescriptor const math_functions[] = {
G_MODULE_EXPORT void
go_plugin_init (GOPlugin *plugin, GOCmdContext *cc)
{
gnm_expr_deriv_install_handler (gnm_func_lookup ("sumsq", NULL),
gnumeric_sumsq_deriv,
GNM_EXPR_DERIV_NO_CHAIN | GNM_EXPR_DERIV_OPTIMIZE,
NULL, NULL);
gnm_expr_deriv_install_handler (gnm_func_lookup ("exp", NULL),
gnumeric_exp_deriv,
GNM_EXPR_DERIV_CHAIN,
NULL, NULL);
gnm_expr_deriv_install_handler (gnm_func_lookup ("ln", NULL),
gnumeric_ln_deriv,
GNM_EXPR_DERIV_CHAIN,
NULL, NULL);
g_signal_connect (gnm_func_lookup ("sumsq", NULL),
"derivative", G_CALLBACK (gnumeric_sumsq_deriv), NULL);
g_signal_connect (gnm_func_lookup ("exp", NULL),
"derivative", G_CALLBACK (gnumeric_exp_deriv), NULL);
g_signal_connect (gnm_func_lookup ("ln", NULL),
"derivative", G_CALLBACK (gnumeric_ln_deriv), NULL);
}
G_MODULE_EXPORT void
go_plugin_shutdown (GOPlugin *plugin, GOCmdContext *cc)
{
gnm_expr_deriv_uninstall_handler (gnm_func_lookup ("sumsq", NULL));
gnm_expr_deriv_uninstall_handler (gnm_func_lookup ("exp", NULL));
gnm_expr_deriv_uninstall_handler (gnm_func_lookup ("ln", NULL));
}
......@@ -32,25 +32,6 @@
/* ------------------------------------------------------------------------- */
static GHashTable *deriv_handlers;
struct DerivHandler {
GnmExprDerivHandler handler;
GnmExprDerivFlags flags;
gpointer data;
GDestroyNotify notify;
};
static void
deriv_handler_free (struct DerivHandler *di)
{
if (di->notify)
di->notify (di->data);
g_free (di);
}
/* ------------------------------------------------------------------------- */
struct GnmExprDeriv_ {
unsigned ref_count;
GnmEvalPos var;
......@@ -474,7 +455,7 @@ gnm_expr_deriv_collect (GnmExpr const *expr,
&user);
}
return user.args;
return g_slist_reverse (user.args);
}
/* ------------------------------------------------------------------------- */
......@@ -494,7 +475,15 @@ gnm_expr_deriv_collect (GnmExpr const *expr,
#define COMMON_BINARY_END }
/**
* gnm_expr_deriv:
* @expr: #GnmExpr
* @ep: position for @expr
* @info: Derivative information
*
* Returns: (transfer full) (nullable): the derivative of @expr with respect
* to @info.
*/
GnmExpr const *
gnm_expr_deriv (GnmExpr const *expr,
GnmEvalPos const *ep,
......@@ -585,33 +574,12 @@ gnm_expr_deriv (GnmExpr const *expr,
case GNM_EXPR_OP_FUNCALL: {
GnmFunc *f = gnm_expr_get_func_def (expr);
struct DerivHandler const *di = deriv_handlers
? g_hash_table_lookup (deriv_handlers, f)
: NULL;
GnmExpr const *res = di
? di->handler (expr, ep, info, di->data)
: NULL;
if (!res)
return NULL;
if (di->flags & GNM_EXPR_DERIV_CHAIN) {
GnmExpr const *e2 =
gnm_expr_deriv (gnm_expr_get_func_arg (expr, 0), ep, info);
if (!e2) {
gnm_expr_free (res);
return NULL;
}
res = mmul (res, 0, e2, 0);
GnmExpr const *res = gnm_func_derivative (f, expr, ep, info);
GnmExpr const *opt = optimize (res);
if (opt) {
gnm_expr_free (res);
res = opt;
}
if (di->flags & GNM_EXPR_DERIV_OPTIMIZE) {
GnmExpr const *opt = optimize (res);
if (opt) {
gnm_expr_free (res);
res = opt;
}
}
return res;
}
......@@ -794,40 +762,35 @@ gnm_expr_cell_deriv_value (GnmCell *y, GnmCell *x)
/* ------------------------------------------------------------------------- */
/**
* gnm_expr_deriv_install_handler:
* @func: the function being given a handler
* @h: (scope notified): #GnmExprDerivHandler
* @flags:
* @data: user data for @h
* @notify: destroy notification for @data
* gnm_expr_deriv_chain:
* @expr: #GnmExpr for a function call with one argument
* @deriv: (transfer full) (nullable): Derivative of @expr's function.
* @ep: position for @expr
* @info: Derivative information
*
* Applies the chain rule to @expr.
*
* Returns: (transfer full) (nullable): the derivative of @expr with respect
* to @info.
*/
void
gnm_expr_deriv_install_handler (GnmFunc *func, GnmExprDerivHandler h,
GnmExprDerivFlags flags,
gpointer data,
GDestroyNotify notify)
GnmExpr const *
gnm_expr_deriv_chain (GnmExpr const *expr,
GnmExpr const *deriv,
GnmEvalPos const *ep,
GnmExprDeriv *info)
{
struct DerivHandler *hdata;
if (!deriv_handlers) {
deriv_handlers = g_hash_table_new_full
(g_direct_hash, g_direct_equal,
NULL, (GDestroyNotify)deriv_handler_free);
}
GnmExpr const *deriv2;
hdata = g_new (struct DerivHandler, 1);
hdata->handler = h;
hdata->flags = flags;
hdata->data = data;
hdata->notify = notify;
if (!deriv)
return NULL;
g_hash_table_replace (deriv_handlers, func, hdata);
}
deriv2 = gnm_expr_deriv (gnm_expr_get_func_arg (expr, 0), ep, info);
if (!deriv2) {
gnm_expr_free (deriv);
return NULL;
}
void
gnm_expr_deriv_uninstall_handler (GnmFunc *func)
{
g_hash_table_remove (deriv_handlers, func);
return mmul (deriv, 0, deriv2, 0);
}
/* ------------------------------------------------------------------------- */
......@@ -835,10 +798,6 @@ gnm_expr_deriv_uninstall_handler (GnmFunc *func)
void
_gnm_expr_deriv_shutdown (void)
{
if (deriv_handlers) {
g_hash_table_destroy (deriv_handlers);
deriv_handlers = NULL;
}
}
/* ------------------------------------------------------------------------- */
......@@ -3,13 +3,12 @@
G_BEGIN_DECLS
#include <gnumeric-fwd.h>
#include <expr.h>
#include <numbers.h>
/* ------------------------------------------------------------------------- */
typedef struct GnmExprDeriv_ GnmExprDeriv;
GType gnm_expr_deriv_info_get_type (void);
GnmExprDeriv *gnm_expr_deriv_info_new (void);
......@@ -33,26 +32,17 @@ GnmExprTop const *gnm_expr_cell_deriv (GnmCell *y, GnmCell *x);
gnm_float gnm_expr_cell_deriv_value (GnmCell *y, GnmCell *x);
/* ------------------------------------------------------------------------- */
GnmExpr const *gnm_expr_deriv_chain (GnmExpr const *expr,
GnmExpr const *deriv,
GnmEvalPos const *ep,
GnmExprDeriv *info);
GnmExprList *gnm_expr_deriv_collect (GnmExpr const *expr,
GnmEvalPos const *ep,
GnmExprDeriv *info);
typedef GnmExpr const * (*GnmExprDerivHandler) (GnmExpr const *expr,
GnmEvalPos const *ep,
GnmExprDeriv *info,
gpointer user);
typedef enum {
GNM_EXPR_DERIV_NO_CHAIN = 0x0,
GNM_EXPR_DERIV_CHAIN = 0x1,
GNM_EXPR_DERIV_OPTIMIZE = 0x2
} GnmExprDerivFlags;
void gnm_expr_deriv_install_handler (GnmFunc *func, GnmExprDerivHandler h,
GnmExprDerivFlags flags,
gpointer data, GDestroyNotify notify);
void gnm_expr_deriv_uninstall_handler (GnmFunc *func);
/* ------------------------------------------------------------------------- */
void _gnm_expr_deriv_shutdown (void);
/* ------------------------------------------------------------------------- */
......
......@@ -63,13 +63,12 @@ gnumeric_sum (GnmFuncEvalInfo *ei, int argc, GnmExprConstPtr const *argv)
}
static GnmExpr const *
gnumeric_sum_deriv (GnmExpr const *expr,
gnumeric_sum_deriv (GnmFunc *func,
GnmExpr const *expr,
GnmEvalPos const *ep,
GnmExprDeriv *info,
gpointer data)
GnmExprDeriv *info)
{
GnmExprList *l, *args = gnm_expr_deriv_collect (expr, ep, info);
GnmFunc *fsum = gnm_expr_get_func_def (expr);
gboolean bad = FALSE;
for (l = args; l; l = l->next) {
......@@ -90,7 +89,7 @@ gnumeric_sum_deriv (GnmExpr const *expr,
gnm_expr_list_free (args);
return NULL;
} else
return gnm_expr_new_funcall (fsum, args);
return gnm_expr_new_funcall (func, args);
}
/***************************************************************************/
......@@ -541,7 +540,6 @@ func_builtin_init (void)
const char *gname;
const char *tdomain = GETTEXT_PACKAGE;
int i = 0;
GnmFunc *table_func;
gname = N_("Mathematics");
math_group = gnm_func_group_fetch (gname, _(gname));
......@@ -562,13 +560,11 @@ func_builtin_init (void)
logic_group = gnm_func_group_fetch (gname, _(gname));
gnm_func_add (logic_group, builtins + i++, tdomain);
table_func = gnm_func_lookup ("table", NULL);
g_signal_connect (table_func, "link-dep", G_CALLBACK (gnumeric_table_link), NULL);
g_signal_connect (gnm_func_lookup ("table", NULL),
"link-dep", G_CALLBACK (gnumeric_table_link), NULL);
gnm_expr_deriv_install_handler (gnm_func_lookup ("sum", NULL),
gnumeric_sum_deriv,
GNM_EXPR_DERIV_NO_CHAIN | GNM_EXPR_DERIV_OPTIMIZE,
NULL, NULL);
g_signal_connect (gnm_func_lookup ("sum", NULL),
"derivative", G_CALLBACK (gnumeric_sum_deriv), NULL);
}
void
......
......@@ -30,6 +30,7 @@
#include <gnm-plugin.h>
#include <gutils.h>
#include <gui-util.h>
#include <expr-deriv.h>
#include <gnm-marshalers.h>
#include <goffice/goffice.h>
......@@ -47,6 +48,7 @@ enum {
enum {
SIG_LOAD_STUB,
SIG_LINK_DEP,
SIG_DERIVATIVE,
LAST_SIGNAL
};
static guint signals[LAST_SIGNAL] = { 0 };
......@@ -1782,6 +1784,27 @@ gnm_func_link_dep (GnmFunc *func, GnmFuncEvalInfo *ei, gboolean qlink)
return (GnmDependentFlags)res;
}
/**
* gnm_func_derivative:
* @func: #GnmFunc
* @expr: expression that calls @func
* @ep: position of @expr
* @info: #GnmExprDeriv
*
* Returns: (transfer full) (nullable): the derivative of @expr with respect to
* @info.
*/
GnmExpr const *
gnm_func_derivative (GnmFunc *func, GnmExpr const *expr, GnmEvalPos const *ep,
GnmExprDeriv *info)
{
GnmExpr *res = NULL;
g_return_val_if_fail (GNM_IS_FUNC (func), NULL);
g_signal_emit (func, signals[SIG_DERIVATIVE], 0, expr, ep, info, &res);
return res;
}
/* ------------------------------------------------------------------------- */
static GObjectClass *parent_class;
......@@ -1791,6 +1814,7 @@ typedef struct {
void (*load_stub) (GnmFunc *func);
int (*link_dep) (GnmFunc *func, GnmFuncEvalInfo *ei, gboolean qlink);
GnmExpr* (*derivative) (GnmFunc *func, GnmExpr const *expr, GnmEvalPos *ep, GnmExprDeriv *info);
} GnmFuncClass;
static void
......@@ -1925,6 +1949,14 @@ gnm_func_class_init (GObjectClass *gobject_class)
GSF_PARAM_STATIC |
G_PARAM_READABLE));
/**
* GnmFunc::load-stub:
* @func: the #GnmFunc that needs to be loaded
*
* Signals that @func, which is a stub, needs to be loaded now. Anyone
* creating a stub function should arrange for this signal to be caught
* and the function to be properly instantiated.
*/
signals[SIG_LOAD_STUB] = g_signal_new
("load-stub",
GNM_FUNC_TYPE,
......@@ -1934,7 +1966,18 @@ gnm_func_class_init (GObjectClass *gobject_class)
g_cclosure_marshal_VOID__VOID,
G_TYPE_NONE, 0);
/**
* GnmFunc::link-dep:
* @func: the #GnmFunc that is being linked or unlinked
* @ei: #GnmFuncEvalInfo for the call initiating the link or unlink.
* @qlink: %TRUE for link, %FALSE for unlink
*
* Signals that an expressions that is a call to @func is being linked
* or unlinked. Most functions do not need this.
*
* Returns: A #GnmDependentFlags allowing arguments not be be linked if
* that is appropriate.
*/
signals[SIG_LINK_DEP] = g_signal_new
("link-dep",
GNM_FUNC_TYPE,
......@@ -1944,6 +1987,28 @@ gnm_func_class_init (GObjectClass *gobject_class)
gnm__INT__POINTER_BOOLEAN,
// GnmDependentFlags ... GnmFuncEvalInfo
G_TYPE_INT, 2, G_TYPE_POINTER, G_TYPE_BOOLEAN);
/**
* GnmFunc::derivative:
* @func: #GnmFunc
* @expr: #GnmExpr for the call for which the derivative is sought
* @ep: position f @expr
* @info: #GnmExprDeriv telling which derivative is sought
*
* Signals that a function call's derivative should be calculatted
*
* Returns: (transfer full) (nullable): #GnmExpr representing the
* derivative, %NULL for error.
*/
signals[SIG_DERIVATIVE] = g_signal_new
("derivative",
GNM_FUNC_TYPE,
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (GnmFuncClass, derivative),
NULL, NULL,
gnm__BOXED__BOXED_BOXED_BOXED,
gnm_expr_get_type(),
3, gnm_expr_get_type(), gnm_eval_pos_get_type(), gnm_expr_deriv_info_get_type());
}
GSF_CLASS (GnmFunc, gnm_func,
......
......@@ -306,6 +306,13 @@ struct _GnmFuncEvalInfo {
GnmFunc const *gnm_eval_info_get_func (GnmFuncEvalInfo const *ei);
int gnm_eval_info_get_arg_count (GnmFuncEvalInfo const *ei);
/*************************************************************************/
GnmExpr const *gnm_func_derivative (GnmFunc *func,
GnmExpr const *expr, GnmEvalPos const *ep,
GnmExprDeriv *info);
G_END_DECLS
#endif /* _GNM_FUNC_H_ */
......@@ -25,3 +25,4 @@ BOOLEAN:OBJECT,POINTER
VOID:BOOLEAN,INT
BOOLEAN:VOID
INT:POINTER,BOOLEAN
BOXED:BOXED,BOXED,BOXED
......@@ -104,6 +104,7 @@ typedef struct _WorkbookView WorkbookView;
typedef union _GnmExpr GnmExpr;
typedef union _GnmValue GnmValue;
typedef struct _GenericToolState GnmGenericToolState;
typedef struct GnmExprDeriv_ GnmExprDeriv;
typedef GList ColRowIndexList;
typedef GSList ColRowStateGroup;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment