# Functors

`rpnfunctor.h` is a library of small, stateless "`Fw`" (functor-wrapper) structs, each implementing exactly one operator or math primitive as a templated `operator()` plus a matching `ok()` precondition check. They exist to be template parameters: as the header's own comment notes, passing a plain function pointer through a template defeats inlining, while a functor *type* lets the generic dispatch templates in [builtin operators and functions](rpnbuiltin.md) fully inline the whole scalar computation into the surrounding element loop.

## The `operator()`/`ok()` pairing

Every functor answers two questions: "is this input valid" (`ok()`, which can report a [`Status`](status.md) failure) and "compute the result" (`operator()`, which cannot fail). `ok()` is always called and checked before `operator()`, so `operator()` itself never has to guard its own domain - it can assume the caller already validated it. `FwDivide` and `FwSqrt` are the clearest examples: `FwDivide::ok()` rejects a zero divisor with `Status::DivZero`, `FwSqrt::ok()` rejects a negative argument with `Status::Domain`, and both `operator()`s are then trivial one-liners. Functors with no way to fail (most of them) simply have `ok()` return `true` unconditionally.

## Generic vs. hard-typed vs. overloaded

Most functors are templated over their operand type(s) (`template<typename T>` / `template<typename T1, typename T2>`), so one functor serves `Int`, `Real`, and mixed `Int`/`Real` operands - the concrete instantiation and its result type are pinned down by the caller via `decltype(functor(...))`. A few (`FwBitAnd`, `FwBitOr`, `FwBitExor`, `FwBitNot`, `FwBitShiftRight`, `FwBitShiftLeft`) are hard-typed to `Int` only, since bitwise operations are undefined on `Real`; the dispatch tables that use them in `rpnbuiltin.h` only ever instantiate them for `Int`/`IntVector`.

`FwMin`, `FwMax`, and `FwSgn` are overloaded per concrete type pair instead of templated, because they need genuinely different behavior per combination - `FwMin(Real, Int)` must promote its result to `Real` while `FwMin(Int, Int)` stays `Int` - which is more direct to write as explicit overloads than to derive from one generic template.

## Truthiness: `makeBool()`

A free helper (not a functor) giving one consistent truthiness rule across `Int`, `Real`, and `String` scalars - nonzero, nonzero, and non-empty respectively - used by `FwAnd`, `FwOr`, `FwNot`, `FwAll`, and `FwAny`. It mirrors `Value::checkCondition()` ([Values](value.md)) at the level of individual C++ scalars rather than a whole `Value`.

## Aggregate functors

`FwAll`, `FwAny`, `FwSum`, `FwProd`, `FwMinAggregate`, `FwMaxAggregate` each write their `operator()` once, generically, to handle either a scalar or a vector `T` via `if constexpr(Value::IsVectorType<Tin>::value)` - collapsing what would otherwise be two near-duplicate bodies (loop-and-combine for vectors, direct pass-through for scalars) into one function selected at compile time. `FwProd`/`FwMinAggregate`/`FwMaxAggregate` reject an empty vector in `ok()` with `Status::BadArguments`, since "the product/minimum/maximum of nothing" has no defined value - unlike `FwSum` (identity `0`) or `FwAll`/`FwAny` (vacuously true/false over zero elements).

## `FwSelector`

Backs the `x[i]` selector operator (`Rpn::OpSelect`, see [expression format](rpnexpr.md)). `operator()` supports negative indices counting back from the end (`x[x.size()+sel]` when `sel<0`, Python-style); `ok()` range-checks both directions before that arithmetic runs.

## Monte Carlo distribution shapes

`FwGauss`, `FwAgauss`, `FwUnif`, `FwAunif` back the `gauss`/`agauss`/`unif`/`aunif` builtins (see [Context](context.md)'s builtins table). Each turns one already-drawn uniform `(0,1)` sample (supplied by `MCData`, see [the netlist context](rpnevalctx.md)) into one draw from the requested distribution:

- `FwGauss`/`FwAgauss` go through `r8_normal_01_cdf_inverse()` (inverse normal CDF, from `asa241`) for a Gaussian shape, scaled by relative (`rvar`, a fraction of the nominal value) or absolute (`avar`) sigma.
- `FwUnif`/`FwAunif` instead scale the `(0,1)` draw linearly into `nom +- rvar*nom` or `nom +- avar`.

All four fall back to returning the nominal value untouched when the requested variation or sigma is non-positive, rather than treating that as an error - "no variation" degenerating to the deterministic nominal case lets a netlist override a Monte Carlo parameter to a fixed value for one instance without special-casing it at the call site.

## `isInf`/`isNaN`/`isFinite`

`FwIsInf`, `FwIsNan`, `FwIsFinite` wrap `isInf()`/`isNaN()`/`isFinite()` from `libplatform.h` rather than calling `std::isinf`/`std::isnan`/`std::isfinite` directly, for platform-portable floating-point classification.
