---
title: "Logical Rules"
output: rmarkdown::html_vignette
vignette: >
  %\VignetteIndexEntry{Logical Rules}
  %\VignetteEngine{knitr::rmarkdown}
  %\VignetteEncoding{UTF-8}
---

This vignette shows how to define observed responses with `first_of()`,
`all_of()`, `none_of()`, and `inhibit()`. Use these helpers when a response is
not a direct readout from a single accumulator.

```{r}
library(AccumulatR)
```

## What a response rule does

An outcome rule states when an observed response should count as having
happened. Common cases include:

- one of several routes being able to generate it
- several processes all being complete
- the absence of some competing event
- a stop-like process that can cancel an otherwise available response

The logical helpers encode those cases directly in the model definition.

## The four main helpers

The main helpers are:

- `first_of(x, y)` finishes when the first of `x` and `y` finishes
- `all_of(x, y)` finishes when both `x` and `y` have finished
- `none_of(x)` requires `x` not to have finished before its enclosing rule
- `inhibit(x, y)` finishes with `x` unless `y` finishes strictly earlier


## `first_of()`: either route can generate the response

Use `first_of()` when one of several routes can generate the same response.

```{r}
model_first <- race_spec() |>
  add_accumulator("word_route", "lognormal") |>
  add_accumulator("picture_route", "lognormal") |>
  add_outcome("target_detected", first_of("word_route", "picture_route")) |>
  finalize_model()

names(model_first$prep$outcomes)
```

## `all_of()`: several things must all be true

Use `all_of()` when a response depends on several processes all being complete.

```{r}
model_all <- race_spec() |>
  add_accumulator("encoding", "lognormal") |>
  add_accumulator("verification", "lognormal") |>
  add_outcome("confirmed_response", all_of("encoding", "verification")) |>
  finalize_model()

names(model_all$prep$outcomes)
```

## `none_of()`: the response requires the competing event to be absent

Use `none_of()` inside `all_of()` to require that another event has not
occurred before the conjunction completes.

```{r}
model_none <- race_spec() |>
  add_accumulator("go", "lognormal") |>
  add_accumulator("stop", "lognormal") |>
  add_outcome("respond", all_of("go", none_of("stop"))) |>
  finalize_model()

names(model_none$prep$outcomes)
```

The response occurs when `go` finishes, provided `stop` has not finished earlier.

## `inhibit()`: the response is driven by one process and blocked by another

Use `inhibit()` when one process would generate the response and another can
block it.

```{r}
model_inhibit <- race_spec() |>
  add_accumulator("go", "lognormal") |>
  add_accumulator("stop", "lognormal") |>
  add_outcome("respond", inhibit("go", "stop")) |>
  finalize_model()

names(model_inhibit$prep$outcomes)
```

Here `stop` cancels the response only if it finishes before `go`.

## `none_of()` versus `inhibit()`

These two rules look similar:

```{r}
rule_none <- all_of("go", none_of("stop"))
rule_inhibit <- inhibit("go", "stop")

c(absence_rule = rule_none$kind, inhibit_rule = rule_inhibit$kind)
```

For these two distinct accumulators, they give the same response rule: `go`
completes before `stop`. The absence condition is evaluated when the enclosing
conjunction completes, not over the entire trial.

Nesting matters. `all_of("a", "b", none_of("stop"))` checks absence when both
`a` and `b` have completed. In `all_of(inhibit("a", "stop"), "b")`, inhibition
is decided when `a` completes; a later stop does not undo that event.

`none_of()` needs an event at which to evaluate absence. Use it inside
`all_of()` with an event that generates a response; a bare `none_of()` cannot
be an outcome or a standalone branch of `first_of()`.

## A combined example

The helpers can be combined when a response depends on several kinds of logic
at once. For example, suppose a response requires:

- an encoding stage to finish
- either a left route or a right route to generate the choice
- no stop event before those requirements have completed

```{r}
model_combo <- race_spec() |>
  add_accumulator("encoding", "lognormal") |>
  add_accumulator("left_route", "lognormal") |>
  add_accumulator("right_route", "lognormal") |>
  add_accumulator("stop", "lognormal") |>
  add_outcome(
    "choice",
    all_of(
      "encoding",
      first_of("left_route", "right_route"),
      none_of("stop")
    )
  ) |>
  finalize_model()

names(model_combo$prep$outcomes)
```
