---
title: "The Solver API"
output: rmarkdown::html_vignette
vignette: >
  %\VignetteIndexEntry{The Solver API}
  %\VignetteEngine{knitr::rmarkdown}
  %\VignetteEncoding{UTF-8}
---

```{r, include = FALSE}
knitr::opts_chunk$set(collapse = TRUE, comment = "#>")
```

While `highs_solve()` is convenient for one-shot problems, the solver
API gives fine-grained control for building models incrementally,
modifying problems, and performing warm-starts.

## Creating a Solver

Build a model and create a solver from it:

```{r create}
library(highs)

model <- highs_model(
  L = c(2, 4, 3),
  lower = 0,
  A = matrix(c(3, 4, 2, 2, 1, 2, 1, 3, 2), nrow = 3, byrow = TRUE),
  rhs = c(60, 40, 80),
  maximum = TRUE
)
solver <- hi_new_solver(model)
```

## Solving and Retrieving Results

```{r solve}
hi_solver_run(solver)
hi_solver_status_message(solver)
sol <- hi_solver_get_solution(solver)
sol$col_value
```

## Solver Information

After solving, retrieve detailed solver information:

```{r info}
info <- hi_solver_info(solver)
info$objective_function_value
info$simplex_iteration_count
info$primal_solution_status
```

## Modifying the Problem

Change objective coefficients, bounds, and constraints without
rebuilding from scratch:

```{r modify}
# Change the objective coefficient for variable 1 (0-based indexing)
hi_solver_set_objective(solver, index = 0L, coeff = 10.0)

# Change variable bounds
hi_solver_set_variable_bounds(solver, index = 0L, lower = 1.0, upper = 20.0)

# Change constraint bounds
hi_solver_set_constraint_bounds(solver, index = 0L, lower = -Inf, upper = 50.0)

# Re-solve
hi_solver_run(solver)
hi_solver_get_solution(solver)$col_value
```

## Adding Variables and Constraints

Grow the model dynamically:

```{r add}
solver <- hi_new_solver(model)

# Add a new variable with bounds [0, 15]
hi_solver_add_vars(solver, lower = 0, upper = 15)
cat("Columns after add:", hi_solver_get_num_col(solver), "\n")

# Add a new constraint: x1 + x2 + x3 + x4 <= 100
hi_solver_add_rows(solver,
  lhs = -Inf, rhs = 100,
  start = 0L,
  index = 0L:3L,
  value = rep(1.0, 4)
)
cat("Rows after add:", hi_solver_get_num_row(solver), "\n")
```

## Querying Model Data

```{r query}
solver <- hi_new_solver(model)
hi_solver_get_lp_costs(solver)
hi_solver_get_num_col(solver)
hi_solver_get_num_row(solver)
```

## The `highs_solver()` Wrapper

For an object-oriented style, use `highs_solver()` which returns an
environment with methods:

```{r wrapper}
hw <- highs_solver(model)
hw$solve()
hw$status_message()
hw$solution()$col_value
hw$info()$objective_function_value

# Modify and re-solve
hw$L(1, 10.0)     # set cost of variable 1 to 10
hw$vbounds(1, 1, 20)  # set bounds of variable 1 to [1, 20]
hw$solve()
hw$solution()$col_value
```

## Reading and Writing Models

```{r io}
solver <- hi_new_solver(model)
temp <- tempfile(fileext = ".mps")
hi_solver_write_model(solver, temp)

# Read into a fresh solver
solver2 <- hi_new_solver(highs_model(L = 0))
hi_solver_read_model(solver2, temp)
hi_solver_run(solver2)
hi_solver_get_solution(solver2)$col_value
unlink(temp)
```

## Version and Timing

```{r version}
solver <- hi_new_solver(model)
hi_solver_version(solver)
hi_solver_run(solver)
hi_solver_get_run_time(solver)
```
