# Elementwise and Reductions

> NumKong for C++

`#include <numkong/each.hpp>`

`#include <numkong/reduce.hpp>`

`#include <numkong/trigonometry.hpp>`

`#include <numkong/tensor.hpp>`

`#include <numkong/types.hpp>`

## moments_result

Result of moments(): Σxᵢ and Σxᵢ².

```cpp
template <typename sum_type_,
          typename sumsq_type_>
struct moments_result
```

- `sum` `sum_type_`
- `sumsq` `sumsq_type_`

## minmax_result

Result of minmax(): min/max values with their indices.

```cpp
template <typename minmax_value_type_>
struct minmax_result
```

- `min_value` `minmax_value_type_`
- `min_index` `std::size_t`
- `max_value` `minmax_value_type_`
- `max_index` `std::size_t`

## add

Elementwise addition: cᵢ = aᵢ + bᵢ

```cpp
template <numeric_dtype in_type_>
status_t add(
    in_type_ const *a,
    in_type_ const *b,
    std::size_t d,
    in_type_ *c,
    nk_capability_t capabilities = default_capabilities(),
    nk_stream_t stream = nullptr) noexcept;
```

```cpp
template <numeric_dtype in_type_, vector_of<in_type_> a_type_, vector_of<in_type_> b_type_, mutable_vector_of<in_type_> output_type_>
status_t add(
    a_type_ const & a,
    b_type_ const & b,
    output_type_ && output,
    nk_capability_t capabilities = default_capabilities(),
    nk_stream_t stream = nullptr) noexcept;
```

Elementwise cᵢ = aᵢ + bᵢ over runs of equal dimensions; [`unexpected_dimensions_k`](https://www.unum.cloud/docs/numkong/cpp/quickstart.md#unexpected_dimensions_k) when they differ or any run is strided or ends mid-value.

```cpp
template <numeric_dtype value_type_, std::size_t max_rank_ = 8>
status_t add(
    tensor_view<value_type_, max_rank_> lhs,
    tensor_view<value_type_, max_rank_> rhs,
    tensor_span<value_type_, max_rank_> output) noexcept;
```

Elementwise addition: each output is the sum of the two operands.

```cpp
template <numeric_dtype value_type_, std::size_t max_rank_ = 8, typename allocator_type_ = aligned_allocator<value_type_>>
expected<tensor<value_type_, allocator_type_, max_rank_>> add(
    tensor_view<value_type_, max_rank_> lhs,
    tensor_view<value_type_, max_rank_> rhs,
    allocator_type_ alloc = {}) noexcept;
```

Allocating elementwise add: the result is the sum of the two operands; fails like the allocating [`blend`](#blend).

```cpp
template <numeric_dtype value_type_, std::size_t max_rank_ = 8>
status_t add(
    tensor_view<value_type_, max_rank_> input,
    typename value_type_::scale_t scalar,
    tensor_span<value_type_, max_rank_> output) noexcept;
```

Elementwise add scalar: output[i] = input[i] + scalar.

```cpp
template <numeric_dtype value_type_, std::size_t max_rank_ = 8, typename allocator_type_ = aligned_allocator<value_type_>>
expected<tensor<value_type_, allocator_type_, max_rank_>> add(
    tensor_view<value_type_, max_rank_> input,
    typename value_type_::scale_t scalar,
    allocator_type_ alloc = {}) noexcept;
```

Allocating add scalar; fails like the allocating [`scale`](#scale).

- `a` (in): First input vector
- `b` (in): Second input vector
- `d` (in): Number of dimensions in input vectors
- `c` (out): Output vector
- `capabilities` (in): Capabilities to pick from, or zero for the C++ template
- `stream` (in): Null on the CPU, or the stream of the device `capabilities` describes

## scale

Elementwise scale: cᵢ = α × aᵢ + β

```cpp
template <numeric_dtype in_type_, numeric_dtype precision_type_ = in_type_>
status_t scale(
    in_type_ const *a,
    std::size_t d,
    typename in_type_::scale_t const *alpha,
    typename in_type_::scale_t const *beta,
    in_type_ *c,
    nk_capability_t capabilities = default_capabilities(),
    nk_stream_t stream = nullptr) noexcept;
```

```cpp
template <numeric_dtype in_type_, vector_of<in_type_> a_type_, mutable_vector_of<in_type_> output_type_>
requires (!requires(a_type_ const &tensor) { tensor.rank(); })
status_t scale(
    a_type_ const & a,
    typename in_type_::scale_t alpha,
    typename in_type_::scale_t beta,
    output_type_ && output,
    nk_capability_t capabilities = default_capabilities(),
    nk_stream_t stream = nullptr) noexcept;
```

Elementwise cᵢ = α × aᵢ + β, failing like the concept [`add`](#add).

```cpp
template <numeric_dtype value_type_, std::size_t max_rank_ = 8>
status_t scale(
    tensor_view<value_type_, max_rank_> input,
    typename value_type_::scale_t alpha,
    typename value_type_::scale_t beta,
    tensor_span<value_type_, max_rank_> output) noexcept;
```

Scale: output[i] = α × input[i] + β.

```cpp
template <numeric_dtype value_type_, std::size_t max_rank_ = 8, typename allocator_type_ = aligned_allocator<value_type_>>
expected<tensor<value_type_, allocator_type_, max_rank_>> scale(
    tensor_view<value_type_, max_rank_> input,
    typename value_type_::scale_t alpha,
    typename value_type_::scale_t beta,
    allocator_type_ alloc = {}) noexcept;
```

Allocating scale: result[i] = α × input[i] + β; empty for an empty `input`, or the allocation's or the kernel's failure.

- `a` (in): Input vector
- `d` (in): Number of dimensions in input vector
- `alpha` (in): Scale coefficient
- `beta` (in): Shift coefficient
- `c` (out): Output vector
- `capabilities` (in): Capabilities to pick from, or zero for the C++ template
- `stream` (in): Null on the CPU, or the stream of the device `capabilities` describes

## blend

Blend: cᵢ = α × aᵢ + β × bᵢ

```cpp
template <numeric_dtype in_type_, numeric_dtype precision_type_ = in_type_>
status_t blend(
    in_type_ const *a,
    in_type_ const *b,
    std::size_t d,
    typename in_type_::scale_t const *alpha,
    typename in_type_::scale_t const *beta,
    in_type_ *c,
    nk_capability_t capabilities = default_capabilities(),
    nk_stream_t stream = nullptr) noexcept;
```

```cpp
template <numeric_dtype in_type_, vector_of<in_type_> a_type_, vector_of<in_type_> b_type_, mutable_vector_of<in_type_> output_type_>
requires (!requires(a_type_ const &tensor) { tensor.rank(); })
status_t blend(
    a_type_ const & a,
    b_type_ const & b,
    typename in_type_::scale_t alpha,
    typename in_type_::scale_t beta,
    output_type_ && output,
    nk_capability_t capabilities = default_capabilities(),
    nk_stream_t stream = nullptr) noexcept;
```

Elementwise cᵢ = α × aᵢ + β × bᵢ, failing like the concept [`add`](#add).

```cpp
template <numeric_dtype value_type_, std::size_t max_rank_ = 8>
status_t blend(
    tensor_view<value_type_, max_rank_> lhs,
    tensor_view<value_type_, max_rank_> rhs,
    typename value_type_::scale_t alpha,
    typename value_type_::scale_t beta,
    tensor_span<value_type_, max_rank_> output) noexcept;
```

Blend: each output is α times the left operand plus β times the right operand.

```cpp
template <numeric_dtype value_type_, std::size_t max_rank_ = 8, typename allocator_type_ = aligned_allocator<value_type_>>
expected<tensor<value_type_, allocator_type_, max_rank_>> blend(
    tensor_view<value_type_, max_rank_> lhs,
    tensor_view<value_type_, max_rank_> rhs,
    typename value_type_::scale_t alpha,
    typename value_type_::scale_t beta,
    allocator_type_ alloc = {}) noexcept;
```

Allocating blend: each result is α times the left operand plus β times the right operand; [`unexpected_dimensions_k`](https://www.unum.cloud/docs/numkong/cpp/quickstart.md#unexpected_dimensions_k) when the operands disagree, empty for empty ones, or the allocation's or the kernel's failure.

- `a` (in): First input vector
- `b` (in): Second input vector
- `d` (in): Number of dimensions in input vectors
- `alpha` (in): Weight coefficient of `a`
- `beta` (in): Weight coefficient of `b`
- `c` (out): Output vector
- `capabilities` (in): Capabilities to pick from, or zero for the C++ template
- `stream` (in): Null on the CPU, or the stream of the device `capabilities` describes

## fma

Elementwise FMA: outᵢ = α × aᵢ × bᵢ + β × cᵢ

```cpp
template <numeric_dtype in_type_, numeric_dtype precision_type_ = in_type_>
status_t fma(
    in_type_ const *a,
    in_type_ const *b,
    in_type_ const *c,
    std::size_t d,
    typename in_type_::scale_t const *alpha,
    typename in_type_::scale_t const *beta,
    in_type_ *out,
    nk_capability_t capabilities = default_capabilities(),
    nk_stream_t stream = nullptr) noexcept;
```

```cpp
template <numeric_dtype in_type_, vector_of<in_type_> a_type_, vector_of<in_type_> b_type_, vector_of<in_type_> c_type_, mutable_vector_of<in_type_> output_type_>
requires (!requires(a_type_ const &tensor) { tensor.rank(); })
status_t fma(
    a_type_ const & a,
    b_type_ const & b,
    c_type_ const & c,
    typename in_type_::scale_t alpha,
    typename in_type_::scale_t beta,
    output_type_ && output,
    nk_capability_t capabilities = default_capabilities(),
    nk_stream_t stream = nullptr) noexcept;
```

Elementwise outᵢ = α × aᵢ × bᵢ + β × cᵢ, failing like the concept [`add`](#add).

```cpp
template <numeric_dtype value_type_, std::size_t max_rank_ = 8>
status_t fma(
    tensor_view<value_type_, max_rank_> lhs,
    tensor_view<value_type_, max_rank_> rhs,
    tensor_view<value_type_, max_rank_> addend,
    typename value_type_::scale_t alpha,
    typename value_type_::scale_t beta,
    tensor_span<value_type_, max_rank_> output) noexcept;
```

FMA: each output is α times the product of both operands plus β times the addend.

```cpp
template <numeric_dtype value_type_, std::size_t max_rank_ = 8, typename allocator_type_ = aligned_allocator<value_type_>>
expected<tensor<value_type_, allocator_type_, max_rank_>> fma(
    tensor_view<value_type_, max_rank_> lhs,
    tensor_view<value_type_, max_rank_> rhs,
    tensor_view<value_type_, max_rank_> addend,
    typename value_type_::scale_t alpha,
    typename value_type_::scale_t beta,
    allocator_type_ alloc = {}) noexcept;
```

Allocating FMA: each result is α times the product of both operands plus β times the addend; fails like the allocating [`blend`](#blend).

- `a` (in): First input vector
- `b` (in): Second input vector
- `c` (in): Third input vector
- `d` (in): Number of dimensions in input vectors
- `alpha` (in): Coefficient of the product
- `beta` (in): Coefficient of `c`
- `out` (out): Output vector
- `capabilities` (in): Capabilities to pick from, or zero for the C++ template
- `stream` (in): Null on the CPU, or the stream of the device `capabilities` describes

## swiglu

Fused SwiGLU: yᵢ = silu(gateᵢ · `gate_scale`) · upᵢ · `output_scale`.

```cpp
template <numeric_dtype in_type_>
status_t swiglu(
    in_type_ const *gate,
    in_type_ const *up,
    in_type_ *y,
    std::size_t rows,
    std::size_t columns,
    std::size_t gate_stride,
    std::size_t up_stride,
    std::size_t y_stride,
    f32_t gate_scale = 1.0f,
    f32_t output_scale = 1.0f,
    nk_capability_t capabilities = default_capabilities(),
    nk_stream_t stream = nullptr) noexcept;
```

```cpp
template <numeric_dtype value_type_>
status_t swiglu(
    matrix_view<value_type_> gate,
    matrix_view<value_type_> up,
    matrix_span<value_type_> output,
    f32_t gate_scale = 1.0f,
    f32_t output_scale = 1.0f) noexcept;
```

Fused SwiGLU over ____[rows,columns] matrices into a matching output span — `up` empty means SiLU; [`unexpected_dimensions_k`](https://www.unum.cloud/docs/numkong/cpp/quickstart.md#unexpected_dimensions_k) when the shapes disagree.

```cpp
template <numeric_dtype value_type_, typename allocator_type_ = aligned_allocator<value_type_>>
expected<tensor<value_type_, allocator_type_, 2>> swiglu(
    matrix_view<value_type_> gate,
    matrix_view<value_type_> up,
    f32_t gate_scale = 1.0f,
    f32_t output_scale = 1.0f,
    allocator_type_ alloc = {}) noexcept;
```

Allocating SwiGLU returning a fresh matrix — `up` empty means SiLU; empty for an empty `gate`, or the allocation's or the kernel's failure.

With a null `up` this reduces to plain SiLU.

- `gate` (in): Gate input, shape ____[rows,columns]
- `up` (in): Up input, same shape as `gate`; `nullptr` collapses to plain SiLU
- `y` (out): Output, same shape and dtype as `gate`; may alias `gate`
- `rows` (in): Logical row count
- `columns` (in): Logical column count
- `gate_stride` (in): Row stride of `gate` in bytes
- `up_stride` (in): Row stride of `up` in bytes
- `y_stride` (in): Row stride of `y` in bytes
- `gate_scale` (in): Scalar applied to each loaded gate element, the E4M3 descale or 1.0
- `output_scale` (in): Scalar applied to each result before it is stored
- `capabilities` (in): Capabilities to pick from, or zero for the C++ template
- `stream` (in): Null on the CPU, or the stream of the device `capabilities` describes

## rmsnorm

Grouped RMSNorm: yᵢ = xᵢ · rsqrt(mean(x²) + epsilon) · gammaᵢ

```cpp
template <numeric_dtype in_type_>
status_t rmsnorm(
    in_type_ const *x,
    f32_t const *gamma,
    in_type_ *y,
    std::size_t rows,
    std::size_t groups,
    std::size_t columns,
    std::size_t x_stride,
    std::size_t y_stride,
    f32_t epsilon,
    nk_capability_t capabilities = default_capabilities(),
    nk_stream_t stream = nullptr) noexcept;
```

```cpp
template <numeric_dtype value_type_>
status_t rmsnorm(
    matrix_view<value_type_> input,
    vector_view<f32_t> gamma,
    matrix_span<value_type_> output,
    std::size_t groups,
    f32_t epsilon) noexcept;
```

Grouped RMSNorm over a matrix of [`rows`](https://www.unum.cloud/docs/numkong/cpp/matrix-kernels.md#rows) rows, `groups` groups and [`columns`](https://www.unum.cloud/docs/numkong/cpp/tensors-and-views.md#columns) columns, into a matching output span; [`unexpected_dimensions_k`](https://www.unum.cloud/docs/numkong/cpp/quickstart.md#unexpected_dimensions_k) when the shapes, the groups or `gamma` disagree with each other.

```cpp
template <numeric_dtype value_type_, typename allocator_type_ = aligned_allocator<value_type_>>
expected<tensor<value_type_, allocator_type_, 2>> rmsnorm(
    matrix_view<value_type_> input,
    vector_view<f32_t> gamma,
    std::size_t groups,
    f32_t epsilon,
    allocator_type_ alloc = {}) noexcept;
```

Allocating grouped RMSNorm returning a fresh matrix, empty for an empty `input`, or the allocation's or the kernel's failure.

Each row holds `groups` independent `columns-vectors`, normalized separately.

- `x` (in): Input matrix, shaped ____[rows,groups,columns], each group packed contiguously
- `gamma` (in): Per-column gain, length [`columns`](https://www.unum.cloud/docs/numkong/cpp/tensors-and-views.md#columns), shared by groups; `nullptr` is unit scale.
- `y` (out): Output matrix, same shape and dtype as `x`; may alias `x`
- `rows` (in): Logical row count
- `groups` (in): Logical group count
- `columns` (in): Logical column count
- `x_stride` (in): Row (outer) stride of `x` in bytes
- `y_stride` (in): Row (outer) stride of `y` in bytes
- `epsilon` (in): Variance epsilon added before the reciprocal square root
- `capabilities` (in): Capabilities to pick from, or zero for the C++ template
- `stream` (in): Null on the CPU, or the stream of the device `capabilities` describes

## sub

Elementwise subtraction: each output is the left operand minus the right operand.

```cpp
template <numeric_dtype value_type_, std::size_t max_rank_ = 8>
status_t sub(
    tensor_view<value_type_, max_rank_> lhs,
    tensor_view<value_type_, max_rank_> rhs,
    tensor_span<value_type_, max_rank_> output) noexcept;
```

```cpp
template <numeric_dtype value_type_, std::size_t max_rank_ = 8, typename allocator_type_ = aligned_allocator<value_type_>>
expected<tensor<value_type_, allocator_type_, max_rank_>> sub(
    tensor_view<value_type_, max_rank_> lhs,
    tensor_view<value_type_, max_rank_> rhs,
    allocator_type_ alloc = {}) noexcept;
```

Allocating elementwise sub; fails like the allocating [`blend`](#blend).

```cpp
template <numeric_dtype value_type_, std::size_t max_rank_ = 8>
status_t sub(
    tensor_view<value_type_, max_rank_> input,
    typename value_type_::scale_t scalar,
    tensor_span<value_type_, max_rank_> output) noexcept;
```

Elementwise sub scalar: output[i] = input[i] − scalar.

```cpp
template <numeric_dtype value_type_, std::size_t max_rank_ = 8, typename allocator_type_ = aligned_allocator<value_type_>>
expected<tensor<value_type_, allocator_type_, max_rank_>> sub(
    tensor_view<value_type_, max_rank_> input,
    typename value_type_::scale_t scalar,
    allocator_type_ alloc = {}) noexcept;
```

Allocating sub scalar; fails like the allocating [`scale`](#scale).

## mul

Elementwise multiplication: each output is the product of the two operands.

```cpp
template <numeric_dtype value_type_, std::size_t max_rank_ = 8>
status_t mul(
    tensor_view<value_type_, max_rank_> lhs,
    tensor_view<value_type_, max_rank_> rhs,
    tensor_span<value_type_, max_rank_> output) noexcept;
```

```cpp
template <numeric_dtype value_type_, std::size_t max_rank_ = 8, typename allocator_type_ = aligned_allocator<value_type_>>
expected<tensor<value_type_, allocator_type_, max_rank_>> mul(
    tensor_view<value_type_, max_rank_> lhs,
    tensor_view<value_type_, max_rank_> rhs,
    allocator_type_ alloc = {}) noexcept;
```

Allocating elementwise multiply; fails like the allocating [`blend`](#blend).

```cpp
template <numeric_dtype value_type_, std::size_t max_rank_ = 8>
status_t mul(
    tensor_view<value_type_, max_rank_> input,
    typename value_type_::scale_t scalar,
    tensor_span<value_type_, max_rank_> output) noexcept;
```

Elementwise multiply by scalar: output[i] = input[i] × scalar.

```cpp
template <numeric_dtype value_type_, std::size_t max_rank_ = 8, typename allocator_type_ = aligned_allocator<value_type_>>
expected<tensor<value_type_, allocator_type_, max_rank_>> mul(
    tensor_view<value_type_, max_rank_> input,
    typename value_type_::scale_t scalar,
    allocator_type_ alloc = {}) noexcept;
```

Allocating multiply by scalar; fails like the allocating [`scale`](#scale).

## reduce_moments

Computes sum and sum of squares in a single pass: sum = Σ dataᵢ, sumsq = Σ dataᵢ².

```cpp
template <numeric_dtype in_type_, numeric_dtype sum_type_ = typename in_type_::reduce_moments_sum_t, numeric_dtype sumsq_type_ = typename in_type_::reduce_moments_sumsq_t>
status_t reduce_moments(
    in_type_ const *data,
    std::size_t count,
    std::size_t stride,
    sum_type_ *sum,
    sumsq_type_ *sumsq,
    nk_capability_t capabilities = default_capabilities(),
    nk_stream_t stream = nullptr) noexcept;
```

```cpp
template <numeric_dtype in_type_, numeric_dtype sum_type_ = typename in_type_::reduce_moments_sum_t, numeric_dtype sumsq_type_ = typename in_type_::reduce_moments_sumsq_t, vector_of<in_type_> input_type_>
expected<moments_result<sum_type_, sumsq_type_>> reduce_moments(
    input_type_ const & input,
    nk_capability_t capabilities = default_capabilities(),
    nk_stream_t stream = nullptr) noexcept;
```

Σxᵢ and Σxᵢ² over one run of `in_type_`; [`unexpected_dimensions_k`](https://www.unum.cloud/docs/numkong/cpp/quickstart.md#unexpected_dimensions_k) for a strided run or one ending mid-value.

- `data` (in): Input array.
- `count` (in): Counts dimensions, a multiple of the values per byte.
- `stride` (in): Stride between elements in bytes, `sizeof(in_type_)` for contiguous.
- `sum` (out): Output sum.
- `sumsq` (out): Output sum of squares.
- `capabilities` (in): Capabilities to pick from, or zero for the C++ template.
- `stream` (in): Null on the CPU, or the stream of the device `capabilities` describes.

## reduce_minmax

Find minimum and maximum elements with their indices in a single pass.

```cpp
template <numeric_dtype in_type_, numeric_dtype minmax_type_ = typename in_type_::reduce_minmax_value_t>
status_t reduce_minmax(
    in_type_ const *data,
    std::size_t count,
    std::size_t stride,
    minmax_type_ *min_value,
    std::size_t *min_index,
    minmax_type_ *max_value,
    std::size_t *max_index,
    nk_capability_t capabilities = default_capabilities(),
    nk_stream_t stream = nullptr) noexcept;
```

```cpp
template <numeric_dtype in_type_, numeric_dtype minmax_type_ = typename in_type_::reduce_minmax_value_t, vector_of<in_type_> input_type_>
expected<minmax_result<minmax_type_>> reduce_minmax(
    input_type_ const & input,
    nk_capability_t capabilities = default_capabilities(),
    nk_stream_t stream = nullptr) noexcept;
```

Minimum and maximum, with their indices, over one run of `in_type_`; [`unexpected_dimensions_k`](https://www.unum.cloud/docs/numkong/cpp/quickstart.md#unexpected_dimensions_k) for a strided run or one ending mid-value, and `NUMKONG_SIZE_MAX` indices when every value is NaN, which no minimum or maximum can index.

- `data` (in): Input array
- `count` (in): Counts dimensions, a multiple of the values per byte.
- `stride` (in): Stride between elements in bytes (use sizeof(in_type_) for contiguous)
- `min_value` (out): Output minimum value
- `min_index` (out): Output index of minimum value, `NUMKONG_SIZE_MAX` if every value is NaN
- `max_value` (out): Output maximum value
- `max_index` (out): Output index of maximum value, `NUMKONG_SIZE_MAX` if every value is NaN
- `capabilities` (in): Capabilities to pick from, or zero for the C++ template
- `stream` (in): Null on the CPU, or the stream of the device `capabilities` describes

## moments

Compute Σxᵢ and Σxᵢ² in a single pass.

```cpp
template <numeric_dtype value_type_, std::size_t max_rank_ = 8>
moments_result<typename value_type_::reduce_moments_sum_t, typename value_type_::reduce_moments_sumsq_t> moments(
    tensor_view<value_type_, max_rank_> input) noexcept;
```

```cpp
template <numeric_dtype value_type_>
moments_result<typename value_type_::reduce_moments_sum_t, typename value_type_::reduce_moments_sumsq_t> moments(
    vector_view<value_type_> input) noexcept;
```

Compute Σxᵢ and Σxᵢ² over a vector view, strided ones included.

```cpp
template <numeric_dtype value_type_, std::size_t max_rank_ = 8, typename allocator_type_ = aligned_allocator<typename value_type_::reduce_moments_sum_t>>
expected<moments_result<tensor<typename value_type_::reduce_moments_sum_t, allocator_type_, max_rank_>, tensor<typename value_type_::reduce_moments_sumsq_t, typename std::allocator_traits<allocator_type_>::template rebind_alloc<typename value_type_::reduce_moments_sumsq_t>, max_rank_>>> moments(
    tensor_view<value_type_, max_rank_> input,
    std::size_t axis,
    keep_dims_t keep_dims = collapse_dims_k,
    allocator_type_ alloc = {}) noexcept;
```

Moments along an axis, Σxᵢ and Σxᵢ² per slice; fails like the axis [`sum`](#sum).

Returns zeroed result for empty tensors.

## minmax

Find min and max values with their flat indices.

```cpp
template <numeric_dtype value_type_, std::size_t max_rank_ = 8>
minmax_result<typename value_type_::reduce_minmax_value_t> minmax(
    tensor_view<value_type_, max_rank_> input) noexcept;
```

```cpp
template <numeric_dtype value_type_>
minmax_result<typename value_type_::reduce_minmax_value_t> minmax(
    vector_view<value_type_> input) noexcept;
```

Find min and max values with their indices over a vector view, strided ones included.

```cpp
template <numeric_dtype value_type_, std::size_t max_rank_ = 8, typename allocator_type_ = aligned_allocator<typename value_type_::reduce_minmax_value_t>>
expected<minmax_result<tensor<typename value_type_::reduce_minmax_value_t, allocator_type_, max_rank_>>> minmax(
    tensor_view<value_type_, max_rank_> input,
    std::size_t axis,
    keep_dims_t keep_dims = collapse_dims_k,
    allocator_type_ alloc = {}) noexcept;
```

Min and max along an axis; fails like the axis [`sum`](#sum).

## sum

Σ of all elements.

```cpp
template <numeric_dtype value_type_, std::size_t max_rank_ = 8>
value_type_::reduce_moments_sum_t sum(
    tensor_view<value_type_, max_rank_> input) noexcept;
```

```cpp
template <numeric_dtype value_type_>
value_type_::reduce_moments_sum_t sum(
    vector_view<value_type_> input) noexcept;
```

Σ of all elements in a vector view.

```cpp
template <numeric_dtype value_type_, std::size_t max_rank_ = 8, typename allocator_type_ = aligned_allocator<typename value_type_::reduce_moments_sum_t>>
expected<tensor<typename value_type_::reduce_moments_sum_t, allocator_type_, max_rank_>> sum(
    tensor_view<value_type_, max_rank_> input,
    std::size_t axis,
    keep_dims_t keep_dims = collapse_dims_k,
    allocator_type_ alloc = {}) noexcept;
```

Σ along a single axis; empty for an empty `input`, [`unexpected_dimensions_k`](https://www.unum.cloud/docs/numkong/cpp/quickstart.md#unexpected_dimensions_k) for an `axis` past the rank, [`misaligned_k`](https://www.unum.cloud/docs/numkong/cpp/quickstart.md#misaligned_k) for an unsupported layout, or the allocation's failure.

## min

Find the minimum element value.

```cpp
template <numeric_dtype value_type_, std::size_t max_rank_ = 8>
value_type_::reduce_minmax_value_t min(
    tensor_view<value_type_, max_rank_> input) noexcept;
```

```cpp
template <numeric_dtype value_type_>
value_type_::reduce_minmax_value_t min(
    vector_view<value_type_> input) noexcept;
```

Find the minimum element value in a vector view.

```cpp
template <numeric_dtype value_type_, std::size_t max_rank_ = 8, typename allocator_type_ = aligned_allocator<typename value_type_::reduce_minmax_value_t>>
expected<tensor<typename value_type_::reduce_minmax_value_t, allocator_type_, max_rank_>> min(
    tensor_view<value_type_, max_rank_> input,
    std::size_t axis,
    keep_dims_t keep_dims = collapse_dims_k,
    allocator_type_ alloc = {}) noexcept;
```

Min along an axis; fails like the axis [`minmax`](#minmax).

## max

Find the maximum element value.

```cpp
template <numeric_dtype value_type_, std::size_t max_rank_ = 8>
value_type_::reduce_minmax_value_t max(
    tensor_view<value_type_, max_rank_> input) noexcept;
```

```cpp
template <numeric_dtype value_type_>
value_type_::reduce_minmax_value_t max(
    vector_view<value_type_> input) noexcept;
```

Find the maximum element value in a vector view.

```cpp
template <numeric_dtype value_type_, std::size_t max_rank_ = 8, typename allocator_type_ = aligned_allocator<typename value_type_::reduce_minmax_value_t>>
expected<tensor<typename value_type_::reduce_minmax_value_t, allocator_type_, max_rank_>> max(
    tensor_view<value_type_, max_rank_> input,
    std::size_t axis,
    keep_dims_t keep_dims = collapse_dims_k,
    allocator_type_ alloc = {}) noexcept;
```

Max along an axis; fails like the axis [`minmax`](#minmax).

## argmin

Index of the minimum element, flat.

```cpp
template <numeric_dtype value_type_, std::size_t max_rank_ = 8>
std::size_t argmin(tensor_view<value_type_, max_rank_> input) noexcept;
```

```cpp
template <numeric_dtype value_type_>
std::size_t argmin(vector_view<value_type_> input) noexcept;
```

Index of the minimum element in a vector view.

```cpp
template <numeric_dtype value_type_, std::size_t max_rank_ = 8, typename allocator_type_ = aligned_allocator<std::size_t>>
expected<tensor<std::size_t, allocator_type_, max_rank_>> argmin(
    tensor_view<value_type_, max_rank_> input,
    std::size_t axis,
    keep_dims_t keep_dims = collapse_dims_k,
    allocator_type_ alloc = {}) noexcept;
```

Argmin along an axis; fails like the axis [`sum`](#sum), and with [`missing_kernel_k`](https://www.unum.cloud/docs/numkong/cpp/quickstart.md#missing_kernel_k) for sub-byte inputs.

## argmax

Index of the maximum element, flat.

```cpp
template <numeric_dtype value_type_, std::size_t max_rank_ = 8>
std::size_t argmax(tensor_view<value_type_, max_rank_> input) noexcept;
```

```cpp
template <numeric_dtype value_type_>
std::size_t argmax(vector_view<value_type_> input) noexcept;
```

Index of the maximum element in a vector view.

```cpp
template <numeric_dtype value_type_, std::size_t max_rank_ = 8, typename allocator_type_ = aligned_allocator<std::size_t>>
expected<tensor<std::size_t, allocator_type_, max_rank_>> argmax(
    tensor_view<value_type_, max_rank_> input,
    std::size_t axis,
    keep_dims_t keep_dims = collapse_dims_k,
    allocator_type_ alloc = {}) noexcept;
```

Argmax along an axis; fails like [`argmin`](#argmin).

## popcount

Population count of a packed bit tensor.

```cpp
template <std::size_t max_rank_ = 8>
u64_t popcount(tensor_view<u1x8_t, max_rank_> input) noexcept;
```

```cpp
u64_t popcount(vector_view<u1x8_t> input) noexcept;
```

Population count over a 1D bit-vector view.

Wraps [`nk_reduce_moments_u1_best`](https://www.unum.cloud/docs/numkong/c/elementwise-and-reductions.md#nk_reduce_moments_u1_best) via [`sum`](#sum); returns 0 for empty inputs.

## any_set

True if any bit of the packed bit tensor is set.

```cpp
template <std::size_t max_rank_ = 8>
bool any_set(tensor_view<u1x8_t, max_rank_> input) noexcept;
```

```cpp
bool any_set(vector_view<u1x8_t> input) noexcept;
```

True if any bit of the 1D bit-vector view is set.

Suffixed `_set` to avoid colliding with the `[all_t](https://www.unum.cloud/docs/numkong/cpp/tensors-and-views.md#all_t)` and [`all`](https://www.unum.cloud/docs/numkong/cpp/tensors-and-views.md#all) slice marker in `[vector.hpp](https://www.unum.cloud/docs/numkong/cpp/tensors-and-views.md#vector)`.

## none_set

True if no bit of the packed bit tensor is set.

```cpp
template <std::size_t max_rank_ = 8>
bool none_set(tensor_view<u1x8_t, max_rank_> input) noexcept;
```

```cpp
bool none_set(vector_view<u1x8_t> input) noexcept;
```

True if no bit of the 1D bit-vector view is set.

## all_set

True if every bit of the packed bit tensor is set.

```cpp
template <std::size_t max_rank_ = 8>
bool all_set(tensor_view<u1x8_t, max_rank_> input) noexcept;
```

```cpp
bool all_set(vector_view<u1x8_t> input) noexcept;
```

True if every bit of the 1D bit-vector view is set.

## sin

Array sine: outᵢ = sin(inᵢ).

```cpp
template <numeric_dtype in_type_, numeric_dtype precision_type_ = in_type_>
status_t sin(
    in_type_ const *in,
    std::size_t n,
    in_type_ *out,
    nk_capability_t capabilities = default_capabilities(),
    nk_stream_t stream = nullptr) noexcept;
```

```cpp
template <numeric_dtype in_type_, vector_of<in_type_> input_type_, mutable_vector_of<in_type_> output_type_>
status_t sin(
    input_type_ const & input,
    output_type_ && output,
    nk_capability_t capabilities = default_capabilities(),
    nk_stream_t stream = nullptr) noexcept;
```

Elementwise sin of one run into another of equal dimensions; [`unexpected_dimensions_k`](https://www.unum.cloud/docs/numkong/cpp/quickstart.md#unexpected_dimensions_k) when they differ or either run is strided or ends mid-value.

```cpp
template <numeric_dtype value_type_, std::size_t max_rank_ = 8>
status_t sin(
    tensor_view<value_type_, max_rank_> input,
    tensor_span<value_type_, max_rank_> output) noexcept;
```

Elementwise sin into pre-allocated output.

```cpp
template <numeric_dtype value_type_, std::size_t max_rank_ = 8, typename allocator_type_ = aligned_allocator<value_type_>>
expected<tensor<value_type_, allocator_type_, max_rank_>> sin(
    tensor_view<value_type_, max_rank_> input,
    allocator_type_ alloc = {}) noexcept;
```

Allocating sin; empty for an empty `input`, or the allocation's or the kernel's failure.

- `in` (in): Input array
- `n` (in): Number of elements
- `out` (out): Output array
- `capabilities` (in): Capabilities to pick from, or zero for the C++ template
- `stream` (in): Null on the CPU, or the stream of the device `capabilities` describes

## cos

Array cosine: outᵢ = cos(inᵢ).

```cpp
template <numeric_dtype in_type_, numeric_dtype precision_type_ = in_type_>
status_t cos(
    in_type_ const *in,
    std::size_t n,
    in_type_ *out,
    nk_capability_t capabilities = default_capabilities(),
    nk_stream_t stream = nullptr) noexcept;
```

```cpp
template <numeric_dtype in_type_, vector_of<in_type_> input_type_, mutable_vector_of<in_type_> output_type_>
status_t cos(
    input_type_ const & input,
    output_type_ && output,
    nk_capability_t capabilities = default_capabilities(),
    nk_stream_t stream = nullptr) noexcept;
```

Elementwise cos of one run into another, failing like the concept [`sin`](#sin).

```cpp
template <numeric_dtype value_type_, std::size_t max_rank_ = 8>
status_t cos(
    tensor_view<value_type_, max_rank_> input,
    tensor_span<value_type_, max_rank_> output) noexcept;
```

Elementwise cos into pre-allocated output.

```cpp
template <numeric_dtype value_type_, std::size_t max_rank_ = 8, typename allocator_type_ = aligned_allocator<value_type_>>
expected<tensor<value_type_, allocator_type_, max_rank_>> cos(
    tensor_view<value_type_, max_rank_> input,
    allocator_type_ alloc = {}) noexcept;
```

Allocating cos; empty for an empty `input`, or the allocation's or the kernel's failure.

- `in` (in): Input array
- `n` (in): Number of elements
- `out` (out): Output array
- `capabilities` (in): Capabilities to pick from, or zero for the C++ template
- `stream` (in): Null on the CPU, or the stream of the device `capabilities` describes

## atan

Array arctangent: outᵢ = arctan(inᵢ).

```cpp
template <numeric_dtype in_type_, numeric_dtype precision_type_ = in_type_>
status_t atan(
    in_type_ const *in,
    std::size_t n,
    in_type_ *out,
    nk_capability_t capabilities = default_capabilities(),
    nk_stream_t stream = nullptr) noexcept;
```

```cpp
template <numeric_dtype in_type_, vector_of<in_type_> input_type_, mutable_vector_of<in_type_> output_type_>
status_t atan(
    input_type_ const & input,
    output_type_ && output,
    nk_capability_t capabilities = default_capabilities(),
    nk_stream_t stream = nullptr) noexcept;
```

Elementwise atan of one run into another, failing like the concept [`sin`](#sin).

```cpp
template <numeric_dtype value_type_, std::size_t max_rank_ = 8>
status_t atan(
    tensor_view<value_type_, max_rank_> input,
    tensor_span<value_type_, max_rank_> output) noexcept;
```

Elementwise atan into pre-allocated output.

```cpp
template <numeric_dtype value_type_, std::size_t max_rank_ = 8, typename allocator_type_ = aligned_allocator<value_type_>>
expected<tensor<value_type_, allocator_type_, max_rank_>> atan(
    tensor_view<value_type_, max_rank_> input,
    allocator_type_ alloc = {}) noexcept;
```

Allocating atan; empty for an empty `input`, or the allocation's or the kernel's failure.

- `in` (in): Input array
- `n` (in): Number of elements
- `out` (out): Output array
- `capabilities` (in): Capabilities to pick from, or zero for the C++ template
- `stream` (in): Null on the CPU, or the stream of the device `capabilities` describes

## fma

FMA helper template for baseline dot-product implementations.

```cpp
template <typename in_type_, typename accumulator_type_>
requires (dimensions_per_value<in_type_>() == 1)
accumulator_type_ fma(
    in_type_ a,
    in_type_ b,
    accumulator_type_ acc) noexcept;
```

```cpp
template <typename accumulator_type_>
accumulator_type_ fma(
    i4x2_t a,
    i4x2_t b,
    accumulator_type_ acc) noexcept;
```

FMA specialization for [i4x2_t](https://www.unum.cloud/docs/numkong/cpp/scalar-types.md#i4x2_t), signed 4-bit packed pairs.

```cpp
template <typename accumulator_type_>
accumulator_type_ fma(
    u4x2_t a,
    u4x2_t b,
    accumulator_type_ acc) noexcept;
```

FMA specialization for [u4x2_t](https://www.unum.cloud/docs/numkong/cpp/scalar-types.md#u4x2_t), unsigned 4-bit packed pairs.

```cpp
template <typename accumulator_type_>
accumulator_type_ fma(
    e2m1x2_t a,
    e2m1x2_t b,
    accumulator_type_ acc) noexcept;
```

FMA specialization for [e2m1x2_t](https://www.unum.cloud/docs/numkong/cpp/scalar-types.md#e2m1x2_t), FP4 packed pairs; both nibble products are exact in f32.

```cpp
template <typename accumulator_type_>
accumulator_type_ fma(
    u1x8_t a,
    u1x8_t b,
    accumulator_type_ acc) noexcept;
```

FMA specialization for [u1x8_t](https://www.unum.cloud/docs/numkong/cpp/scalar-types.md#u1x8_t), 8 packed bits.

Counts matching set bits, popcount of AND.

