---
title: "05 - Extending 'caracas'"
output: rmarkdown::html_vignette
vignette: >
  %\VignetteIndexEntry{05 - Extending 'caracas'}
  %\VignetteEngine{knitr::rmarkdown}
  %\VignetteEncoding{UTF-8}
---


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

```{r, message=FALSE}
library(caracas)
```

```{r, include = FALSE}
inline_code <- function(x) {
  x
}

if (!has_sympy()) {
  # SymPy not available, so the chunks shall not be evaluated
  knitr::opts_chunk$set(eval = FALSE)
  
  inline_code <- function(x) {
    deparse(substitute(x))
  }
}
```

It is relatively easy to extend `caracas` by calling `SymPy` functions
directly. 

This can be achived using `sympy_func(x, fun, ...)` that calls a member 
function on the object provided, i.e. `x$fun(...)`, or if that fails it 
calls a function from the global namespace `fun(x, ...)`.

As an example consider inverting a regular matrix $A$: Let $B$ be the
inverse of $A$. Then, using cofactors, $B_{ij} =C_{ji} / det(A)$. The
cofactor $C_{ij}$ is given as $C_{ij}=(-1)^{i+j}M_{ij}$ where $M_{ij}$
is the determinant of the submatrix of $A$ obtained by deleting the
$i$th row and the $j$th column of $A$. 

A quick search
https://docs.sympy.org/latest/modules/matrices/matrices.html shows
that there are two relevant functions in `SymPy`: `cofactor` and
`cofactor_matrix`.

If these functions are not available in `caracas` they can be made so using `sympy_func`:

```{r}
cofactor_matrix <- function(x) {
  sympy_func(x, "cofactor_matrix")
}

cofactor <- function(x, i, j) {
  # Python indexing starts at 0 - thus subtract 1 to convert from R indexing
  # to Python indexing
  sympy_func(x, "cofactor", i - 1, j - 1)
}
```

```{r}
A <- matrix_sym(3, 3, "a")
```

```{r}
CC <- cofactor_matrix(A)
CC
cc <- cofactor(A, 1, 1)
cc
```

We get the right answer
```{r}
B <- t(CC) / det(A)
P <- A %*% B
P %>% simplify()
```

