aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorNathan Reiner <nathan@nathanreiner.xyz>2024-01-19 19:06:09 +0100
committerNathan Reiner <nathan@nathanreiner.xyz>2024-01-19 19:06:09 +0100
commit52e4dfc10616accaa6e4dcdbfc4a706e94dbcdc9 (patch)
tree3f6c7d7886774f1c10b77e7782b071aca7d4d31a /src
parent22c656d0be6dd5356f8839fae716174a9fe474c7 (diff)
math: pregenerate eval tree
Diffstat (limited to 'src')
-rw-r--r--src/main.rs19
-rw-r--r--src/math/expression.rs80
-rw-r--r--src/math/expression_function.rs10
-rw-r--r--src/math/function.rs51
4 files changed, 108 insertions, 52 deletions
diff --git a/src/main.rs b/src/main.rs
index 34adc14..534d634 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -47,11 +47,12 @@ impl Application for Graph {
type Flags = ();
fn new(_flags: ()) -> (Self, Command<Message>) {
+ let ctx = Context::commonsense();
(
Graph {
input_state: "f(x) = x^2".to_string(),
- func: ExpressionFunction::from_string("f(x) = x^2".to_string()),
- ctx: Context::commonsense(),
+ func: ExpressionFunction::from_string("f(x) = x^2".to_string(), ctx.operations()),
+ ctx,
..Default::default()
},
Command::none(),
@@ -68,7 +69,7 @@ impl Application for Graph {
self.input_state = s;
}
Message::UpdateScreen => {
- self.func = ExpressionFunction::from_string(self.input_state.clone());
+ self.func = ExpressionFunction::from_string(self.input_state.clone(), self.ctx.operations());
self.graph.clear();
}
}
@@ -137,26 +138,26 @@ impl canvas::Program<Message, Renderer> for Graph {
let unitline = Stroke {
width: 2.0,
style: stroke::Style::Solid(Color::new(0.0, 0.0, 0.0, 1.0)),
- line_cap: LineCap::Square,
+ line_cap: LineCap::Round,
..Stroke::default()
};
let unitline_whole = Stroke {
width: 1.0,
style: stroke::Style::Solid(Color::new(0.5, 0.5, 0.5, 1.0)),
- line_cap: LineCap::Square,
+ line_cap: LineCap::Round,
..Stroke::default()
};
let zeroline = Stroke {
width: 3.0,
style: stroke::Style::Solid(Color::new(0.0, 0.0, 0.8, 1.0)),
- line_cap: LineCap::Square,
+ line_cap: LineCap::Round,
..Stroke::default()
};
let graphline = Stroke {
width: 3.0,
style: stroke::Style::Solid(Color::new(0.8, 0.0, 0.0, 1.0)),
- line_cap: LineCap::Square,
+ line_cap: LineCap::Round,
..Stroke::default()
};
@@ -230,7 +231,7 @@ impl canvas::Program<Message, Renderer> for Graph {
let mut y1 = self
.func
.eval(
- FunctionArgument::new(vec![Complex::new(start_x as f64, 0.0)]),
+ &FunctionArgument::from_values(vec![Complex::new(start_x as f64, 0.0)]),
&self.ctx,
)
.unwrap()
@@ -243,7 +244,7 @@ impl canvas::Program<Message, Renderer> for Graph {
let y2 = self
.func
.eval(
- FunctionArgument::new(vec![Complex::new(x2 as f64, 0.0)]),
+ &FunctionArgument::from_values(vec![Complex::new(x2 as f64, 0.0)]),
&self.ctx,
)
.unwrap()
diff --git a/src/math/expression.rs b/src/math/expression.rs
index 9d6d4f3..049886d 100644
--- a/src/math/expression.rs
+++ b/src/math/expression.rs
@@ -1,25 +1,42 @@
-use crate::math::complex::Complex;
-use crate::math::context::Context;
-use crate::math::function::FunctionArgument;
-use crate::math::string::{ContainsAndSkipBrackets, SplitMatchingBracket};
+use super::complex::Complex;
+use super::context::Context;
+use super::function::FunctionArgument;
+use super::string::{ContainsAndSkipBrackets, SplitMatchingBracket};
+use super::operation::Operation;
+
+enum ExpressionType {
+ Value(Complex),
+ Variable(String),
+ Function(String, FunctionArgument),
+ Operation(Operation, Box<Expression>, Box<Expression>),
+}
+
+impl Default for ExpressionType {
+ fn default() -> Self {
+ ExpressionType::Value(Complex::default())
+ }
+}
#[derive(Default)]
pub struct Expression {
+ expr_type: ExpressionType,
repr: String,
}
impl Expression {
- pub fn from_string(str: &str) -> Self {
- Self {
- repr: str.replace(' ', ""),
- }
+ pub fn from_string(str: &str, operations: &Vec<Operation>) -> Self {
+ let mut ex = Self::default();
+ ex.repr = str.to_string();
+ ex.set_operations(operations);
+
+ ex
}
pub fn content(&self) -> &str {
&self.repr
}
- pub fn evaluate(&self, context: &Context) -> Result<Complex, String> {
+ pub fn set_operations(&mut self, operations: &Vec<Operation>) {
let (repr, oprepr) = {
if self.repr.starts_with('(') {
let (oprepr, r) = self.repr.split_on_matching_bracket();
@@ -36,7 +53,7 @@ impl Expression {
let curop: Option<_> = {
let mut o = None;
- for op in context.operations() {
+ for op in operations {
if oprepr.contains_and_skip_brackets(op.sign().to_string().as_str()) {
o = Some(op);
break;
@@ -47,27 +64,44 @@ impl Expression {
if let Some(op) = curop {
let (first, rest) = repr.split_once_and_skipt_brackets(op.sign()).unwrap();
- let first_expr = Expression::from_string(first);
- let rest_expr = Expression::from_string(rest);
- Ok(op.evaluate(first_expr.evaluate(context)?, rest_expr.evaluate(context)?))
+ let first_expr = Expression::from_string(first, operations);
+ let rest_expr = Expression::from_string(rest, operations);
+ self.expr_type = ExpressionType::Operation(op.clone(), Box::new(first_expr), Box::new(rest_expr));
} else if let Ok(r) = repr.parse::<Complex>() {
- Ok(r)
+ self.expr_type = ExpressionType::Value(r);
} else if let Some((func, args)) = repr.split_once('(') {
let mut argv = Vec::new();
for arg in args[..args.len() - 1].split(',') {
- argv.push(Expression::from_string(arg).evaluate(context)?);
+ argv.push(Expression::from_string(arg, operations));
}
- if let Some(func) = context.function(func) {
- Ok(func.eval(FunctionArgument::new(argv), context)?)
- } else {
- Err(format!("function '{func}' not found"))
- }
- } else if let Some(res) = context.variable(&repr) {
- Ok(*res)
+ self.expr_type = ExpressionType::Function(func.to_string(), FunctionArgument::from_expressions(argv));
} else {
- Err(format!("variable '{repr}' not found"))
+ self.expr_type = ExpressionType::Variable(repr);
+ }
+ }
+
+ pub fn evaluate(&self, ctx: &Context) -> Result<Complex, String> {
+ match &self.expr_type {
+ ExpressionType::Value(v) => Ok(*v),
+ ExpressionType::Variable(var) => {
+ if let Some(v) = ctx.variable(&var) {
+ Ok(*v)
+ } else {
+ Err(format!("variable '{var}' not found"))
+ }
+ },
+ ExpressionType::Function(func, args) => {
+ if let Some(f) = ctx.function(&func) {
+ f.eval(args, ctx)
+ } else {
+ Err(format!("function '{func}' not found"))
+ }
+ }
+ ExpressionType::Operation(op, a, b) => {
+ Ok(op.evaluate(a.evaluate(ctx)?, b.evaluate(ctx)?))
+ }
}
}
}
diff --git a/src/math/expression_function.rs b/src/math/expression_function.rs
index 20f4816..44f83d1 100644
--- a/src/math/expression_function.rs
+++ b/src/math/expression_function.rs
@@ -5,6 +5,7 @@ use crate::math::{
complex::Complex,
context::Context,
expression::Expression,
+ operation::Operation,
function::{Function, FunctionArgument, EmptyFunction}
};
@@ -16,7 +17,7 @@ pub struct ExpressionFunction {
}
impl ExpressionFunction {
- pub fn from_string(str: String) -> Self {
+ pub fn from_string(str: String, operations: &Vec<Operation>) -> Self {
let str = str.replace(' ', "");
let (lhs, expr) = str.split_once('=').unwrap();
let (name, a) = lhs.split_once('(').unwrap();
@@ -25,7 +26,7 @@ impl ExpressionFunction {
.map(|s| s.to_string())
.collect();
Self {
- expr: Expression::from_string(expr),
+ expr: Expression::from_string(expr, operations),
name: name.to_string(),
args,
}
@@ -41,10 +42,11 @@ impl ExpressionFunction {
}
impl Function for ExpressionFunction {
- fn eval(&self, args: FunctionArgument, ctx: &Context) -> Result<Complex, String> {
+ fn eval(&self, args: &FunctionArgument, ctx: &Context) -> Result<Complex, String> {
if args.len() == self.args.len() {
let mut nctx = ctx.clone();
- for (n, v) in zip(self.args.iter(), args.data().iter()) {
+ let vargs = args.eval_args(ctx)?;
+ for (n, v) in zip(self.args.iter(), vargs.iter()) {
nctx.set_variable(n, *v)
}
nctx.set_function(self.name(), Arc::new(EmptyFunction {}));
diff --git a/src/math/function.rs b/src/math/function.rs
index bd782f3..93d288c 100644
--- a/src/math/function.rs
+++ b/src/math/function.rs
@@ -1,33 +1,51 @@
-use crate::math::{complex::Complex, context::Context};
+use super::expression::Expression;
+use super::{complex::Complex, context::Context};
+
+enum FunctionArgumentType {
+ ExpressionArgument(Vec<Expression>),
+ ValueArgument(Vec<Complex>),
+}
pub struct FunctionArgument {
- args : Vec<Complex>,
+ args: FunctionArgumentType,
+ len: usize,
}
impl FunctionArgument {
- pub fn new(args: Vec<Complex>) -> Self {
- Self { args }
+ pub fn from_expressions(args: Vec<Expression>) -> Self {
+ Self {
+ len: args.len(),
+ args: FunctionArgumentType::ExpressionArgument(args),
+ }
}
- pub fn get(&self, i : usize) -> Complex {
- self.args[i]
+ pub fn from_values(args: Vec<Complex>) -> Self {
+ Self {
+ len: args.len(),
+ args: FunctionArgumentType::ValueArgument(args),
+ }
}
pub fn len(&self) -> usize {
- self.args.len()
+ self.len
}
pub fn is_empty(&self) -> bool {
- self.args.len() == 0
+ self.len == 0
}
- pub fn data(&self) -> &[Complex] {
- &self.args
+ pub fn eval_args(&self, ctx: &Context) -> Result<Vec<Complex>, String> {
+ match &self.args {
+ FunctionArgumentType::ExpressionArgument(args) => {
+ args.iter().map(|a| a.evaluate(ctx)).collect()
+ }
+ FunctionArgumentType::ValueArgument(args) => Ok(args.to_vec()),
+ }
}
}
pub trait Function {
- fn eval(&self, args: FunctionArgument, ctx: &Context) -> Result<Complex, String>;
+ fn eval(&self, args: &FunctionArgument, ctx: &Context) -> Result<Complex, String>;
}
#[macro_export]
@@ -43,26 +61,27 @@ macro_rules! functions {
};
}
-
// Some default implementations
impl<T> Function for T
where
T: Fn(Complex) -> Complex,
{
- fn eval(&self, args: FunctionArgument, _ctx: &Context) -> Result<Complex, String> {
+ fn eval(&self, args: &FunctionArgument, ctx: &Context) -> Result<Complex, String> {
if args.len() == 1 {
- Ok(self(args.get(0)))
+ match args.eval_args(ctx) {
+ Ok(args) => Ok(self(args[0])),
+ Err(e) => Err(e),
+ }
} else {
Err("too many arguments".to_string())
}
}
}
-
pub struct EmptyFunction {}
impl Function for EmptyFunction {
- fn eval(&self, _args: FunctionArgument, _ctx: &Context) -> Result<Complex, String> {
+ fn eval(&self, _args: &FunctionArgument, _ctx: &Context) -> Result<Complex, String> {
Err("function not implemented in this scope".to_string())
}
}