# Contributing to `perpetual`

## Development Setup

The repository comes with a predefined set of development tools in the `Makefile` which can be listed by running `make help` or `make`. Before you start contributing or developing further it is required that you initialize the artifacts and dependencies needed for conducting the unit-tests. To do this run the following command:

```sh
make init
```

For development, it is assumed you have stable Rust installed, and at least Python 3.10.

### Running tests

To run the tests in the repository use `make test` - this will run all tests available in the repository. For Python or Rust specific tests that has no downstream or upstream changes use `make py-test` or `make rust-test`, respectively.

## Benchmarking

Benchmarking is run using the [`criterion`](https://github.com/bheisler/criterion.rs) Rust crate.
To run the benchmarks, you can run the following command from your terminal.

```sh
cargo bench
```

specific benchmarks can be targeted by referring to them by name.

```sh
cargo bench "fast sum"
```

## Pre-commit

The [`pre-commit`](https://pre-commit.com/) framework should be installed and used to ensure all commits meet the required formatting, and linting checks prior to a commit being made to the repository. This is installed by `make init` too.

## Serialization

The saving and loading of the model is all handled by the [`serde`](https://docs.rs/serde/1.0.163/serde/) and [`serde_json`](https://docs.rs/serde_json/latest/serde_json/) crates.

Because of this you will see the following attribue calls sprinkled throughout the package.

```rust
#[derive(Deserialize, Serialize)]
```

Additionally in order to not break backwards compatibility with models saved in previous versions, any new items added to the `Tree` or `PerpetualBooster` struts, should have a default value defined. This way models can be loaded, even if they were saved before the new fields was added.
A default value can be added for a fields using the `#[#[serde(default = "default_sample_method")]]` attribute. Where the string that default is referring to must be the name of a valid function, the following is a complete example of this.

```rust
use crate::sampler::SampleMethod, Sampler;

#[derive(Deserialize, Serialize)]
pub struct PerpetualBooster {
    // ...
    #[serde(default = "default_sample_method")]
    pub sample_method: SampleMethod,
    // ...
}

fn default_sample_method() -> SampleMethod {
    SampleMethod::None
}
```
