# `math`

> Look up math.avg, math.min, math.max, math.derivative, the float checks, and the float constants of the Arc math module.

The `math` module gives running statistics and rates of change of a numeric value, and checks and constants for NaN and infinity. Import it with `import math`. `avg`, `min`, `max`, and `derivative` are flow functions: a function body cannot call them.

`avg`, `min`, and `max` share these parameters:

* [`duration`](https://docs.synnaxlabs.com/reference/control/arc/standard-library/math#duration)`i64 ns = 0`

  Start again when this much time has passed, measured on trigger timestamps.

* [`count`](https://docs.synnaxlabs.com/reference/control/arc/standard-library/math#count)`i64 = 0`

  Start again on the next trigger after this many values.

* [`reset`](https://docs.synnaxlabs.com/reference/control/arc/standard-library/math#reset)`bool = false`

  Has no effect in text mode, which cannot wire a trigger to it.

With `duration` and `count` at 0, the value covers every trigger since the stage started. A window starts again with the trigger that ends the old window. Synnax reads `duration` and `count` once, when the automation starts.

## `avg`

Gives the average of all trigger values since the window started.

* Trigger -> a numeric value.
* Output -> one value for each trigger batch, of the same type as the trigger. It has the timestamp of the last value in the batch. It is not a moving average.
* Runs -> on each new trigger. Its window starts again when its stage starts.

```arc
import math

tank_pt -> math.avg(duration=5s) -> tank_pt_avg
```

An integer trigger gives an integer average, rounded toward zero. On the Core, the sum uses the trigger type, so it can overflow and loses precision. Use a float trigger. A Driver sums in `f64`.

## `derivative`

Gives the rate of change of the trigger, in units per second.

* Trigger -> a numeric value.
* Output -> one `f64` value for each trigger value, with the same timestamp. The rate is the change from the previous value, divided by the time between their timestamps.
* Runs -> on each new trigger. The first value, and a value with a timestamp that is not later than the one before it, give 0. It starts again when its stage starts.

`derivative` has no parameters.

```arc
import math

tank_pt -> math.derivative() -> tank_pt_rate
```

## `max`

Gives the largest trigger value since the window started.

* Trigger -> a numeric value.
* Output -> one value for each trigger batch, of the same type as the trigger.
* Runs -> on each new trigger. Its window starts again when its stage starts.

```arc
import math

tank_pt -> math.max() -> tank_pt_max
```

## `min`

Gives the smallest trigger value since the window started.

* Trigger -> a numeric value.
* Output -> one value for each trigger batch, of the same type as the trigger.
* Runs -> on each new trigger. Its window starts again when its stage starts.

```arc
import math

tank_pt -> math.min(count=1000) -> tank_pt_min
```

## Float checks

* `is_nan(x)` -> `true` if `x` is NaN.
* `is_infinite(x)` -> `true` if `x` is positive or negative infinity.
* `is_finite(x)` -> `true` if `x` is not NaN and not infinite.

Each check takes one `f32` or `f64` value and gives a `bool`. An integer variable fails to compile. An integer literal is read as a float. Use a check in an expression, a stage condition, or a function body.

```arc
import math

sequence main {
    stage spin_down {
        not math.is_finite(pump_speed) or pump_speed < 15 => next
    }
    stage stopped {
        1 -> pump_stopped
    }
}
```

## Float constants

* `nan` -> the NaN value.
* `inf` -> the positive infinity value. `-math.inf` is negative infinity.

A constant fits `f32` or `f64`, and it defaults to `f64`. It does not fit an integer type, and a cast of one to an integer type is a compile error. Use a constant in an expression, such as `limit := math.inf * 2.0`, a `$=` initializer, or a function input. In a flow, a constant sends its value: `x -> math.nan -> y`.

```arc
import math

func lowest() {x f64} f64 {
    low f64 $= math.inf
    if x < low {
        low = x
    }
    return low
}
```

To test for NaN, use `is_nan`. `x != math.nan` is always `true`, and `==`, `<`, `>`, `<=`, or `>=` with `math.nan` is always `false`, so each fails with error ARC2004. Write `math.is_nan(x)` and `not math.is_nan(x)`.
