#[path = "support/mod.rs"]
mod test_helpers;
use runmat_parser::parse;
use test_helpers::execute;
use test_helpers::lower;
#[test]
fn closure_simple_no_capture() {
let ast = parse("f = @(x) x + 1; y = feval(f, 2);").unwrap();
let hir = lower(&ast).unwrap();
let vars = execute(&hir).unwrap();
assert!(vars
.iter()
.any(|v| matches!(v, runmat_builtins::Value::Num(n) if (*n - 3.0).abs() < 1e-9)));
}
#[test]
fn closure_captures_free_variables() {
let ast = parse("a=1; b=2; f=@(x) x + a + b; y = feval(f, 3);").unwrap();
let hir = lower(&ast).unwrap();
let vars = execute(&hir).unwrap();
assert!(vars
.iter()
.any(|v| matches!(v, runmat_builtins::Value::Num(n) if (*n - 6.0).abs() < 1e-9)));
}
#[test]
fn nested_closures_capture_outer() {
let ast = parse("a=10; f=@(x) @(y) (x + y + a); g = feval(f, 2); r = feval(g, 3);").unwrap();
let hir = lower(&ast).unwrap();
let vars = execute(&hir).unwrap();
assert!(vars
.iter()
.any(|v| matches!(v, runmat_builtins::Value::Num(n) if (*n - 15.0).abs() < 1e-9)));
}
#[test]
fn feval_with_string_handle() {
let ast = parse("r = feval('@max', 2, 5);").unwrap();
let hir = lower(&ast).unwrap();
let vars = execute(&hir).unwrap();
assert!(vars
.iter()
.any(|v| matches!(v, runmat_builtins::Value::Num(n) if (*n - 5.0).abs() < 1e-9)));
}
#[test]
fn fzero_accepts_anonymous_function() {
let ast = parse("f = @(x) cos(x) - x; r = fzero(f, 0.5);").unwrap();
let hir = lower(&ast).unwrap();
let vars = execute(&hir).unwrap();
assert!(vars
.iter()
.any(|v| matches!(v, runmat_builtins::Value::Num(n) if (*n - 0.7390851332).abs() < 1e-6)));
}
#[test]
fn fzero_accepts_optimset_options() {
let ast =
parse("opts = optimset('TolX', 1e-10, 'Display', 'off'); r = fzero(@sin, [3 4], opts);")
.unwrap();
let hir = lower(&ast).unwrap();
let vars = execute(&hir).unwrap();
assert!(vars.iter().any(
|v| matches!(v, runmat_builtins::Value::Num(n) if (*n - std::f64::consts::PI).abs() < 1e-8)
));
}
#[test]
fn integral_accepts_anonymous_function() {
let ast = parse("q = integral(@(x) x.^2, 0, 1);").unwrap();
let hir = lower(&ast).unwrap();
let vars = execute(&hir).unwrap();
assert!(vars
.iter()
.any(|v| matches!(v, runmat_builtins::Value::Num(n) if (*n - (1.0 / 3.0)).abs() < 1e-8)));
}
#[test]
fn integral_accepts_named_function_handle() {
let ast = parse("q = integral(@sin, 0, pi);").unwrap();
let hir = lower(&ast).unwrap();
let vars = execute(&hir).unwrap();
assert!(vars
.iter()
.any(|v| matches!(v, runmat_builtins::Value::Num(n) if (*n - 2.0).abs() < 1e-7)));
}
#[test]
fn fminbnd_accepts_anonymous_function() {
let ast = parse("x = fminbnd(@(x) (x-2).^2, 0, 5);").unwrap();
let hir = lower(&ast).unwrap();
let vars = execute(&hir).unwrap();
assert!(vars
.iter()
.any(|v| matches!(v, runmat_builtins::Value::Num(n) if (*n - 2.0).abs() < 1e-3)));
}
#[test]
fn fminbnd_returns_optional_function_value() {
let ast = parse("[x, fval] = fminbnd(@(x) (x-3).^2 + 1, 0, 5);").unwrap();
let hir = lower(&ast).unwrap();
let vars = execute(&hir).unwrap();
let x_ok = vars
.iter()
.any(|v| matches!(v, runmat_builtins::Value::Num(n) if (*n - 3.0).abs() < 1e-3));
let fval_ok = vars
.iter()
.any(|v| matches!(v, runmat_builtins::Value::Num(n) if (*n - 1.0).abs() < 1e-5));
assert!(x_ok, "expected x ≈ 3 in {vars:?}");
assert!(fval_ok, "expected fval ≈ 1 in {vars:?}");
}
#[test]
fn fminbnd_accepts_optimset_options() {
let ast =
parse("opts = optimset('TolX', 1e-10, 'Display', 'off'); x = fminbnd(@cos, 0, pi, opts);")
.unwrap();
let hir = lower(&ast).unwrap();
let vars = execute(&hir).unwrap();
assert!(vars.iter().any(
|v| matches!(v, runmat_builtins::Value::Num(n) if (*n - std::f64::consts::PI).abs() < 1e-3)
));
}
#[test]
fn fsolve_accepts_anonymous_vector_function() {
let ast =
parse("F = @(x) [x(1)^2 + x(2)^2 - 4; x(1)*x(2) - 1]; x = fsolve(F, [1; 1]);").unwrap();
let hir = lower(&ast).unwrap();
let vars = execute(&hir).unwrap();
assert!(vars.iter().any(|v| {
if let runmat_builtins::Value::Tensor(t) = v {
t.data.len() == 2
&& (t.data[0] * t.data[0] + t.data[1] * t.data[1] - 4.0).abs() < 1e-5
&& (t.data[0] * t.data[1] - 1.0).abs() < 1e-5
} else {
false
}
}));
}
#[test]
fn ode45_accepts_anonymous_rhs_function() {
let ast = parse("y = ode45(@(t, y) -y, [0 1], 1);").unwrap();
let hir = lower(&ast).unwrap();
let vars = execute(&hir).unwrap();
assert!(vars.iter().any(|v| {
if let runmat_builtins::Value::Tensor(t) = v {
t.cols() == 1 && (t.data[t.rows() - 1] - (-1.0_f64).exp()).abs() < 1e-2
} else {
false
}
}));
}
#[test]
fn ode23_accepts_two_output_assignment() {
let ast = parse("[t, y] = ode23(@(t, y) -2*y, [0 0.25 0.5 1.0], 1);").unwrap();
let hir = lower(&ast).unwrap();
let vars = execute(&hir).unwrap();
assert!(vars.iter().any(|v| {
if let runmat_builtins::Value::Tensor(tensor) = v {
tensor.cols() == 1
&& tensor.rows() == 4
&& (tensor.data[tensor.rows() - 1] - (-2.0_f64).exp()).abs() < 2e-2
} else {
false
}
}));
}