test_that("Newton step succeeds with PD Hessian (sphere)", {
  # Sphere function: H = I, g = random
  set.seed(123)
  n <- 5
  g <- rnorm(n)
  H <- diag(n)

  result <- try_newton_step(g, H)

  expect_true(result$success)
  # For H = I, Newton step should be s = -g
  expect_equal(result$s, -g, tolerance = 1e-10)
})

test_that("Newton step fails with indefinite Hessian", {
  # Indefinite Hessian with negative eigenvalue
  g <- c(1, 1)
  H <- matrix(c(1, 0, 0, -1), 2, 2)  # eigenvalues: 1, -1

  result <- try_newton_step(g, H)

  expect_false(result$success)
  expect_null(result$s)
})

test_that("Newton step succeeds with general PD Hessian", {
  # General positive definite Hessian
  g <- c(1, 2)
  H <- matrix(c(2, 0.5, 0.5, 3), 2, 2)  # PD: eigenvalues > 0

  result <- try_newton_step(g, H)

  expect_true(result$success)
  # Verify H * s = -g
  H_s <- as.vector(H %*% result$s)
  expect_equal(H_s, -g, tolerance = 1e-10)
})

test_that("Newton step computes correct solution", {
  # Verify s = -H^{-1} g
  g <- c(1, 2, 3)
  # Create random PD Hessian
  set.seed(456)
  A <- matrix(rnorm(9), 3, 3)
  H <- t(A) %*% A + diag(3)  # Guaranteed PD

  result <- try_newton_step(g, H)

  expect_true(result$success)
  # Verify s = -H^{-1} g by checking H * s = -g
  expected_s <- -solve(H, g)
  expect_equal(result$s, expected_s, tolerance = 1e-10)
})

test_that("Newton step handles nearly singular PD Hessian", {
  # Hessian with very small positive eigenvalue
  g <- c(1, 1)
  H <- matrix(c(1e-6, 0, 0, 1), 2, 2)

  result <- try_newton_step(g, H)

  # Should succeed since both eigenvalues are positive
  expect_true(result$success)
  expect_true(all(is.finite(result$s)))
})

test_that("Newton step uses Gershgorin bound correctly", {
  # Test case where Gershgorin bound is slightly negative
  # H = [[1, 1.1], [1.1, 1]]
  # Diagonal: 1, Off-diagonal sums: 1.1
  # Gershgorin bounds: 1 - 1.1 = -0.1 for both rows
  g <- c(1, 1)
  H <- matrix(c(1, 1.1, 1.1, 1), 2, 2)

  result <- try_newton_step(g, H)

  # Should fail due to Gershgorin bound <= 0
  expect_false(result$success)
  expect_null(result$s)
})

test_that("Newton step handles zero gradient", {
  # Zero gradient should give zero Newton step
  g <- c(0, 0, 0)
  H <- diag(3)

  result <- try_newton_step(g, H)

  expect_true(result$success)
  expect_equal(result$s, c(0, 0, 0), tolerance = 1e-10)
})

test_that("Newton step works on larger problem", {
  # Test on larger dimensional problem
  set.seed(789)
  n <- 10
  g <- rnorm(n)
  # Create diagonally dominant PD Hessian (guaranteed to pass Gershgorin)
  H <- diag(n) * 10 + matrix(rnorm(n * n, sd = 0.1), n, n)
  H <- (H + t(H)) / 2  # Make symmetric

  result <- try_newton_step(g, H)

  expect_true(result$success)
  # Verify solution
  H_s <- as.vector(H %*% result$s)
  expect_equal(H_s, -g, tolerance = 1e-9)
})

test_that("Newton step fails when Cholesky fails despite positive Gershgorin", {
  # Edge case: Gershgorin bound may be positive but matrix still non-PD
  # This is rare but theoretically possible
  # Create a matrix that passes Gershgorin but is not PD
  # [[2, 0], [0, -0.5]] won't work because diagonal is negative
  # Try [[0.1, 0], [0, -0.05]] - Gershgorin: 0.1, -0.05
  # Actually, for this to happen, we need off-diagonal dominance
  # Let's use a known non-PD matrix
  g <- c(1, 1)
  H <- matrix(c(0.01, 0, 0, -0.005), 2, 2)

  result <- try_newton_step(g, H)

  # Should fail because H has negative eigenvalue
  expect_false(result$success)
})

# Tests for predicted reduction computation

test_that("Newton step computes correct predicted reduction", {
  g <- c(2, 4)
  H <- matrix(c(2, 0, 0, 2), 2, 2)

  result <- try_newton_step(g, H)

  expect_true(result$success)
  expect_equal(result$s, c(-1, -2), tolerance = 1e-10)
  expect_equal(result$pred_reduction, 5.0, tolerance = 1e-10)
})

test_that("Newton predicted reduction is always positive for PD Hessian", {
  set.seed(999)
  n <- 5
  g <- rnorm(n)
  # Create diagonally dominant PD Hessian (guaranteed to pass Gershgorin)
  H <- diag(n) * 10 + matrix(rnorm(n * n, sd = 0.1), n, n)
  H <- (H + t(H)) / 2  # Make symmetric

  result <- try_newton_step(g, H)

  expect_true(result$success)
  expect_true(result$pred_reduction > 0)
})

test_that("Newton predicted reduction is NULL when step fails", {
  g <- c(1, 1)
  H <- matrix(c(1, 0, 0, -1), 2, 2)

  result <- try_newton_step(g, H)

  expect_false(result$success)
  expect_null(result$pred_reduction)
})

test_that("Newton predicted reduction matches quadratic model formula", {
  g <- c(1, 2, 3)
  set.seed(456)
  # Create diagonally dominant PD Hessian (guaranteed to pass Gershgorin)
  H <- diag(3) * 10 + matrix(rnorm(9, sd = 0.1), 3, 3)
  H <- (H + t(H)) / 2  # Make symmetric

  result <- try_newton_step(g, H)
  s <- result$s

  manual_pred <- -(sum(g * s) + 0.5 * sum(s * (H %*% s)))

  expect_equal(result$pred_reduction, manual_pred, tolerance = 1e-10)
})
