# Comprehensive math and numeric helper tests engine <- make_engine() # ============================================================================ # Numeric Helpers # ============================================================================ test_that("numeric helpers inc/dec/clamp/within? work", { env <- new.env() toplevel_env(engine, env = env) expect_equal(get("inc", envir = env)(5), 6) expect_equal(get("inc", envir = env)(5, 2), 7) expect_equal(get("dec", envir = env)(5), 4) expect_equal(get("dec", envir = env)(5, 2), 3) expect_equal(get("clamp", envir = env)(5, 1, 10), 5) expect_equal(get("clamp", envir = env)(-1, 0, 10), 0) expect_equal(get("clamp", envir = env)(11, 0, 10), 10) expect_true(get("within?", envir = env)(5, 1, 10)) expect_false(get("within?", envir = env)(0, 1, 10)) expect_false(get("within?", envir = env)(11, 1, 10)) }) # ============================================================================ # Complex Numbers # ============================================================================ test_that("type coercion functions work", { env <- new.env(parent = emptyenv()) toplevel_env(engine, env = env) expect_equal(engine$eval(engine$read("(exact->inexact 5)")[[1]], env = env), 5.0) expect_equal(engine$eval(engine$read("(inexact->exact 5.7)")[[1]], env = env), 6L) expect_equal(engine$eval(engine$read("(->integer \"42\")")[[1]], env = env), 42L) expect_equal(engine$eval(engine$read("(->double 5)")[[1]], env = env), 5.0) }) test_that("complex number utilities work", { env <- new.env(parent = emptyenv()) toplevel_env(engine, env = env) z <- engine$eval(engine$read("(make-rectangular 3 4)")[[1]], env = env) expect_equal(Re(z), 3.0) expect_equal(Im(z), 4.0) expect_equal(engine$eval(engine$read("(real-part (make-rectangular 3 4))")[[1]], env = env), 3.0) expect_equal(engine$eval(engine$read("(imag-part (make-rectangular 3 4))")[[1]], env = env), 4.0) expect_equal(engine$eval(engine$read("(magnitude (make-rectangular 3 4))")[[1]], env = env), 5.0) }) # ============================================================================ # Edge Cases: Boundary Conditions for Numeric Operations # ============================================================================ test_that("numeric operations handle boundary conditions", { env <- new.env() toplevel_env(engine, env = env) # Large numbers large <- 1e100 expect_equal(get("=", envir = env)(large, large), TRUE) # Negative numbers expect_equal(get("%", envir = env)(-10, 3), -10 %% 3) # Zero expect_equal(get("%", envir = env)(0, 5), 0) # Floating point (uses exact equality, not all.equal) expect_false(get("=", envir = env)(0.1 + 0.2, 0.3)) # floating point precision issues expect_true(get("=", envir = env)(1.0, 1.0)) }) test_that("division by zero returns Inf", { env <- new.env(parent = emptyenv()) toplevel_env(engine, env = env) # In R, division by zero returns Inf, not an error expect_equal(engine$eval(engine$read("(/ 1 0)")[[1]], env = env), Inf) expect_equal(engine$eval(engine$read("(/ -10 0)")[[1]], env = env), -Inf) }) # ============================================================================ # Coverage: Variadic comparison operators with 1 arg (error paths) # ============================================================================ test_that("variadic comparison operators return #t with 0 or 1 arguments (vacuously true)", { env <- new.env(parent = emptyenv()) toplevel_env(engine, env = env) # 1 argument: vacuously true expect_true(engine$eval(engine$read("(< 1)")[[1]], env = env)) expect_true(engine$eval(engine$read("(> 1)")[[1]], env = env)) expect_true(engine$eval(engine$read("(<= 1)")[[1]], env = env)) expect_true(engine$eval(engine$read("(>= 1)")[[1]], env = env)) expect_true(engine$eval(engine$read("(== 1)")[[1]], env = env)) # 0 arguments: vacuously true expect_true(engine$eval(engine$read("(<)")[[1]], env = env)) expect_true(engine$eval(engine$read("(>)")[[1]], env = env)) expect_true(engine$eval(engine$read("(<=)")[[1]], env = env)) expect_true(engine$eval(engine$read("(>=)")[[1]], env = env)) expect_true(engine$eval(engine$read("(==)")[[1]], env = env)) }) # ============================================================================ # Coverage: Arithmetic/stats with 0 args (error paths) # ============================================================================ test_that("variadic arithmetic operators error with 0 arguments", { env <- new.env(parent = emptyenv()) toplevel_env(engine, env = env) expect_error(engine$eval(engine$read("(-)")[[1]], env = env), "requires at least one argument") expect_error(engine$eval(engine$read("(/)")[[1]], env = env), "requires at least one argument") # min/max with 0 args now fall through to R's native min/max (Inf/-Inf with warning) expect_warning( expect_equal(engine$eval(engine$read("(min)")[[1]], env = env), Inf), "no non-missing arguments" ) expect_warning( expect_equal(engine$eval(engine$read("(max)")[[1]], env = env), -Inf), "no non-missing arguments" ) # gcd with 0 args returns 0 (identity element) expect_equal(engine$eval(engine$read("(gcd)")[[1]], env = env), 0) # lcm with 0 args returns 1 (identity element) expect_equal(engine$eval(engine$read("(lcm)")[[1]], env = env), 1) }) # ============================================================================ # Coverage: Number predicate edge cases # ============================================================================ test_that("number predicate edge cases cover remaining lines", { env <- new.env(parent = emptyenv()) toplevel_env(engine, env = env) # integer? with 3.0 (finite, == as.integer) -> #t expect_true(engine$eval(engine$read("(integer? 3.0)")[[1]], env = env)) # rational? with finite real -> #t expect_true(engine$eval(engine$read("(rational? 1.5)")[[1]], env = env)) # rational? with infinite -> #f expect_false(engine$eval(engine$read("(rational? Inf)")[[1]], env = env)) # inexact? with double -> #t expect_true(engine$eval(engine$read("(inexact? 3.14)")[[1]], env = env)) # inexact? with non-number -> #f expect_false(engine$eval(engine$read("(inexact? \"hi\")")[[1]], env = env)) }) # ============================================================================ # Coverage: Uncalled math functions (expt, atan2) # ============================================================================ test_that("expt and atan2 work", { env <- new.env(parent = emptyenv()) toplevel_env(engine, env = env) expect_equal(engine$eval(engine$read("(expt 2 10)")[[1]], env = env), 1024) expect_equal( engine$eval(engine$read("(atan2 1.0 1.0)")[[1]], env = env), atan2(1.0, 1.0) ) })