summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFranklin Wei <me@fwei.tk>2018-05-18 20:51:00 -0400
committerFranklin Wei <me@fwei.tk>2018-05-18 20:51:00 -0400
commitb5fe0bd4a9896c54445b2c9e33fe65b9fe3cd74f (patch)
treeba5523a2be8255d71c63fbd8a28a1dcbe6e7f4a6
parentb7d50370731a52431582b3dc9074f5db6648370b (diff)
downloadyacas-b5fe0bd4a9896c54445b2c9e33fe65b9fe3cd74f.zip
yacas-b5fe0bd4a9896c54445b2c9e33fe65b9fe3cd74f.tar.gz
yacas-b5fe0bd4a9896c54445b2c9e33fe65b9fe3cd74f.tar.bz2
yacas-b5fe0bd4a9896c54445b2c9e33fe65b9fe3cd74f.tar.xz
add basic integration
-rw-r--r--yacas.c64
1 files changed, 57 insertions, 7 deletions
diff --git a/yacas.c b/yacas.c
index e1bbe77..1fbb8ef 100644
--- a/yacas.c
+++ b/yacas.c
@@ -34,13 +34,13 @@ struct expression {
};
} __attribute__((packed));
-enum { OP_POW = 0, OP_MUL, OP_DIV, OP_ADD, OP_SUB, OP_DIFF, OP_ASSIGN, OP_EQUALS, OP_LAST, /* not a real operator: */ OP_LPAREN };
+enum { OP_POW = 0, OP_MUL, OP_DIV, OP_ADD, OP_SUB, OP_DIFF, OP_INT, OP_ASSIGN, OP_EQUALS, OP_LAST, /* not a real operator: */ OP_LPAREN };
enum { T_CONST = 0, T_VAR, T_SUBEXPR, T_SPECFUNC };
enum { FN_LOG = 0, FN_EXP, FN_SIN, FN_COS, FN_TAN, FN_CSC, FN_SEC, FN_COT, FN_ASIN, FN_ACOS, FN_ATAN, FN_ACSC, FN_ASEC, FN_ACOT, FN_ABS, FN_NEGATE, FN_SQRT, FN_LAST };
enum { RIGHT, LEFT };
const char *op_names[] = {
- "^", "*", "/", "+", "-", "diff", "=", "==", "none", "("
+ "^", "*", "/", "+", "-", "diff", "int", "=", "==", "none", "("
};
const char *type_names[] = {
@@ -66,6 +66,7 @@ bool check_boolean(const char *varname);
double eval_numexpr(struct expression *expr);
double eval_numexpr_and_free(struct expression *expr);
struct expression *diff(struct expression *expr, const char *var);
+struct expression *integrate(struct expression *expr, const char *var);
struct expression *dup_expr(struct expression *expr);
struct expression *eval_expr(struct expression *expr);
struct expression *simp(struct expression *expr);
@@ -171,6 +172,7 @@ int expr_prec(struct expression expr)
case OP_EQUALS:
return 1;
case OP_DIFF:
+ case OP_INT:
return 2;
case OP_ADD:
case OP_SUB:
@@ -235,6 +237,13 @@ struct expression *diff_and_free(struct expression *expr, const char *var)
return ret;
}
+struct expression *integrate_and_free(struct expression *expr, const char *var)
+{
+ struct expression *ret = integrate(expr, var);
+ free_expr(expr);
+ return ret;
+}
+
/* simplify as much as possible, while freeing original */
struct expression *simp_and_free_max(struct expression *expr)
{
@@ -673,6 +682,12 @@ struct expression *eval_op(int op, struct expression *lexpr, struct expression *
fail("diff operator must take variable as second operand");
}
}
+ else if(op == OP_INT)
+ {
+ struct expression *result = integrate_and_free(eval_expr(lexpr), rexpr->varname);
+ if(!result)
+ return new_op(op, dup_expr(lexpr), dup_expr(rexpr));
+ }
else if(op == OP_ASSIGN)
{
if(lexpr->type != T_VAR)
@@ -928,8 +943,9 @@ struct expression *simp(struct expression *expr)
}
return new_op(expr->subexpr.op, simp(expr->subexpr.left), simp(expr->subexpr.right));
case OP_DIFF:
- assert(expr->subexpr.right->type == T_VAR);
return new_op(OP_DIFF, simp(expr->subexpr.left), dup_expr(expr->subexpr.right));
+ case OP_INT:
+ return new_op(OP_INT, simp(expr->subexpr.left), dup_expr(expr->subexpr.right));
case OP_ASSIGN:
return new_op(OP_ASSIGN, dup_expr(expr->subexpr.left), simp(expr->subexpr.right));
case OP_EQUALS:
@@ -957,6 +973,37 @@ struct expression *simp(struct expression *expr)
}
}
+/* integrate with respect to "var", treating all other variables as
+ * CONSTANTS; probably won't work (in which case it will return NULL) */
+struct expression *integrate(struct expression *expr, const char *var)
+{
+ switch(expr->type)
+ {
+ case T_CONST:
+ return new_op(OP_MUL, new_const(expr->constant), new_var(var));
+ case T_VAR:
+ if(!strcmp(expr->varname, var))
+ return new_op(OP_MUL,
+ new_const(.5),
+ new_op(OP_POW,
+ new_var(var),
+ new_const(2)));
+ return new_op(OP_MUL, dup_expr(expr), new_var(var));
+ case T_SUBEXPR:
+ switch(expr->subexpr.op)
+ {
+ case OP_ADD:
+ case OP_SUB:
+ return new_op(expr->subexpr.op, integrate(expr->subexpr.left, var), integrate(expr->subexpr.right, var));
+ default:
+ return NULL;
+ }
+ case T_SPECFUNC:
+ default:
+ return NULL;
+ }
+}
+
/* differentiate with respect to "var", returns entirely new object */
struct expression *diff(struct expression *expr, const char *var)
{
@@ -1026,15 +1073,18 @@ struct expression *diff(struct expression *expr, const char *var)
new_specfunc(FN_LOG, dup_expr(expr->subexpr.left)))))));
}
case OP_DIFF:
- //if(expr->subexpr.left->type == T_VAR && !is_var(expr->subexpr.left->varname))
return simp_and_free(new_op(OP_DIFF, dup_expr(expr), new_var(var)));
- /*return diff_and_free(diff(expr->subexpr.left, expr->subexpr.right->varname), var);*/
+ case OP_INT:
+ /* d/dx int f(x) dx = f(x) */
+ if(expr->subexpr.right->type == T_VAR && !strcmp(expr->subexpr.right->varname, var))
+ return simp(expr->subexpr.left);
+
+ /* otherwise return nothing */
+ return simp_and_free(new_op(OP_INT, dup_expr(expr), new_var(var)));
case OP_ASSIGN:
- /* todo: how to make this logical? */
fail("cannot differentiate assignment operator (did you mean == ?)");
case OP_EQUALS:
return simp_and_free(new_op(OP_EQUALS, diff(expr->subexpr.left, var), diff(expr->subexpr.right, var)));
- /* todo: how to make this logical? */
default:
fatal("fall through");
}