# Saving and loading Armadillo objects on C++ side

This vignette is adapted from the official Armadillo documentation.

# Saving and loading matrices and cubes

Armadillo provides functions to save and load matrices and cubes to and from files. The following file types are supported:

- `arma_binary`: Numerical data stored in machine dependent binary format, with a simple header to speed up loading. The header indicates the type and size of matrix/cube.
- `arma_ascii`: Numerical data stored in human readable text format, with a simple header to speed up loading. The header indicates the type and size of matrix/cube.
- `raw_binary`: Numerical data stored in machine dependent raw binary format, without a header. Matrices are loaded to have one column, while cubes are loaded to have one slice with one column. The `.reshape()` function can be used to alter the size of the loaded matrix/cube without losing data.
- `raw_ascii`: Numerical data stored in raw ASCII format, without a header. The numbers are separated by whitespace. The number of columns must be the same in each row. Cubes are loaded as one slice. Data which was saved in Matlab/Octave using the `-ascii` option can be read in Armadillo, except for complex numbers. Complex numbers are stored in standard C++ notation, which is a tuple surrounded by brackets: eg. `(1.23,4.56)` indicates 1.24 + 4.56i.
- `csv_ascii`: Numerical data stored in comma separated value (CSV) text format, without a header. To save/load with a header, use the `csv_name(filename, header)` specification instead. Handles complex numbers stored in the compound form of `1.24+4.56i`. Applicable to `Mat` and `SpMat`.
- `coord_ascii`: Numerical data stored as a text file in coordinate list format, without a header. Only non-zero values are stored. For real matrices, each line contains information in the following format: `row column value`. For complex matrices, each line contains information in the following format: `row column real_value imag_value`. The rows and columns start at zero. Applicable to `Mat` and `SpMat`.
- `pgm_binary`: Image data stored in Portable Gray Map (PGM) format. Applicable to `Mat` only. Saving int, float or double matrices is a lossy operation, as each element is copied and converted to an 8 bit representation. As such the matrix should have values in the `[0,255]` interval, otherwise the resulting image may not display correctly.
- `ppm_binary`: Image data stored in Portable Pixel Map (PPM) format. Applicable to `Cube` only. Saving int, float or double matrices is a lossy operation, as each element is copied and converted to an 8 bit representation. As such the cube/field should have values in the `[0,255]` interval, otherwise the resulting image may not display correctly.
- `hdf5_binary`: Numerical data stored in portable HDF5 binary format. For saving, the default dataset name within the HDF5 file is `"dataset"`. For loading, the order of operations is: (1) try loading a dataset named `"dataset"`, (2) try loading a dataset named `"value"`, (3) try loading the first available dataset. To explicitly control the dataset name, specify it via the `hdf5_name(filename, dataset)` argument. HDF5 support can be enabled by defining `ARMA_USE_HDF5` before including the Armadillo header.

The following file types are supported for fields:

- `arma_binary`: See above.
- `ppm_binary`: See above.

Usage:

    .save(filename)
    .load(filename)

    .save(filename, file_type)
    .load(filename, file_type)

    .save(stream)
    .load(stream)

    .save(stream, file_type)
    .load(stream, file_type)

    .save(hdf5_name(filename, dataset))
    .load(hdf5_name(filename, dataset))

    .save(hdf5_name(filename, dataset, settings))
    .load(hdf5_name(filename, dataset, settings))

    .save(csv_name(filename, header))
    .load(csv_name(filename, header))

    .save(csv_name(filename, header, settings))
    .load(csv_name(filename, header, settings))

By providing either `hdf5_name(filename, dataset)` or `hdf5_name(filename, dataset, settings)`, the `file_type` type is assumed to be `hdf5_binary`. The `dataset` argument specifies an HDF5 dataset name (eg. `"my_dataset"`) that can include a full path (eg. `"/group_name/my_dataset"`); if a blank dataset name is specified (ie. `""`), it is assumed to be `"dataset"`. The `settings` argument is optional; it is one of the following, or a combination thereof

- `hdf5_opts::trans`: Save/load the data with columns transposed to rows (and vice versa).
- `hdf5_opts::append`: Instead of overwriting the file, append the specified dataset to the file; the specified dataset must not already exist in the file.
- `hdf5_opts::replace`: Instead of overwriting the file, replace the specified dataset in the file.
- These settings can be combined using the `+` operator (e.g., `hdf5_opts::trans + hdf5_opts::append`)

By providing either `csv_name(filename, header)` or `csv_name(filename, header, settings)`, the file is assumed to have data in comma separated value (CSV) text format. The `header` argument specifies the object which stores the separate elements of the header line; it must have the type `field<std::string>`. The optional `settings` argument is one of the following, or a combination thereof

- `csv_opts::trans`: Save/load the data with columns transposed to rows (and vice versa).
- `csv_opts::no_header`: Assume there is no header line; the header argument is not referenced.
- `csv_opts::semicolon`: Use semicolon (`;`) instead of comma (`,`) as the separator character.
- `csv_opts::strict`: Interpret missing values as `NaN` (not applicable to sparse matrices).
- These settings can be combined using the `+` operator (e.g., `csv_opts::trans + csv_opts::no_header`)

## Caveats

- For saving/loading HDF5 files, support for HDF5 must be enabled within Armadillo’s configuration. The `hdf5.h` header file must be available on your system and you will need to link with the HDF5 library (eg. `-lhdf5`). HDF5 support can be enabled by defining `ARMA_USE_HDF5` before including the Armadillo header.
- Enabling HD5 requires to vendor cpp11armadillo in orderto adhere to CRAN compliance.
- Armadillo save and load methods are only accessible from C++ side.
- Once an object is exported to R, it is more efficient to load/save using `saveRDS()` and `readRDS()`.

## Examples

Save and load matrices:

    [[cpp11::register]] int saveload1_(const int& n) {
      arma::mat A(n, n, fill::randu);

      // default save format is arma_binary
      A.save("A.bin");

      // save in raw_ascii format
      A.save("A.txt", arma::raw_ascii);

      // save in CSV format without a header
      A.save("A.csv", arma::csv_ascii);

      // save in CSV format with a header
      arma::field<std::string> header(A.n_cols);
      header(0) = "foo";
      header(1) = "bar";  // etc
      A.save(arma::csv_name("A.csv", header));

      // save in HDF5 format with internal dataset named as "my_data"
      // see the caveats
      // A.save(arma::hdf5_name("A.h5", "my_data"));

      // automatically detect format type while loading
      arma::mat B;
      B.load("A.bin");

      // force loading in arma_ascii format
      arma::mat C;
      C.load("A.txt", arma::arma_ascii);

      // example of testing for success
      arma::mat D;
      bool ok = D.load("A.bin");

      if(ok == true) {
        message("Matrix loaded successfully");
      } else {
        stop("Problem with loading");
      }

      return 0;
    }

Save and load fields:

    [[cpp11::register]] int saveload2_(const int& n) {
      arma::field<arma::mat> F(n);

      for (int i = 0; i < n; i++) {
        F(i) = arma::mat(n, n, fill::randu);
      }

      // default save format is arma_binary
      F.save("F.bin");

      // save in PPM format
      F.save("F.ppm", arma::ppm_binary);

      // automatically detect format type while loading
      arma::field<arma::mat> G;
      G.load("F.bin");

      return 0;
    }