tests/testthat/test-stdlib-math.R

# Comprehensive math and numeric helper tests

engine <- make_engine()

# ============================================================================
# Numeric Helpers
# ============================================================================

thin <- make_cran_thinner()

test_that("numeric helpers inc/dec/clamp/within? work", {
  thin()
  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", {
  thin()
  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", {
  thin()
  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", {
  thin()
  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", {
  thin()
  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)", {
  thin()
  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", {
  thin()
  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", {
  thin()
  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", {
  thin()
  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)
  )
})

Try the arl package in your browser

Any scripts or data that you put into this service are public.

arl documentation built on March 19, 2026, 5:09 p.m.