Added converge builtin function
This commit is contained in:
parent
e53832ca4b
commit
1e5e9a3ae9
@ -16,7 +16,8 @@ Features
|
|||||||
Supported operators: `!` `!=` `""` `''` `()` `[]` `,` `>` `<` `>=` `<=` `==`
|
Supported operators: `!` `!=` `""` `''` `()` `[]` `,` `>` `<` `>=` `<=` `==`
|
||||||
`+` `-` `*` `/` `%` `&&` `||` `n..m`.
|
`+` `-` `*` `/` `%` `&&` `||` `n..m`.
|
||||||
|
|
||||||
Built-in functions: `min()` `max()` `len()` `is_empty()` `array()`.
|
Built-in functions: `min()` `max()` `len()` `is_empty()` `array()` `converge()`.
|
||||||
|
See the `builtin` module for a detailed description of each.
|
||||||
|
|
||||||
Where can eval be used?
|
Where can eval be used?
|
||||||
-----------------------
|
-----------------------
|
||||||
|
@ -14,10 +14,35 @@ impl BuiltIn {
|
|||||||
functions.insert("len".to_owned(), create_len_fuction());
|
functions.insert("len".to_owned(), create_len_fuction());
|
||||||
functions.insert("is_empty".to_owned(), create_is_empty_fuction());
|
functions.insert("is_empty".to_owned(), create_is_empty_fuction());
|
||||||
functions.insert("array".to_owned(), create_array_function());
|
functions.insert("array".to_owned(), create_array_function());
|
||||||
|
functions.insert("converge".to_owned(), create_converge_function());
|
||||||
functions
|
functions
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn expect_number_f64(value: &Value) -> Result<f64, Error> {
|
||||||
|
if let Some(number) = value.as_f64() {
|
||||||
|
Ok(number)
|
||||||
|
} else {
|
||||||
|
Err(Error::Custom(format!("Expected number that can be represented as f64. But the given is: {:?}", value)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn expect_normal_f64(number: f64) -> Result<f64, Error> {
|
||||||
|
if number.is_normal() {
|
||||||
|
Ok(number)
|
||||||
|
} else {
|
||||||
|
Err(Error::Custom(format!("Expected normal number. But the given is: {:?}", number)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn expect_finite_f64(number: f64) -> Result<f64, Error> {
|
||||||
|
if number.is_finite() {
|
||||||
|
Ok(number)
|
||||||
|
} else {
|
||||||
|
Err(Error::Custom(format!("Expected finite number. But the given is: {:?}", number)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(PartialEq)]
|
#[derive(PartialEq)]
|
||||||
enum Compare {
|
enum Compare {
|
||||||
Min,
|
Min,
|
||||||
@ -116,3 +141,60 @@ fn create_len_fuction() -> Function {
|
|||||||
fn create_array_function() -> Function {
|
fn create_array_function() -> Function {
|
||||||
Function::new(|values| Ok(to_value(values)))
|
Function::new(|values| Ok(to_value(values)))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Converges exponentially agains `conv_y`, starting from `start_y`.
|
||||||
|
/// The first four parameters parameterize a function `f`, the last one is the `x`-value where the function should be evaluated.
|
||||||
|
/// The result of the call is `f(x)`.
|
||||||
|
///
|
||||||
|
/// The parameters `start_x` and `start_y` set the starting point of the function.
|
||||||
|
/// It holds that `f(start_x) = start_y`.
|
||||||
|
/// The parameter `conv_y` is the convergence target value.
|
||||||
|
/// It is never reached (assuming no numerical errors).
|
||||||
|
/// The parameter `step_x` is the "speed" of the convergence, that is how fast the function converges against `conv_y`.
|
||||||
|
/// In detail, the absolute difference between `start_y` and `conv_y` halves if `x` is increased by `step_x`.
|
||||||
|
///
|
||||||
|
/// All parameters are expected to be numbers.
|
||||||
|
/// The parameters `start_x`, `start_y` and `conv_y` are expected to be finite.
|
||||||
|
/// The parameter `step_x` is expected to be `normal`.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// The function `2^(-x)` is expressed as `conv(0, 1, 1, 0, x)`.
|
||||||
|
/// This is the same as `converge(1, 0.5, 1, 0, x)`.
|
||||||
|
fn create_converge_function() -> Function {
|
||||||
|
Function {
|
||||||
|
max_args: Some(5),
|
||||||
|
min_args: Some(5),
|
||||||
|
compiled: Box::new(|values| {
|
||||||
|
let start_x = expect_finite_f64(expect_number_f64(&values[0])?)?;
|
||||||
|
let start_y = expect_finite_f64(expect_number_f64(&values[1])?)?;
|
||||||
|
let step_x = expect_normal_f64(expect_number_f64(&values[2])?)?;
|
||||||
|
let conv_y = expect_finite_f64(expect_number_f64(&values[3])?)?;
|
||||||
|
let x = expect_number_f64(&values[4])?;
|
||||||
|
|
||||||
|
let units = (x - start_x) / step_x;
|
||||||
|
let interpolation_factor = 2.0_f64.powf(-units);
|
||||||
|
Ok(to_value(interpolation_factor * start_y + (1.0 - interpolation_factor) * conv_y))
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test {
|
||||||
|
use crate::{Expr, to_value};
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_conv() {
|
||||||
|
let valid_test_cases = vec![
|
||||||
|
("converge(0, 4, 10, 0, 0)", "4.0"),
|
||||||
|
("converge(0, 4, 10, 0, 10)", "2.0"),
|
||||||
|
("converge(0, 4, 10, 0, 5)", "2.8284271247461900976033774484193961571393437507538961"),
|
||||||
|
("converge(0, 4, 10, 0, 20)", "1.0"),
|
||||||
|
("converge(0, 4, 10, 0, 0-10)", "8.0"),
|
||||||
|
];
|
||||||
|
|
||||||
|
for (term, result) in valid_test_cases {
|
||||||
|
assert_eq!(Expr::new(format!("({term} < {result} * 1.0001) && ({term} > {result} / 1.0001)", term = term, result = result)).exec(), Ok(to_value(true)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -3,7 +3,8 @@
|
|||||||
//! Supported operators: `!` `!=` `""` `''` `()` `[]` `.` `,` `>` `<` `>=` `<=`
|
//! Supported operators: `!` `!=` `""` `''` `()` `[]` `.` `,` `>` `<` `>=` `<=`
|
||||||
//! `==` `+` `-` `*` `/` `%` `&&` `||` `n..m`.
|
//! `==` `+` `-` `*` `/` `%` `&&` `||` `n..m`.
|
||||||
//!
|
//!
|
||||||
//! Built-in functions: `min()` `max()` `len()` `is_empty()` `array()`.
|
//! Built-in functions: `min()` `max()` `len()` `is_empty()` `array()` `converge()`.
|
||||||
|
//! See the `builtin` module for a detailed description of each.
|
||||||
//!
|
//!
|
||||||
//! ## Examples
|
//! ## Examples
|
||||||
//!
|
//!
|
||||||
|
Loading…
Reference in New Issue
Block a user