---
title: "Advanced Usage of rcpptimer"
author: Jonathan Berrisch
date: "`r Sys.Date()`"
output:
  rmarkdown::html_vignette:
    number_sections: no
    toc: no
vignette: >
  %\VignetteIndexEntry{Advanced Usage of rcpptimer}
  %\VignetteEngine{knitr::rmarkdown}
  %\VignetteEncoding{UTF-8}
---

## Accessing Unprocessed Data

The `stop()` method returns the aggregated data as a `DataFrame`. However, you can also access the raw data if you need to. The `timer` class stores the timings in two vectors: `durations` and `tags`, both public class members. The snippet below demonstrates how to access them: 

```{r, eval = TRUE}
Rcpp::cppFunction('
DataFrame demo_rnorm()
{
  Rcpp::Timer timer;
  double x=0;
  for(int i = 0; i < 7; i++)
  {
    timer.tic("rnorm");
    x += rnorm(1, 1)[0];
    timer.toc("rnorm");
  }
  DataFrame times = DataFrame::create(
          Named("Durations") = timer.durations,
          Named("Tags") = timer.tags);
  return(times);
}',
  depends = "rcpptimer"
)

demo_rnorm()
```

You can see that the `tags` vector contains the names of the timings, and the `durations` vector contains the actual timings (in nanoseconds).

## Updating the Results

Sometimes, you may want to access the results of your `Timer` instance before all timers have finished. This is possible. Due to the way the `Timer` class processes the data, it is also very efficient. The `.aggregate()` method (called by `.stop()`) will update the results if new timings have been observed. The snippet below demonstrates this:

```c++
List test_update()
{
  Rcpp::Timer timer;
  timer.autoreturn = false;
  List L = List::create();
  {
    Rcpp::Timer::ScopedTimer scoped_timer(timer, "t1");
    timer.tic("t2");
    std::this_thread::sleep_for(std::chrono::nanoseconds(5));
    timer.toc("t2");
    DataFrame results1 = timer.stop();
    timer.print_warnings();
    L.push_back(results1);
    timer.tic("t2");
    std::this_thread::sleep_for(std::chrono::nanoseconds(500));
    timer.toc("t2");
    timer.tic("t3");
    std::this_thread::sleep_for(std::chrono::nanoseconds(500));
    timer.toc("t3");
  }
  DataFrame results2 = timer.stop();
  L.push_back(results2);
  return (L);
}
```

We use the above function in rcpptimer for testing purposes:

```{r, eval = TRUE}
rcpptimer:::test_update()
```

You can see that the timer "t2" has been updated with the new timing. This example also shows that it is unnecessary to stop all timers before calling `.stop()`. In this example, timer "t1" is started before calculating the results for the first time, but it is stopped before calling `.stop()` a second time. 

Also, note that we need to manually call `.print_warnings()` if we want to print warnings early. Otherwise, they will only be printed upon destruction of the `Timer` instance (e.g., when your `Timer` object goes out of scope).

## Resetting the Timer

You can reset the timer at any time by calling the `reset()` method. This method will clear your instance of the `Timer` class and reset the internal state. The example below demonstrates how to use it:

```c++
List test_reset()
{
  Rcpp::Timer timer;
  {
    Rcpp::Timer::ScopedTimer scoped_timer(timer, "t1");
    timer.autoreturn = false;
    timer.tic("t2");
    std::this_thread::sleep_for(std::chrono::nanoseconds(5));
    timer.toc("t2");
  }
  DataFrame results1 = timer.stop();
  timer.reset();
  timer.tic("t3");
  List L = List::create();
  L.push_back(results1);
  timer.toc("t3");
  DataFrame results2 = timer.stop();
  L.push_back(results2);
  return (L);
}
```

We use the above function in rcpptimer for testing purposes:

```{r, eval = TRUE}
rcpptimer:::test_reset()
```