# Quickstart

> NumKong for C++

```c
#include <numkong/numkong.h>
#include <stdio.h>

int main(void) {
    nk_f32_t a[] = {1, 2, 3};
    nk_f32_t b[] = {4, 5, 6};
    nk_f64_t dot = 0;
    nk_capability_t capabilities = nk_cap_serial_k;
    nk_cpu_capabilities_enabled(&capabilities); // the capabilities this CPU runs and this build holds
    nk_cpu_configure_thread(capabilities);
    nk_status_t status = nk_dot_f32_best(a, b, 3, &dot, capabilities, NULL); // widened f32 → f64 output
    printf("dot=%f, %s\n", dot, nk_status_name(status));
    return status != nk_success_k;
}
```

Every operation has one such dispatch point, named with `_best`, which runs the best capability in the mask it gets; the root README describes [the dispatch model](https://github.com/ashvardanian/NumKong/blob/main/README.md#dispatch-points--capability-masks) once.

### The C ABI

The C ABI keeps the operation family, input dtype, and output policy visible in the symbol name.
That makes widening obvious at the call site.

```c
#include <numkong/numkong.h>

nk_i8_t a[1536];
nk_i8_t b[1536];
nk_i32_t dot = 0;     // widened from int8 storage
nk_f32_t l2 = 0;      // widened from int8 storage

nk_dot_i8_best(a, b, 1536, &dot, capabilities, NULL);
nk_euclidean_i8_best(a, b, 1536, &l2, capabilities, NULL);
```

The examples below pass the `capabilities` of the Quickstart and a null stream, as every CPU call does.
Each capability kernel, like `nk_dot_i8_haswell`, is also callable directly with the same arguments short of the mask.
To resolve a kernel once and call it many times without naming a capability, library builds provide the punned finder:

```c
nk_metric_dense_punned_t angular = 0;
nk_capability_t capability = 0;
nk_find_kernel_punned(nk_kernel_angular_k, nk_f32_k, capabilities, (nk_kernel_punned_t *)&angular, &capability);

nk_f32_t a[768], b[768];
nk_f64_t result = 0; // widened f32 → f64 output
angular(a, b, 768, &result, NULL);
```

That is the lowest-level dynamic path.
The typed C++ wrappers usually read better unless you are building your own dispatch layer.

### The C++ Layer

The C++ wrappers come in three layers.
Raw-pointer overloads mirror the C ABI one to one, with explicit lengths, strides and task windows for sharded launches.
Overloads over the `nk::vector_of` and matrix concepts accept any contiguous run, like `std::vector`, `std::array`, `std::span`, a C array or an `nk::vector_view`, and return their results.
Owning types, like `nk::tensor`, `nk::packed_matrix` and `nk::packed_attention`, allocate through your allocator for repeated workloads.

```cpp
#include <vector>
#include <numkong/numkong.hpp>

namespace nk = ashvardanian::numkong;

int main() {
    std::vector<nk::f32_t> a {1, 2, 3}, b {4, 5, 6};
    auto [dot, status] = nk::dot<nk::f32_t>(a, b); // nk::f32_t::dot_result_t == nk::f64_t
    if (nk::failed(status)) return 1;

    nk::f64_t raw {};
    status = nk::dot(a.data(), b.data(), a.size(), &raw, nk_cap_serial_k); // the raw layer, pinned to serial
    if (nk::failed(status)) return 1;

    auto [matrix, matrix_status] = nk::matrix<nk::f32_t>::full({2, 3}, nk::f32_t(1));
    if (nk::failed(matrix_status)) return 1;
    auto [packed, packed_status] = nk::packed_matrix<nk::f32_t>::make(matrix.view()); // owning, reused across calls
    return nk::succeeded(packed_status) && dot == raw ? 0 : 1;
}
```

Standard containers count storage values, so an `std::vector<nk::u1x8_t>` of 4 bytes holds 32 dimensions, while NumKong's own vectors and views count dimensions.
Runs must fill whole storage values, so odd bit and nibble lengths pad with zero bits up to the next byte, and a view ending mid-value is refused.
Strided views are refused with `nk::status_t::unexpected_dimensions_k`, and `vector_view::values()` turns a contiguous view into an `std::span` or refuses the same way.
Outputs of elementwise kernels, like `nk::scale<nk::f32_t>(a, 2.0f, 1.0f, out)`, are any mutable run of the same size, and the call returns an `nk::status_t`.
Multi-part results come back as small structs: `nk::reduce_moments` returns `{sum, sumsq}`, `nk::reduce_minmax` a `minmax_result`, and `nk::shapes_packed::make` an owner of many packs.

Every wrapper ends in the two arguments of its dispatch point, defaulted to `nk::default_capabilities()` and a null stream, and returns an `nk::status_t`.
That scoped enum mirrors `nk_status_t` value for value, converts to it with `static_cast`, and is tested with `nk::succeeded` and `nk::failed` rather than as a `bool`.
Factories, like `nk::tensor<T>::zeros`, and the allocating overloads return an `nk::expected<T>` holding the `value` and its `status`, which converts to `true` on success and unpacks with structured bindings.
A failed factory leaves its `value` empty, while a zero-volume request succeeds with an empty one.
`nk::default_capabilities()` is the `nk_cpu_capabilities_enabled` mask, and a zero mask runs the C++ reference template instead of any capability's kernel.
`nk::device_t` names one device by its kind and its runtime's ordinal, and asks the matching `nk_<kind>_*` functions for its masks:

```cpp
nk::device_t const cpu = nk::device_t::cpu();
if (auto enabled = cpu.capabilities_enabled()) (void)cpu.configure_thread(enabled.value);
auto const [devices, counted] = nk::device_t::count(nk::device_kind_t::cuda_k);
for (std::size_t ordinal = 0; ordinal != devices; ++ordinal)
    if (auto gpu = nk::device_t::make(nk::device_kind_t::cuda_k, ordinal))
        if (auto mask = gpu.value.capabilities_enabled()) { /* dispatch over `mask.value` on that GPU */ }
```

In header-only builds the dispatch points report `nk_missing_library_k`, so wrappers there take a zero mask or call a capability's kernel.

The API is intentionally not STL-shaped.
`vector_view`, `tensor_view`, and `matrix_view` prioritize signed strides, sub-byte storage, and kernel compatibility over resizable-container ergonomics.

`#include <numkong/types.hpp>`

## status_t

Why a call failed, in the vocabulary the C ABI and every binding share.

```cpp
enum status_t
```

Every enumerator takes its value from `[nk_status_t](https://www.unum.cloud/docs/numkong/c/quickstart.md#nk_status_t)` in `types.h`, so the C++ mirror and the ABI cannot drift, and the crossing is a cast rather than a table.

Statuses fall into three categories, with no interruptions or defects to report. Resources: [`bad_alloc_k`](#bad_alloc_k), [`missing_gpu_k`](#missing_gpu_k), [`device_code_mismatch_k`](#device_code_mismatch_k), [`missing_kernel_k`](#missing_kernel_k) and [`missing_library_k`](#missing_library_k). Input: [`unexpected_dimensions_k`](#unexpected_dimensions_k), [`device_memory_mismatch_k`](#device_memory_mismatch_k) and [`misaligned_k`](#misaligned_k). State: [`pack_mismatch_k`](#pack_mismatch_k). Any verb that allocates, binds a device or touches a file may also return a Resources status. A verb's own block lists only what its contract adds.

- `success_k` = `nk_success_k`: Scheduled, or finished, without error.
- `bad_alloc_k` = `nk_bad_alloc_k`: An allocation failed, or the queue could not record another block.
- `unexpected_dimensions_k` = `nk_unexpected_dimensions_k`: Operand shapes contradict each other, or exceed a rank or a capacity.
- `missing_gpu_k` = `nk_missing_gpu_k`: No GPU of the vendor this build targets answers.
- `device_code_mismatch_k` = `nk_device_code_mismatch_k`: The device code lacks the kernel, or it failed to build, launch or finish.
- `device_memory_mismatch_k` = `nk_device_memory_mismatch_k`: An operand lies in memory the device cannot address.
- `missing_kernel_k` = `nk_missing_kernel_k`: No capability in the capability mask has this kernel.
- `misaligned_k` = `nk_misaligned_k`: An operand or stride breaks the alignment contract, like a strided sub-byte view.
- `pack_mismatch_k` = `nk_pack_mismatch_k`: The buffer was packed by another capability or layout than the one reading it.
- `missing_library_k` = `nk_missing_library_k`: A dispatch point or finder called from a header-only build, which links no library.

## expected

A result paired with the `[status_t](#status_t)` explaining it; the value is only meaningful on [`success_k`](#success_k).

```cpp
template <typename value_type_>
struct expected
```

- `value` `value_type_`
- `status` `status_t`

## succeeded

Whether `status` reports success.

```cpp
bool succeeded(status_t status) noexcept;
```

## failed

Whether `status` reports failure.

```cpp
bool failed(status_t status) noexcept;
```

## status_name

Static, English description of `status`.

```cpp
char const * status_name(status_t status) noexcept;
```

Never returns `nullptr`, never allocates.

