Commit 96ac55d4 authored by Jiri (George) Lebl's avatar Jiri (George) Lebl Committed by George Lebl

Add a primitive SymbolicDerivative function. Doesn't yet even get even all


Tue Jun 14 10:23:51 2005  George Lebl <jirka@5z.com>

	* src/symbolic.[ch], src/eval.[ch]: Add a primitive
	  SymbolicDerivative function.  Doesn't yet even get even all the
	  simple cases (such as functions with more then one argument) but
	  should be semi usable for simple things.  Of course there is no
	  simplification of expressions in genius so things can get wild a
	  bit.

	* src/graphing.c, src/funclib.c: adjustments because of the above

	* src/calc.c: remove an extra parenthesis in function printing
	  output.

	* src/geniustests.txt: add tests for SymbolicDerivative

	* help/C/gel-function-list.xml: Add SymbolicDerivative blurb
parent 09cb1eb6
Tue Jun 14 10:23:51 2005 George Lebl <jirka@5z.com>
* src/symbolic.[ch], src/eval.[ch]: Add a primitive
SymbolicDerivative function. Doesn't yet even get even all the
simple cases (such as functions with more then one argument) but
should be semi usable for simple things. Of course there is no
simplification of expressions in genius so things can get wild a
bit.
* src/graphing.c, src/funclib.c: adjustments because of the above
* src/calc.c: remove an extra parenthesis in function printing
output.
* src/geniustests.txt: add tests for SymbolicDerivative
* help/C/gel-function-list.xml: Add SymbolicDerivative blurb
2005-04-01 Steve Murphy <murf@e-tools.com>
* configure.in: Added "rw" to ALL_LINGUAS.
......
......@@ -3749,6 +3749,27 @@ number is specified) of the given size returned. For example
</variablelist>
</sect1>
<sect1 id="genius-gel-function-list-symbolic">
<title>Symbolic Operations</title>
<variablelist>
<varlistentry id="gel-function-SymbolicDerivative">
<term>SymbolicDerivative</term>
<listitem>
<synopsis>SymbolicDerivative (f)</synopsis>
<para>Attempt to symbolically differentiate the function f, where f is a function of one variable.</para>
<para>
Examples:
<screen><prompt>genius></prompt> <userinput>SymbolicDerivative(sin)</userinput>
<prompt>genius></prompt> <userinput>(`(x)=cos(x))</userinput>
<prompt>genius></prompt> <userinput>SymbolicDerivative(`(x)=7*x^2)</userinput>
<prompt>genius></prompt> <userinput>(`(x)=(7*(2*x)))</userinput>
</screen>
</para>
</listitem>
</varlistentry>
</variablelist>
</sect1>
<sect1 id="genius-gel-function-list-plotting">
<title>Plotting</title>
<variablelist>
......
......@@ -171,6 +171,7 @@ Kai Willadsen
Polynomials
Set Theory
Miscellaneous
Symbolic Operations
Plotting
Example programs in GEL
......@@ -4246,6 +4247,23 @@ Miscellaneous
letters
_________________________________________________________
Symbolic Operations
SymbolicDerivative
SymbolicDerivative (f)
Attempt to symbolically differentiate the function f,
where f is a function of one variable.
Examples:
genius> SymbolicDerivative(sin)
genius> (`(x)=cos(x))
genius> SymbolicDerivative(`(x)=7*x^2)
genius> (`(x)=(7*(2*x)))
_________________________________________________________
Plotting
LinePlot
......
......@@ -62,6 +62,8 @@ gnome_genius_SOURCES = \
dict.h \
funclib.c \
funclib.h \
symbolic.h \
symbolic.c \
mpwrap.c \
mpwrap.h \
mpzextra.c \
......@@ -139,6 +141,8 @@ genius_SOURCES = \
dict.h \
funclib.c \
funclib.h \
symbolic.h \
symbolic.c \
mpwrap.c \
mpwrap.h \
mpzextra.c \
......
......@@ -1331,10 +1331,10 @@ gel_print_etree (GelOutput *gelo,
gel_output_string (gelo, "...");
if G_LIKELY (f->type==GEL_USER_FUNC) {
gel_output_string(gelo,")=(");
gel_output_string(gelo,")=");
D_ENSURE_USER_BODY (f);
gel_print_etree (gelo, f->data.user, FALSE);
gel_output_string(gelo,"))");
gel_output_string(gelo,")");
} else {
/*variable and reference functions should
never be in the etree*/
......
......@@ -814,6 +814,15 @@ copyreplacenode(GelETree *to, GelETree *from)
to->any.next = next;
}
void
gel_replacenode (GelETree *to, GelETree *from, gboolean copy)
{
if (copy)
copyreplacenode (to, from);
else
replacenode (to, from);
}
GelETree *
makeoperator (int oper, GSList **stack)
{
......@@ -2555,6 +2564,7 @@ function_finish_bin_op (GelCtx *ctx, GelETree *n, int nargs, GelETree *la, GelET
freetree_full (n, TRUE /* free args */, FALSE /* kill */);
n->type = FUNCTION_NODE;
n->func.func = f;
n->func.func->context = -1;
return TRUE;
}
......@@ -2669,6 +2679,7 @@ function_uni_op (GelCtx *ctx, GelETree *n, GelETree *l)
freetree_full (n, TRUE /* free args */, FALSE /* kill */);
n->type = FUNCTION_NODE;
n->func.func = f;
n->func.func->context = -1;
return TRUE;
}
......@@ -2725,6 +2736,7 @@ function_from_function (GelEFunc *func, GelETree *l)
GET_NEW_NODE (n);
n->type = FUNCTION_NODE;
n->func.func = f;
n->func.func->context = -1;
return n;
}
......@@ -7078,15 +7090,17 @@ fixup_num_neg (GelETree *n)
}
}
/* IMPORTANT: There's also a tree traversal function in symbolic.c */
/* find an identifier */
gboolean
eval_find_identifier (GelETree *n, GelToken *tok)
eval_find_identifier (GelETree *n, GelToken *tok, gboolean funcbody)
{
if (n == NULL)
return FALSE;
if (n->type == SPACER_NODE) {
return eval_find_identifier (n->sp.arg, tok);
return eval_find_identifier (n->sp.arg, tok, funcbody);
} else if (n->type == IDENTIFIER_NODE ) {
if (n->id.id == tok)
return TRUE;
......@@ -7095,7 +7109,7 @@ eval_find_identifier (GelETree *n, GelToken *tok)
} else if(n->type == OPERATOR_NODE) {
GelETree *args = n->op.args;
while (args != NULL) {
if (eval_find_identifier (args, tok))
if (eval_find_identifier (args, tok, funcbody))
return TRUE;
args = args->any.next;
}
......@@ -7111,7 +7125,7 @@ eval_find_identifier (GelETree *n, GelToken *tok)
GelETree *t = gel_matrixw_set_index
(n->mat.matrix, i, j);
if (t != NULL &&
eval_find_identifier (t, tok))
eval_find_identifier (t, tok, funcbody))
return TRUE;
}
}
......@@ -7119,20 +7133,21 @@ eval_find_identifier (GelETree *n, GelToken *tok)
} else if (n->type == SET_NODE ) {
GelETree *ali;
for (ali = n->set.items; ali != NULL; ali = ali->any.next) {
if (eval_find_identifier (ali, tok))
if (eval_find_identifier (ali, tok, funcbody))
return TRUE;
}
return FALSE;
} else if (n->type == FUNCTION_NODE &&
} else if (funcbody &&
n->type == FUNCTION_NODE &&
(n->func.func->type == GEL_USER_FUNC ||
n->func.func->type == GEL_VARIABLE_FUNC) &&
n->func.func->data.user != NULL) {
return eval_find_identifier (n->func.func->data.user, tok);
n->func.func->type == GEL_VARIABLE_FUNC)) {
D_ENSURE_USER_BODY (n->func.func);
return eval_find_identifier (n->func.func->data.user, tok,
funcbody);
}
return FALSE;
}
/*this means that it will precalc even complex and float
numbers*/
static void
......@@ -7223,6 +7238,7 @@ try_to_precalc_op(GelETree *n)
}
}
/* FIXME: try to also precalc things like 3*(10*foo) */
void
try_to_do_precalc(GelETree *n)
{
......
......@@ -153,6 +153,8 @@ void gel_makenum_null_from(GelETree *n);
/*copy a node*/
GelETree * copynode(GelETree *o);
void gel_replacenode (GelETree *to, GelETree *from, gboolean copy);
/*functions for reclaiming memory*/
void gel_freetree(GelETree *n);
void gel_emptytree(GelETree *n);
......@@ -186,7 +188,9 @@ void fixup_num_neg (GelETree *n);
void try_to_do_precalc(GelETree *n);
/* find an identifier */
gboolean eval_find_identifier (GelETree *n, GelToken *tok);
gboolean eval_find_identifier (GelETree *n,
GelToken *tok,
gboolean funcbody);
char * gel_similar_possible_ids (const char *id);
......
......@@ -32,6 +32,7 @@
#include "eval.h"
#include "dict.h"
#include "funclib.h"
#include "symbolic.h"
#include "matrix.h"
#include "matrixw.h"
#include "matop.h"
......@@ -65,6 +66,11 @@ GelEFunc *Re_function = NULL;
GelEFunc *Im_function = NULL;
/* GelEFunc *ErrorFunction_function = NULL; */
/* GelEFunc *RiemannZeta_function = NULL; */
GelEFunc *pi_function = NULL;
GelEFunc *e_function = NULL;
GelEFunc *GoldenRatio_function = NULL;
GelEFunc *Gravity_function = NULL;
GelEFunc *EulerConstant_function = NULL;
/*maximum number of primes to precalculate and store*/
#define MAXPRIMES 30000
......@@ -4678,12 +4684,17 @@ gel_funclib_addall(void)
ALIAS (arctan, 1, atan);
FUNC (pi, 0, "", "constants", N_("The number pi"));
pi_function = f;
FUNC (e, 0, "", "constants", N_("The natural number e"));
e_function = f;
FUNC (GoldenRatio, 0, "", "constants", N_("The Golden Ratio"));
GoldenRatio_function = f;
FUNC (Gravity, 0, "", "constants", N_("Free fall acceleration"));
Gravity_function = f;
FUNC (EulerConstant, 0, "", "constants",
N_("Euler's Constant gamma"));
ALIAS (gamma, 0, EulerConstant);
EulerConstant_function = f;
/* FIXME: need to handle complex values */
/*
......@@ -4872,6 +4883,9 @@ gel_funclib_addall(void)
NULL, NULL),
g_slist_append(NULL,d_intern("x")),1,
NULL);
gel_add_symbolic_functions ();
/*protect EVERYthing up to this point*/
d_protect_all();
d_protect_all ();
}
......@@ -69,7 +69,7 @@ sqrt(-1) 1i
OPTIONS --maxdigits=0
50! 30414093201713378043612608166064768844377641568960512000000000000
OPTIONS --maxdigits=12
function t()=(1+2);t (`()=(3))
function t()=(1+2);t (`()=3)
function t()=(1+2);t() 3
function t()=(g=1;g=g+1;g);t() 2
t = `()=(g=1;g=g+1;g);t() 2
......@@ -671,4 +671,7 @@ MoebiusMu(4) 0
MoebiusMu(2*3) 1
RungeKutta (`(x,y) = 2*x+3,0,0,8,10) 88
EulersMethod (`(x,y) = 2*x+3,0,0,8,100) 88.88
SymbolicDerivative(sin) (`(x)=cos(x))
SymbolicDerivative(cos) (`(x)=(-sin(x)))
SymbolicDerivative(`(x)=10*x^3+x^2+88*x+100) (`(x)=((((10*(3*(x^2)))+(2*x))+88)+0))
load "longtest.gel" true
......@@ -2559,9 +2559,10 @@ function_from_expression2 (const char *e, gboolean *ex)
NULL /* dirprefix */);
g_free (ce);
got_x = eval_find_identifier (value, d_intern ("x"));
got_y = eval_find_identifier (value, d_intern ("y"));
got_z = eval_find_identifier (value, d_intern ("z"));
/* FIXME: funcbody? I think it must be done. */
got_x = eval_find_identifier (value, d_intern ("x"), TRUE /*funcbody*/);
got_y = eval_find_identifier (value, d_intern ("y"), TRUE /*funcbody*/);
got_z = eval_find_identifier (value, d_intern ("z"), TRUE /*funcbody*/);
/* FIXME: if "x" or "y" or "z" not used try to evaluate and if it returns a function use that */
if (value != NULL) {
......
/* GENIUS Calculator
* Copyright (C) 2005 Jiri (George) Lebl
*
* Author: Jiri (George) Lebl
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
* USA.
*/
#include "config.h"
#include <string.h>
#include <unistd.h>
#include <sys/time.h>
#include <math.h>
#include <glib.h>
#include "calc.h"
#include "mpwrap.h"
#include "mpzextra.h"
#include "eval.h"
#include "dict.h"
#include "symbolic.h"
#include "matrix.h"
#include "matrixw.h"
#include "matop.h"
#include "geloutput.h"
#include "compil.h"
#include "funclibhelper.cP"
extern calcstate_t calcstate;
extern GHashTable *uncompiled;
static GelETree * differentiate_expr (GelETree *expr, GelToken *xtok);
static GelETree * gel_differentiate_func1_expr (GelToken *tok);
#if 0
static GelEFunc *
zero_function (void)
{
GelEFunc *f;
f = d_makeufunc (NULL /* id */,
gel_makenum_ui (0),
g_slist_append (NULL, d_intern ("x")),
1 /* nargs */,
NULL /* extra_dict */);
f->context = -1;
return f;
}
static GelEFunc *
hack_parse_function (const char *expr)
{
GelEFunc *f;
f = d_makeufunc (NULL /* id */,
gel_parseexp(expr,
NULL, FALSE, FALSE,
NULL, NULL),
g_slist_append (NULL, d_intern ("x")),
1 /* nargs */,
NULL /* extra_dict */);
f->context = -1;
return f;
}
#endif
#define PARSE(expr) (gel_parseexp (expr, NULL, FALSE, FALSE, NULL, NULL))
/* Differentiate 1-var function */
static GelETree *
gel_differentiate_func1_expr (GelToken *tok)
{
GelETree *n = NULL;
#define DERIVATIVE_ENTRY(func,expr) \
} else if (tok == d_intern (func)) { \
n = gel_parseexp (expr, NULL, FALSE, FALSE, \
NULL, NULL);
#define DERIVATIVE_ENTRY_ALIAS(func1,func2,expr) \
} else if (tok == d_intern (func1) || \
tok == d_intern (func2)) { \
n = gel_parseexp (expr, NULL, FALSE, FALSE, \
NULL, NULL);
if (tok == NULL) {
return NULL;
DERIVATIVE_ENTRY ("conj", "0");
DERIVATIVE_ENTRY ("exp", "exp(x)");
DERIVATIVE_ENTRY ("ln", "1/x");
DERIVATIVE_ENTRY ("log2", "log2(e)/x");
DERIVATIVE_ENTRY ("log10", "log10(e)/x");
/* treat z and zbar separately */
DERIVATIVE_ENTRY ("Re", "(1/2)*conj(x)");
DERIVATIVE_ENTRY ("Im", "(-1/2i)*conj(x)");
DERIVATIVE_ENTRY ("sin", "cos(x)");
DERIVATIVE_ENTRY ("sinh", "cosh(x)");
DERIVATIVE_ENTRY ("cos", "-sin(x)");
DERIVATIVE_ENTRY ("cosh", "sinh(x)");
DERIVATIVE_ENTRY ("csc", "-csc(x)*cot(x)");
DERIVATIVE_ENTRY ("csch", "-csch(x)*coth(x)");
DERIVATIVE_ENTRY ("sec", "sec(x)*tan(x)");
DERIVATIVE_ENTRY ("sech", "-sech(x)*tanh(x)");
DERIVATIVE_ENTRY ("tan", "sec(x)^2");
DERIVATIVE_ENTRY ("tanh", "sech(x)^2");
DERIVATIVE_ENTRY ("cot", "-csc(x)^2");
DERIVATIVE_ENTRY ("coth", "-csch(x)^2");
/* FIXME: check these, I don't trust the CRC handbook */
DERIVATIVE_ENTRY_ALIAS ("asin", "arcsin", "1/sqrt(1-x^2)");
DERIVATIVE_ENTRY_ALIAS ("asinh", "arcsinh", "1/sqrt(1+x^2)");
DERIVATIVE_ENTRY_ALIAS ("acos", "arccos", "-1/sqrt(1-x^2)");
DERIVATIVE_ENTRY_ALIAS ("acosh", "arccosh", "1/sqrt(x^2-1)");
DERIVATIVE_ENTRY_ALIAS ("acsc", "arccsc", "-1/(u*sqrt(x^2-1))");
DERIVATIVE_ENTRY_ALIAS ("acsch", "arccsch", "-1/(u*sqrt(x^2+1))");
DERIVATIVE_ENTRY_ALIAS ("asec", "arcsec", "1/(u*sqrt(x^2-1))");
DERIVATIVE_ENTRY_ALIAS ("asech", "arcsech", "-1/(u*sqrt(1-x^2))");
DERIVATIVE_ENTRY_ALIAS ("atan", "arctan", "1/(1+x^2)");
DERIVATIVE_ENTRY_ALIAS ("atanh", "arctanh", "1/(1-x^2)");
DERIVATIVE_ENTRY_ALIAS ("acot", "arccot", "-1/(x^2+1)");
DERIVATIVE_ENTRY_ALIAS ("acoth", "arccoth", "-1/(x^2-1)");
DERIVATIVE_ENTRY ("cis", "-cos(x)+1i*cos(x)");
DERIVATIVE_ENTRY_ALIAS ("sqrt", "SquareRoot", "1/(2*sqrt(x))");
#undef DERIVATIVE_ENTRY
#undef DERIVATIVE_ENTRY_ALIAS
}
return n;
}
static void
substitute_x_y_z_w (GelETree *expr,
GelToken *xtok, GelETree *x, gboolean xcopy,
GelToken *ytok, GelETree *y, gboolean ycopy,
GelToken *ztok, GelETree *z, gboolean zcopy,
GelToken *wtok, GelETree *w, gboolean wcopy)
{
if (expr == NULL)
return;
if (expr->type == SPACER_NODE) {
substitute_x_y_z_w (expr->sp.arg,
xtok, x, xcopy,
ytok, y, ycopy,
ztok, z, zcopy,
wtok, w, wcopy);
} else if (expr->type == IDENTIFIER_NODE ) {
if (xtok != NULL && x != NULL && expr->id.id == xtok) {
gel_replacenode (expr, x, xcopy);
} else if (ytok != NULL && y != NULL && expr->id.id == ytok) {
gel_replacenode (expr, y, ycopy);
} else if (ztok != NULL && z != NULL && expr->id.id == ztok) {
gel_replacenode (expr, z, zcopy);
} else if (wtok != NULL && w != NULL && expr->id.id == wtok) {
gel_replacenode (expr, w, wcopy);
}
} else if (expr->type == OPERATOR_NODE) {
GelETree *args = expr->op.args;
while (args != NULL) {
substitute_x_y_z_w (args,
xtok, x, xcopy,
ytok, y, ycopy,
ztok, z, zcopy,
wtok, w, wcopy);
args = args->any.next;
}
} else if (expr->type == MATRIX_NODE &&
expr->mat.matrix != NULL) {
int i, j;
int mw, mh;
mw = gel_matrixw_width (expr->mat.matrix);
mh = gel_matrixw_height (expr->mat.matrix);
for (i = 0; i < mw; i++) {
for(j = 0; j < mh; j++) {
GelETree *t = gel_matrixw_set_index
(expr->mat.matrix, i, j);
if (t != NULL)
substitute_x_y_z_w (t,
xtok, x, xcopy,
ytok, y, ycopy,
ztok, z, zcopy,
wtok, w, wcopy);
}
}
} else if (expr->type == SET_NODE ) {
GelETree *ali;
for (ali = expr->set.items; ali != NULL; ali = ali->any.next) {
substitute_x_y_z_w (ali,
xtok, x, xcopy,
ytok, y, ycopy,
ztok, z, zcopy,
wtok, w, wcopy);
}
/* Not inside function body I don't think
} else if (expr->type == FUNCTION_NODE &&
(expr->func.func->type == GEL_USER_FUNC ||
expr->func.func->type == GEL_VARIABLE_FUNC) &&
ENSURE BODY! expr->func.func->data.user != NULL) {
substitute_x_y_z_w (expr->func.func->data.user,
xtok, x, xcopy,
ytok, y, ycopy,
ztok, z, zcopy,
wtok, w, wcopy);
*/
}
}
static void
substitute_x (GelETree *expr, GelToken *xtok, GelETree *x, gboolean xcopy)
{
substitute_x_y_z_w (expr, xtok, x, xcopy,
NULL, NULL, FALSE,
NULL, NULL, FALSE,
NULL, NULL, FALSE);
}
static void
substitute_x_y (GelETree *expr,
GelToken *xtok, GelETree *x, gboolean xcopy,
GelToken *ytok, GelETree *y, gboolean ycopy)
{
substitute_x_y_z_w (expr, xtok, x, xcopy,
ytok, y, ycopy,
NULL, NULL, FALSE,
NULL, NULL, FALSE);
}
/* FIXME: is this always ok? It won't catch things like x^0 !!! */
static gboolean
is_constant (GelETree *expr, GelToken *xtok)
{
if (expr->type == VALUE_NODE ||
(expr->type == IDENTIFIER_NODE &&
expr->id.id != xtok)) {
return TRUE;
}
if (eval_find_identifier (expr, xtok, FALSE /* funcbody */))
return FALSE;
else
return TRUE;
}
static GelETree *
differentiate_oper (GelETree *expr, GelToken *xtok)
{
GelETree *n, *nn, *nnn;
switch (expr->op.oper) {
case E_EXP:
/* FIXME: case E_ELTEXP: */
if (is_constant (expr->op.args, xtok)) {
nnn = differentiate_expr (expr->op.args->any.next, xtok);
if (nnn == NULL) {
/* FIXME: */
return NULL;
}
if (expr->op.args->type == IDENTIFIER_NODE &&
expr->op.args->id.id == d_intern ("e")) {
n = PARSE ("(x^y)*dy");
} else {
n = PARSE ("(ln(x)*x^y)*dy");
}
substitute_x_y_z_w (n,
d_intern ("x"), expr->op.args, TRUE,
d_intern ("y"), expr->op.args->any.next, TRUE,
d_intern ("dy"), nnn, FALSE,
NULL, NULL, FALSE);
return n;
} else if (is_constant (expr->op.args->any.next, xtok)) {
nn = differentiate_expr (expr->op.args, xtok);
if (nn == NULL) {
/* FIXME: */
return NULL;
}
if (expr->op.args->any.next->type == VALUE_NODE) {
GelETree *ymo = NULL;
mpw_t val;
if (mpw_eql_ui (expr->op.args->any.next->val.value, 0)) {
gel_freetree (nn);
return gel_makenum_ui (0);
}
mpw_init (val);
mpw_sub_ui (val,
expr->op.args->any.next->val.value, 1);
if (nn->type == VALUE_NODE &&
mpw_eql_ui (nn->val.value, 1)) {
if (mpw_eql_ui (val, 0)) {
n = gel_makenum_ui (1);
} else if (mpw_eql_ui (val, 1)) {
n = PARSE ("2*x");
} else {
n = PARSE ("y*x^ymo");
ymo = gel_makenum (val);
}
gel_freetree (nn);
nn = NULL;
} else {
if (mpw_eql_ui (val, 0)) {
n = gel_makenum_ui (1);
} else if (mpw_eql_ui (val, 1)) {
n = PARSE ("(2*x)*dx");
} else {
n = PARSE ("(y*x^ymo)*dx");
ymo = gel_makenum (val);
}
}
substitute_x_y_z_w (n,
d_intern ("x"), expr->op.args, TRUE,
d_intern ("y"), expr->op.args->any.next, TRUE,
d_intern ("dx"), nn, FALSE,
d_intern ("ymo"), ymo, FALSE);
return n;
}
if (nn->type == VALUE_NODE &&
mpw_eql_ui (nn->val.value, 1)) {
n = PARSE ("y*x^(y-1)");
gel_freetree (nn);
nn = NULL;
} else {
n = PARSE ("(y*x^(y-1))*dx");
}
substitute_x_y_z_w (n,
d_intern ("x"), expr->op.args, TRUE,
d_intern ("y"), expr->op.args->any.next, TRUE,
d_intern ("dx"), nn, FALSE,
NULL, NULL, FALSE);
return n;
}
nn = differentiate_expr (expr->op.args, xtok);
if (nn == NULL)
/* FIXME: */
return NULL;
nnn = differentiate_expr (expr->op.args->any.next, xtok);
if (nnn == NULL) {
gel_freetree (nn);
/* FIXME: */
return NULL;
}
n = PARSE ("(x*y^(y-1))*dx + (x^y)*ln(x)*dy");
substitute_x_y_z_w (n,
d_intern ("x"), expr->op.args, TRUE,
d_intern ("y"), expr->op.args->any.next, TRUE,
d_intern ("dx"), nn, FALSE,
d_intern ("dy"), nnn, FALSE);
return n;
case E_ABS:
if (is_constant (expr->op.args, xtok))
return gel_makenum_ui (0);
nn = differentiate_expr (expr->op.args, xtok);
if (nn == NULL)
/* FIXME: */
return NULL;
n = PARSE ("sign(x)*y");
substitute_x_y (n,
d_intern ("x"), expr->op.args, TRUE,
d_intern ("y"), nn, FALSE);
return n;
case E_PLUS:
case E_MINUS:
nn = differentiate_expr (expr->op.args, xtok);
if (nn == NULL)
/* FIXME: */
return NULL;
nnn = differentiate_expr (expr->op.args->any.next, xtok);
if (nnn == NULL) {
gel_freetree (nn);
/* FIXME: */
return NULL;
}
GET_NEW_NODE(n);
n->type = OPERATOR_NODE;
n->op.oper = expr->op.oper;
n->op.args = nn;
n->op.args->any.next = nnn;
n->op.args->any.next->any.next = NULL;
n->op.nargs = 2;
return n;
case E_MUL:
/* FIXME: case E_ELTMUL: */
if (is_constant (expr->op.args, xtok)) {
nnn = differentiate_expr (expr->op.args->any.next, xtok);
if (nnn == NULL) {
/* FIXME: */
return NULL;
}
GET_NEW_NODE(n);
n->type = OPERATOR_NODE;
n->op.oper = expr->op.oper;
n->op.args = copynode (expr->op.args);
n->op.args->any.next = nnn;
n->op.args->any.next->any.next = NULL;
n->op.nargs = 2;
return n;
} else if (is_constant (expr->op.args->any.next, xtok)) {
nn = differentiate_expr (expr->op.args, xtok);
if (nn == NULL) {
/* FIXME: */
return NULL;