//! C ABI for the xy native core (design dossier §32: the native Rust core
//! runs inside the Python process, operating zero-copy over NumPy/Arrow buffers).
//!
//! The C ABI is independent of the CPython version — one cdylib per platform
//! covers all Pythons (§33's wheel-matrix goal, minus the ABI cross-product).
//! Compute kernels remain self-contained; static export uses the focused Rust
//! `png` crate so rasterization and compression stay fused.
//!
//! Phase 0 exposes stateless kernels over caller-owned buffers — the canonical
//! column store stays on the Python side as NumPy arrays (CPU is the truth, GPU
//! and every derived buffer is a cache, §27). A Rust-owned column store arrives
//! with Tier 2/3.
//!
//! Safety contract (enforced by the single ctypes wrapper in
//! `python/xy/_native.py`, the only in-tree caller): non-empty inputs
//! use non-null, properly aligned pointers sized as documented per function.
//! Empty inputs are accepted without dereferencing their pointers; invalid
//! pointer/argument combinations return the documented error sentinel instead of
//! panicking across the C boundary.

pub mod css;
#[allow(dead_code)] // generated by scripts/gen_font.py; some metrics unused by the rasterizer
mod font;
pub mod kernels;
pub mod raster;
mod simd;
pub mod svg;
pub mod tiles;
mod transition;

use kernels::ZoneMap;

fn finite_gt(lo: f64, hi: f64) -> bool {
    lo.is_finite() && hi.is_finite() && hi > lo
}

fn finite_ordered(lo: f64, hi: f64) -> bool {
    lo.is_finite() && hi.is_finite() && hi >= lo
}

/// Panic backstop for the C ABI: a Rust panic must never unwind across
/// `extern "C"` into the host interpreter — that is undefined behavior and in
/// practice aborts the embedding CPython process. Any panic (an internal
/// assert, a worker-join failure, an OOM unwind) maps to the calling entry
/// point's error sentinel instead; output buffers may then be partially
/// written, exactly like the existing invalid-argument paths, and callers
/// already treat the sentinel as "output undefined". `AssertUnwindSafe` is
/// sound because nothing observes the closure's captures after a panic. This
/// backstop only operates when the target supports panic unwinding; on
/// panic-abort targets such as wasm32, an internal panic aborts the instance.
fn ffi_guard<R>(sentinel: R, body: impl FnOnce() -> R) -> R {
    std::panic::catch_unwind(std::panic::AssertUnwindSafe(body)).unwrap_or(sentinel)
}

/// Convert parallel pointer/length arrays into call-scoped immutable slices.
/// Empty spans may carry null pointers; no slice outlives the FFI call.
unsafe fn borrowed_byte_spans<'a>(
    pointers: *const *const u8,
    lengths: *const usize,
    count: usize,
) -> Option<Vec<&'a [u8]>> {
    if count == 0 {
        return Some(Vec::new());
    }
    if pointers.is_null() || lengths.is_null() {
        return None;
    }
    let pointers = std::slice::from_raw_parts(pointers, count);
    let lengths = std::slice::from_raw_parts(lengths, count);
    let mut spans = Vec::with_capacity(count);
    for (&pointer, &length) in pointers.iter().zip(lengths) {
        if length == 0 {
            spans.push(&[][..]);
        } else if pointer.is_null() {
            return None;
        } else {
            spans.push(std::slice::from_raw_parts(pointer, length));
        }
    }
    Some(spans)
}

/// ABI version — bumped on any signature change. The Python wrapper checks this
/// at load time and refuses a mismatched library loudly (§33 comm-versioning
/// rule, applied to the in-process boundary).
pub const ABI_VERSION: u32 = 47;
const FACTORIZE_CAPACITY_EXCEEDED: usize = usize::MAX - 1;

#[no_mangle]
pub extern "C" fn xy_abi_version() -> u32 {
    ABI_VERSION
}

/// Encode homogeneous fixed-width NumPy records as stable animation identity
/// keys. `kind` is 0 for UTF-32 Unicode, 1 for fixed bytes, 2 for bool, 3 for
/// signed integers, 4 for unsigned integers, and 5 for f64. Integer widths are
/// 1/2/4/8 bytes; Unicode width is a positive multiple of four. `swap_endian`
/// must be zero or one.
///
/// Returns 0 on success, 1 for scalar data this kernel declines to tokenize
/// (the caller falls back to its reference encoder), 2 for a duplicate token,
/// 3 for a digest collision, and 4 for invalid arguments. Statuses 1, 2, and 3
/// write `out_error_first`/`out_error_index`: the offending row for 1, and the
/// prior/current pair for 2 and 3. Status 4 writes neither, and is a caller
/// bug rather than a data property — keeping it distinct from 1 stops a
/// layout-contract drift from degrading silently into the slow path.
///
/// # Safety
/// For non-empty input, `data` addresses `len * width` readable bytes and each
/// key output addresses `len` writable u32s. Error outputs address one writable
/// usize each. Input and output spans must not overlap.
#[no_mangle]
pub unsafe extern "C" fn xy_transition_keys_fixed(
    data: *const u8,
    len: usize,
    width: usize,
    kind: u32,
    swap_endian: i32,
    out_lo: *mut u32,
    out_hi: *mut u32,
    out_error_first: *mut usize,
    out_error_index: *mut usize,
) -> i32 {
    if !matches!(swap_endian, 0 | 1) {
        return 4;
    }
    if len == 0 {
        return 0;
    }
    if out_error_first.is_null() || out_error_index.is_null() {
        return 4;
    }
    let byte_len = match len.checked_mul(width) {
        Some(value) if width > 0 => value,
        _ => return 4,
    };
    if data.is_null() || out_lo.is_null() || out_hi.is_null() {
        return 4;
    }
    let data = std::slice::from_raw_parts(data, byte_len);
    let low = std::slice::from_raw_parts_mut(out_lo, len);
    let high = std::slice::from_raw_parts_mut(out_hi, len);
    ffi_guard(4, || {
        match transition::encode_fixed_into(data, width, kind, swap_endian != 0, low, high) {
            Ok(()) => 0,
            Err(transition::TransitionKeyError::Invalid { index }) => match index {
                Some(index) => {
                    *out_error_first = index;
                    *out_error_index = index;
                    1
                }
                None => 4,
            },
            Err(transition::TransitionKeyError::Duplicate { first, index }) => {
                *out_error_first = first;
                *out_error_index = index;
                2
            }
            Err(transition::TransitionKeyError::Collision { first, index }) => {
                *out_error_first = first;
                *out_error_index = index;
                3
            }
        }
    })
}

/// Serialize parallel f64 screen coordinates into SVG polyline path data.
/// Returns the required byte count, or `usize::MAX` for invalid inputs. When
/// `out_cap` is too small no bytes are written, allowing callers to retry.
///
/// # Safety
/// `x` and `y` must point to `len` readable f64s. When `out_cap` is sufficient,
/// `out` must point to `out_cap` writable bytes.
#[no_mangle]
pub unsafe extern "C" fn xy_svg_poly_path(
    x: *const f64,
    y: *const f64,
    len: usize,
    out: *mut u8,
    out_cap: usize,
) -> usize {
    if len == 0 || x.is_null() || y.is_null() {
        return usize::MAX;
    }
    let x = std::slice::from_raw_parts(x, len);
    let y = std::slice::from_raw_parts(y, len);
    let Some(path) = ffi_guard(None, || svg::poly_path(x, y)) else {
        return usize::MAX;
    };
    let required = path.len();
    if out_cap < required {
        return required;
    }
    if out.is_null() {
        return usize::MAX;
    }
    std::slice::from_raw_parts_mut(out, out_cap)[..required].copy_from_slice(path.as_bytes());
    required
}

/// Factor `len` fixed-width records into first-seen u32 codes. Returns the
/// number of unique records or `usize::MAX` on invalid pointers/dimensions.
///
/// # Safety
/// `data` must address `len * width` readable bytes. `out_codes` and
/// `out_unique_indices` must each address `len` writable u32 values.
#[no_mangle]
pub unsafe extern "C" fn xy_factorize_fixed(
    data: *const u8,
    len: usize,
    width: usize,
    out_codes: *mut u32,
    out_unique_indices: *mut u32,
) -> usize {
    if len == 0 {
        return 0;
    }
    let byte_len = match len.checked_mul(width) {
        Some(value) if width > 0 => value,
        _ => return usize::MAX,
    };
    if data.is_null() || out_codes.is_null() || out_unique_indices.is_null() {
        return usize::MAX;
    }
    let data = std::slice::from_raw_parts(data, byte_len);
    let codes = std::slice::from_raw_parts_mut(out_codes, len);
    let unique_indices = std::slice::from_raw_parts_mut(out_unique_indices, len);
    ffi_guard(usize::MAX, || {
        kernels::factorize_fixed_into(data, width, codes, unique_indices).unwrap_or(usize::MAX)
    })
}

/// Palette-sized fixed-record factorization with one u8 code per row. Returns
/// `usize::MAX - 1` when `unique_capacity` is exceeded so the caller can retry
/// the general u32 path, and `usize::MAX` for invalid arguments/panics.
///
/// # Safety
/// `data`/`out_codes` address `len * width` readable bytes / `len` writable
/// bytes. `out_unique_indices` addresses `unique_capacity` writable u32s.
#[no_mangle]
pub unsafe extern "C" fn xy_factorize_fixed_u8(
    data: *const u8,
    len: usize,
    width: usize,
    out_codes: *mut u8,
    out_unique_indices: *mut u32,
    unique_capacity: usize,
) -> usize {
    if len == 0 {
        return 0;
    }
    let byte_len = match len.checked_mul(width) {
        Some(value) if width > 0 => value,
        _ => return usize::MAX,
    };
    if unique_capacity == 0
        || unique_capacity > 256
        || data.is_null()
        || out_codes.is_null()
        || out_unique_indices.is_null()
    {
        return usize::MAX;
    }
    let data = std::slice::from_raw_parts(data, byte_len);
    let codes = std::slice::from_raw_parts_mut(out_codes, len);
    let unique_indices = std::slice::from_raw_parts_mut(out_unique_indices, unique_capacity);
    ffi_guard(usize::MAX, || {
        kernels::factorize_fixed_u8_into(data, width, codes, unique_indices)
            .unwrap_or(FACTORIZE_CAPACITY_EXCEEDED)
    })
}

/// Palette-sized fixed-record factorization with exact per-code u64 counts.
/// Counts use the same first-seen order as `out_unique_indices`. Return and
/// pointer semantics match [`xy_factorize_fixed_u8`].
///
/// # Safety
/// `out_counts` addresses `unique_capacity` writable u64 values in addition
/// to the spans required by [`xy_factorize_fixed_u8`].
#[no_mangle]
pub unsafe extern "C" fn xy_factorize_fixed_u8_counts(
    data: *const u8,
    len: usize,
    width: usize,
    out_codes: *mut u8,
    out_unique_indices: *mut u32,
    out_counts: *mut u64,
    unique_capacity: usize,
) -> usize {
    if len == 0 {
        return 0;
    }
    let byte_len = match len.checked_mul(width) {
        Some(value) if width > 0 => value,
        _ => return usize::MAX,
    };
    if unique_capacity == 0
        || unique_capacity > 256
        || data.is_null()
        || out_codes.is_null()
        || out_unique_indices.is_null()
        || out_counts.is_null()
    {
        return usize::MAX;
    }
    let data = std::slice::from_raw_parts(data, byte_len);
    let codes = std::slice::from_raw_parts_mut(out_codes, len);
    let unique_indices = std::slice::from_raw_parts_mut(out_unique_indices, unique_capacity);
    let counts = std::slice::from_raw_parts_mut(out_counts, unique_capacity);
    ffi_guard(usize::MAX, || {
        kernels::factorize_fixed_u8_counts_into(data, width, codes, unique_indices, counts)
            .unwrap_or(FACTORIZE_CAPACITY_EXCEEDED)
    })
}

/// Compact factorization for one-codepoint NumPy Unicode records. The bounded
/// Unicode scalar domain uses direct lookup instead of record hashing. Return
/// semantics match [`xy_factorize_fixed_u8_counts`].
///
/// # Safety
/// `data` addresses `len` readable u32 records. Output spans follow
/// [`xy_factorize_fixed_u8_counts`].
#[no_mangle]
pub unsafe extern "C" fn xy_factorize_unicode1_u8_counts(
    data: *const u32,
    len: usize,
    swap_endian: i32,
    out_codes: *mut u8,
    out_unique_indices: *mut u32,
    out_counts: *mut u64,
    unique_capacity: usize,
) -> usize {
    if len == 0 {
        return 0;
    }
    if unique_capacity == 0
        || unique_capacity > 256
        || !matches!(swap_endian, 0 | 1)
        || data.is_null()
        || out_codes.is_null()
        || out_unique_indices.is_null()
        || out_counts.is_null()
    {
        return usize::MAX;
    }
    let data = std::slice::from_raw_parts(data, len);
    let codes = std::slice::from_raw_parts_mut(out_codes, len);
    let unique_indices = std::slice::from_raw_parts_mut(out_unique_indices, unique_capacity);
    let counts = std::slice::from_raw_parts_mut(out_counts, unique_capacity);
    ffi_guard(usize::MAX, || {
        kernels::factorize_unicode1_u8_counts_into(
            data,
            swap_endian != 0,
            codes,
            unique_indices,
            counts,
        )
        .unwrap_or(FACTORIZE_CAPACITY_EXCEEDED)
    })
}

/// Remap byte codes in place. Returns 1 on success and 0 for invalid input or
/// a code outside the mapping.
///
/// # Safety
/// `values` addresses `len` writable bytes and `mapping` addresses
/// `mapping_len` readable bytes.
#[no_mangle]
pub unsafe extern "C" fn xy_remap_u8(
    values: *mut u8,
    len: usize,
    mapping: *const u8,
    mapping_len: usize,
) -> i32 {
    if len == 0 {
        return 1;
    }
    if values.is_null() || mapping.is_null() || mapping_len == 0 {
        return 0;
    }
    let values = std::slice::from_raw_parts_mut(values, len);
    let mapping = std::slice::from_raw_parts(mapping, mapping_len);
    ffi_guard(0, || kernels::remap_u8_inplace(values, mapping) as i32)
}

/// CSS value validation (styling contract; `src/css.rs`). `kind` selects the
/// grammar: `0` = property declaration (`prop` + `value`), `1` = color
/// (`value` only), `2` = length token list, `3` = number.
///
/// Returns `1` when the value parsed statically (for `kind == 1` the RGBA
/// channels are written to `out_rgba` when it is non-null and the color is
/// statically resolvable — `currentColor` parses without static channels),
/// `2` when the value is valid but browser-resolved (`var()`, `oklch()`,
/// `calc()`, unknown-property passthrough), `0` on invalid pointers or
/// non-UTF-8 input, and a negative error code otherwise: -1 empty, -2 unsafe
/// character (`;`/`{`/`}`/`</`/control), -3 unbalanced quotes or parens,
/// -4 bad hex, -5 bad color syntax, -6 unknown color name, -7 bad number,
/// -8 bad unit, -9 unknown function, -10 bad property name.
///
/// # Safety
/// `value` must point to `value_len` readable bytes (`prop`/`prop_len`
/// likewise; `prop` may be null only when `prop_len == 0`); `out_rgba`, when
/// non-null, must point to 4 writable f32s.
#[no_mangle]
pub unsafe extern "C" fn xy_css_check(
    kind: u32,
    prop: *const u8,
    prop_len: usize,
    value: *const u8,
    value_len: usize,
    out_rgba: *mut f32,
) -> i32 {
    // Null-with-length is invalid; empty inputs never dereference (the same
    // contract as the buffer kernels — a null pointer is only legal at len 0).
    if (value.is_null() && value_len > 0) || (prop.is_null() && prop_len > 0) {
        return 0;
    }
    let bytes = |p: *const u8, n: usize| -> &[u8] {
        if n == 0 {
            &[]
        } else {
            std::slice::from_raw_parts(p, n)
        }
    };
    let (Ok(value), Ok(prop)) = (
        std::str::from_utf8(bytes(value, value_len)),
        std::str::from_utf8(bytes(prop, prop_len)),
    ) else {
        return 0;
    };
    ffi_guard(0, || {
        let checked = match kind {
            0 => css::check_declaration(prop, value),
            1 => css::parse_color(value),
            2 => {
                let toks: Vec<&str> = value.split_whitespace().collect();
                if toks.is_empty() {
                    Err(css::CssErr::Empty)
                } else {
                    toks.iter()
                        .try_fold(css::Checked::Parsed(None), |acc, tok| {
                            Ok(match (acc, css::check_length_token(tok)?) {
                                (css::Checked::Passthrough, _) | (_, css::Checked::Passthrough) => {
                                    css::Checked::Passthrough
                                }
                                _ => css::Checked::Parsed(None),
                            })
                        })
                }
            }
            3 => value
                .trim()
                .parse::<f64>()
                .ok()
                .filter(|v| v.is_finite())
                .map(|_| css::Checked::Parsed(None))
                .ok_or(css::CssErr::BadNumber),
            _ => return 0,
        };
        match checked {
            Ok(css::Checked::Parsed(rgba)) => {
                if let (Some(c), false) = (rgba, out_rgba.is_null()) {
                    std::slice::from_raw_parts_mut(out_rgba, 4).copy_from_slice(&c);
                }
                1
            }
            Ok(css::Checked::Passthrough) => 2,
            Err(e) => -(e as i32),
        }
    })
}

/// Zone maps (§22) over `data[0..len]` in chunks of `chunk_size`.
///
/// Output arrays must each hold `ceil(len / chunk_size)` elements.
/// The final two arrays contain the positive-only min/max values used by log
/// autorange; empty positive chunks retain `+∞`/`-∞` sentinels.
/// Returns the number of chunks written.
///
/// # Safety
/// `data` must point to `len` readable f64s; each out pointer to
/// `ceil(len/chunk_size)` writable elements; `chunk_size > 0`.
#[no_mangle]
pub unsafe extern "C" fn xy_zone_maps(
    data: *const f64,
    len: usize,
    chunk_size: usize,
    out_min: *mut f64,
    out_max: *mut f64,
    out_count: *mut u64,
    out_null_count: *mut u64,
    out_sum: *mut f64,
    out_sum_sq: *mut f64,
    out_positive_min: *mut f64,
    out_positive_max: *mut f64,
) -> usize {
    if chunk_size == 0 {
        return usize::MAX;
    }
    if len == 0 {
        return 0;
    }
    let n_chunks = len.div_ceil(chunk_size);
    if data.is_null()
        || out_min.is_null()
        || out_max.is_null()
        || out_count.is_null()
        || out_null_count.is_null()
        || out_sum.is_null()
        || out_sum_sq.is_null()
        || out_positive_min.is_null()
        || out_positive_max.is_null()
    {
        return usize::MAX;
    }
    let data = std::slice::from_raw_parts(data, len);
    let zms = match ffi_guard(None, || Some(kernels::zone_maps(data, chunk_size))) {
        Some(z) => z,
        None => return usize::MAX,
    };
    debug_assert_eq!(zms.len(), n_chunks);
    for (i, zm) in zms.iter().enumerate() {
        let ZoneMap {
            min,
            max,
            positive_min,
            positive_max,
            count,
            null_count,
            sum,
            sum_sq,
        } = *zm;
        *out_min.add(i) = min;
        *out_max.add(i) = max;
        *out_count.add(i) = count;
        *out_null_count.add(i) = null_count;
        *out_sum.add(i) = sum;
        *out_sum_sq.add(i) = sum_sq;
        *out_positive_min.add(i) = positive_min;
        *out_positive_max.add(i) = positive_max;
    }
    zms.len()
}

/// Paired zone maps for equal-length x/y columns. Each output buffer contains
/// `ceil(len / chunk_size)` stable `repr(C)` [`ZoneMap`] records.
///
/// # Safety
/// `x` and `y` address `len` readable f64s; `out_x` and `out_y` address the
/// required number of writable [`ZoneMap`] records.
#[no_mangle]
pub unsafe extern "C" fn xy_zone_maps_pair(
    x: *const f64,
    y: *const f64,
    len: usize,
    chunk_size: usize,
    out_x: *mut ZoneMap,
    out_y: *mut ZoneMap,
) -> usize {
    if chunk_size == 0 {
        return usize::MAX;
    }
    if len == 0 {
        return 0;
    }
    if x.is_null() || y.is_null() || out_x.is_null() || out_y.is_null() {
        return usize::MAX;
    }
    let n_chunks = len.div_ceil(chunk_size);
    let x = std::slice::from_raw_parts(x, len);
    let y = std::slice::from_raw_parts(y, len);
    let Some((x_maps, y_maps)) = ffi_guard(None, || kernels::zone_maps_pair(x, y, chunk_size))
    else {
        return usize::MAX;
    };
    debug_assert_eq!(x_maps.len(), n_chunks);
    debug_assert_eq!(y_maps.len(), n_chunks);
    std::slice::from_raw_parts_mut(out_x, n_chunks).copy_from_slice(&x_maps);
    std::slice::from_raw_parts_mut(out_y, n_chunks).copy_from_slice(&y_maps);
    n_chunks
}

/// Offset-encode (§4/§16): `out[i] = (data[i] - offset) * scale` as f32.
/// Returns 1 on success (including the empty no-op), 0 on null arguments —
/// callers must treat 0 as "output undefined".
///
/// # Safety
/// `data` must point to `len` readable f64s, `out` to `len` writable f32s.
#[no_mangle]
pub unsafe extern "C" fn xy_encode_f32(
    data: *const f64,
    len: usize,
    offset: f64,
    scale: f64,
    out: *mut f32,
) -> i32 {
    if len == 0 {
        return 1;
    }
    if data.is_null() || out.is_null() {
        return 0;
    }
    let data = std::slice::from_raw_parts(data, len);
    let out = std::slice::from_raw_parts_mut(out, len);
    ffi_guard(0, || {
        kernels::encode_f32_into(data, offset, scale, out);
        1
    })
}

/// M4 decimation (§5 Tier 1): source indices of {first, min, max, last} per
/// bucket over the visible window `[x0, x1)`. `x` must be ascending.
///
/// `out` must hold `4 * n_buckets` u32s. Returns the count written, or
/// `usize::MAX` on invalid arguments (non-finite bounds, x1 <= x0,
/// n_buckets == 0, `len > u32::MAX`, or a `4 * n_buckets` that overflows).
///
/// # Safety
/// `x`/`y` must point to `len` readable f64s; `out` to `4 * n_buckets`
/// writable u32s.
#[no_mangle]
pub unsafe extern "C" fn xy_m4_indices(
    x: *const f64,
    y: *const f64,
    len: usize,
    x0: f64,
    x1: f64,
    n_buckets: usize,
    out: *mut u32,
) -> usize {
    if n_buckets == 0 || !finite_gt(x0, x1) {
        return usize::MAX;
    }
    // Emitted indices are u32 row ids: a longer column would wrap them into
    // valid-looking wrong rows. The in-tree caller never ships one, but the
    // ABI must not depend on that.
    if len > u32::MAX as usize {
        return usize::MAX;
    }
    // Same defensive posture as xy_rasterize: a caller-supplied size product
    // must not wrap in release builds into a too-short slice.
    let out_len = match n_buckets.checked_mul(4) {
        Some(n) => n,
        None => return usize::MAX,
    };
    if len == 0 {
        return 0;
    }
    if x.is_null() || y.is_null() {
        return usize::MAX;
    }
    let x = std::slice::from_raw_parts(x, len);
    let y = std::slice::from_raw_parts(y, len);
    let idx = match ffi_guard(None, || Some(kernels::m4_indices(x, y, x0, x1, n_buckets))) {
        Some(idx) => idx,
        None => return usize::MAX,
    };
    if idx.is_empty() {
        return 0;
    }
    if out.is_null() {
        return usize::MAX;
    }
    let out = std::slice::from_raw_parts_mut(out, out_len);
    out[..idx.len()].copy_from_slice(&idx);
    idx.len()
}

/// Fused M4 decimation for a parallel x/y pair. Unlike `xy_m4_indices`, this
/// writes the selected values directly and avoids returning an index array for
/// Python to gather through NumPy. SVG and PNG payload construction both use
/// this entry point, so their common reduction path stays native.
///
/// Returns the number of values written, or `usize::MAX` on invalid input.
/// `out_x` and `out_y` must each hold `4 * n_buckets` f64 values.
///
/// # Safety
/// `x` and `y` must point to `len` readable f64s; both output pointers must
/// address the documented writable capacity.
#[no_mangle]
pub unsafe extern "C" fn xy_m4_points(
    x: *const f64,
    y: *const f64,
    len: usize,
    x0: f64,
    x1: f64,
    n_buckets: usize,
    out_x: *mut f64,
    out_y: *mut f64,
) -> usize {
    if n_buckets == 0 || !finite_gt(x0, x1) || len > u32::MAX as usize {
        return usize::MAX;
    }
    let out_len = match n_buckets.checked_mul(4) {
        Some(n) => n,
        None => return usize::MAX,
    };
    if len == 0 {
        return 0;
    }
    if x.is_null() || y.is_null() || out_x.is_null() || out_y.is_null() {
        return usize::MAX;
    }
    let x = std::slice::from_raw_parts(x, len);
    let y = std::slice::from_raw_parts(y, len);
    let Some(idx) = ffi_guard(None, || Some(kernels::m4_indices(x, y, x0, x1, n_buckets))) else {
        return usize::MAX;
    };
    let out_x = std::slice::from_raw_parts_mut(out_x, out_len);
    let out_y = std::slice::from_raw_parts_mut(out_y, out_len);
    for (dst, &source) in idx.iter().enumerate() {
        let source = source as usize;
        out_x[dst] = x[source];
        out_y[dst] = y[source];
    }
    idx.len()
}

/// Native stacked-series layout. `values`, `out_lower`, and `out_upper` are
/// row-major `rows * cols` f64 buffers. `baseline` uses the generic engine
/// layout ids documented by `kernels::stacked_bounds_into`.
///
/// # Safety
/// Every pointer must address `rows * cols` readable/writable f64 values.
#[no_mangle]
pub unsafe extern "C" fn xy_stacked_bounds(
    values: *const f64,
    rows: usize,
    cols: usize,
    baseline: u32,
    out_lower: *mut f64,
    out_upper: *mut f64,
) -> i32 {
    let Some(len) = rows.checked_mul(cols) else {
        return 0;
    };
    if len == 0 || values.is_null() || out_lower.is_null() || out_upper.is_null() {
        return 0;
    }
    let values = std::slice::from_raw_parts(values, len);
    let lower = std::slice::from_raw_parts_mut(out_lower, len);
    let upper = std::slice::from_raw_parts_mut(out_upper, len);
    ffi_guard(0, || {
        i32::from(kernels::stacked_bounds_into(
            values, rows, cols, baseline, lower, upper,
        ))
    })
}

/// Weighted 2-D histogram with arbitrary edges. A null `weights` pointer means
/// unit weights; all other buffers are caller-owned f64 arrays.
///
/// # Safety
/// Input pointers address the lengths described by their adjacent arguments;
/// `out` addresses `(x_edge_len - 1) * (y_edge_len - 1)` writable f64s.
#[no_mangle]
#[allow(clippy::too_many_arguments)]
pub unsafe extern "C" fn xy_histogram2d(
    x: *const f64,
    y: *const f64,
    weights: *const f64,
    len: usize,
    x_edges: *const f64,
    x_edge_len: usize,
    y_edges: *const f64,
    y_edge_len: usize,
    out: *mut f64,
) -> i32 {
    if x_edge_len < 2
        || y_edge_len < 2
        || (len > 0 && (x.is_null() || y.is_null()))
        || x_edges.is_null()
        || y_edges.is_null()
        || out.is_null()
    {
        return 0;
    }
    let Some(out_len) = (x_edge_len - 1).checked_mul(y_edge_len - 1) else {
        return 0;
    };
    let x = if len == 0 {
        &[][..]
    } else {
        std::slice::from_raw_parts(x, len)
    };
    let y = if len == 0 {
        &[][..]
    } else {
        std::slice::from_raw_parts(y, len)
    };
    let weights = if weights.is_null() {
        None
    } else {
        Some(std::slice::from_raw_parts(weights, len))
    };
    let x_edges = std::slice::from_raw_parts(x_edges, x_edge_len);
    let y_edges = std::slice::from_raw_parts(y_edges, y_edge_len);
    let out = std::slice::from_raw_parts_mut(out, out_len);
    ffi_guard(0, || {
        i32::from(kernels::histogram2d_into(
            x, y, weights, x_edges, y_edges, out,
        ))
    })
}

/// Expand a rectilinear or curvilinear quadrilateral grid into two triangles
/// per finite cell. Returns the written triangle count or `usize::MAX` on
/// invalid arguments.
///
/// # Safety
/// `values` addresses `cell_rows * cell_cols` f64s. Coordinate lengths are
/// explicit; each output addresses `2 * cell_rows * cell_cols` writable f64s.
#[no_mangle]
#[allow(clippy::too_many_arguments)]
pub unsafe extern "C" fn xy_quad_mesh_triangles(
    x: *const f64,
    x_len: usize,
    y: *const f64,
    y_len: usize,
    values: *const f64,
    cell_rows: usize,
    cell_cols: usize,
    layout: u32,
    out_x0: *mut f64,
    out_y0: *mut f64,
    out_x1: *mut f64,
    out_y1: *mut f64,
    out_x2: *mut f64,
    out_y2: *mut f64,
    out_values: *mut f64,
) -> usize {
    let Some(cell_count) = cell_rows.checked_mul(cell_cols) else {
        return usize::MAX;
    };
    let Some(capacity) = cell_count.checked_mul(2) else {
        return usize::MAX;
    };
    if cell_count == 0
        || x.is_null()
        || y.is_null()
        || values.is_null()
        || out_x0.is_null()
        || out_y0.is_null()
        || out_x1.is_null()
        || out_y1.is_null()
        || out_x2.is_null()
        || out_y2.is_null()
        || out_values.is_null()
    {
        return usize::MAX;
    }
    let x = std::slice::from_raw_parts(x, x_len);
    let y = std::slice::from_raw_parts(y, y_len);
    let values = std::slice::from_raw_parts(values, cell_count);
    let x0 = std::slice::from_raw_parts_mut(out_x0, capacity);
    let y0 = std::slice::from_raw_parts_mut(out_y0, capacity);
    let x1 = std::slice::from_raw_parts_mut(out_x1, capacity);
    let y1 = std::slice::from_raw_parts_mut(out_y1, capacity);
    let x2 = std::slice::from_raw_parts_mut(out_x2, capacity);
    let y2 = std::slice::from_raw_parts_mut(out_y2, capacity);
    let scalar = std::slice::from_raw_parts_mut(out_values, capacity);
    ffi_guard(usize::MAX, || {
        kernels::quad_mesh_triangles_into(
            x, y, values, cell_rows, cell_cols, layout, x0, y0, x1, y1, x2, y2, scalar,
        )
        .unwrap_or(usize::MAX)
    })
}

/// Circular/annular sector tessellation. With `capacity == 0`, output pointers
/// may be null and the required triangle count is returned.
///
/// # Safety
/// Inputs address `len` values; non-null outputs address `capacity` f64s each.
#[no_mangle]
#[allow(clippy::too_many_arguments)]
pub unsafe extern "C" fn xy_sector_triangles(
    values: *const f64,
    len: usize,
    explode: *const f64,
    center_x: f64,
    center_y: f64,
    radius: f64,
    inner_radius: f64,
    start_degrees: f64,
    counterclockwise: i32,
    normalize: i32,
    out_x0: *mut f64,
    out_y0: *mut f64,
    out_x1: *mut f64,
    out_y1: *mut f64,
    out_x2: *mut f64,
    out_y2: *mut f64,
    out_sector: *mut f64,
    capacity: usize,
) -> usize {
    if len == 0
        || values.is_null()
        || !matches!(counterclockwise, 0 | 1)
        || !matches!(normalize, 0 | 1)
        || (capacity > 0
            && (out_x0.is_null()
                || out_y0.is_null()
                || out_x1.is_null()
                || out_y1.is_null()
                || out_x2.is_null()
                || out_y2.is_null()
                || out_sector.is_null()))
    {
        return usize::MAX;
    }
    let values = std::slice::from_raw_parts(values, len);
    let explode = if explode.is_null() {
        &[][..]
    } else {
        std::slice::from_raw_parts(explode, len)
    };
    ffi_guard(usize::MAX, || {
        if capacity == 0 {
            return kernels::sector_triangles_into(
                values,
                explode,
                center_x,
                center_y,
                radius,
                inner_radius,
                start_degrees,
                counterclockwise == 1,
                normalize == 1,
                &mut [],
                &mut [],
                &mut [],
                &mut [],
                &mut [],
                &mut [],
                &mut [],
            )
            .unwrap_or(usize::MAX);
        }
        let x0 = std::slice::from_raw_parts_mut(out_x0, capacity);
        let y0 = std::slice::from_raw_parts_mut(out_y0, capacity);
        let x1 = std::slice::from_raw_parts_mut(out_x1, capacity);
        let y1 = std::slice::from_raw_parts_mut(out_y1, capacity);
        let x2 = std::slice::from_raw_parts_mut(out_x2, capacity);
        let y2 = std::slice::from_raw_parts_mut(out_y2, capacity);
        let sector = std::slice::from_raw_parts_mut(out_sector, capacity);
        kernels::sector_triangles_into(
            values,
            explode,
            center_x,
            center_y,
            radius,
            inner_radius,
            start_degrees,
            counterclockwise == 1,
            normalize == 1,
            x0,
            y0,
            x1,
            y1,
            x2,
            y2,
            sector,
        )
        .unwrap_or(usize::MAX)
    })
}

/// Windowed real FFT, caller-owned nonnegative-frequency outputs.
///
/// # Safety
/// `data` addresses `len` f64s and each output addresses `nfft / 2 + 1` f64s.
#[no_mangle]
#[allow(clippy::too_many_arguments)]
pub unsafe extern "C" fn xy_rfft(
    data: *const f64,
    len: usize,
    nfft: usize,
    sample_rate: f64,
    out_frequency: *mut f64,
    out_real: *mut f64,
    out_imag: *mut f64,
) -> i32 {
    if len == 0
        || data.is_null()
        || out_frequency.is_null()
        || out_real.is_null()
        || out_imag.is_null()
    {
        return 0;
    }
    let bins = nfft / 2 + 1;
    let data = std::slice::from_raw_parts(data, len);
    let frequency = std::slice::from_raw_parts_mut(out_frequency, bins);
    let real = std::slice::from_raw_parts_mut(out_real, bins);
    let imag = std::slice::from_raw_parts_mut(out_imag, bins);
    ffi_guard(0, || {
        i32::from(kernels::rfft_into(
            data,
            nfft,
            sample_rate,
            frequency,
            real,
            imag,
        ))
    })
}

/// Native Welch auto/cross spectra. Null `y` computes only the x autospectrum.
///
/// # Safety
/// Non-null inputs address `len` f64s and all outputs address `nfft / 2 + 1` f64s.
#[no_mangle]
#[allow(clippy::too_many_arguments)]
pub unsafe extern "C" fn xy_welch_spectra(
    x: *const f64,
    y: *const f64,
    len: usize,
    nfft: usize,
    noverlap: usize,
    sample_rate: f64,
    out_frequency: *mut f64,
    out_pxx: *mut f64,
    out_pyy: *mut f64,
    out_pxy_real: *mut f64,
    out_pxy_imag: *mut f64,
) -> i32 {
    if len == 0
        || x.is_null()
        || out_frequency.is_null()
        || out_pxx.is_null()
        || out_pyy.is_null()
        || out_pxy_real.is_null()
        || out_pxy_imag.is_null()
    {
        return 0;
    }
    let bins = nfft / 2 + 1;
    let x = std::slice::from_raw_parts(x, len);
    let y = if y.is_null() {
        None
    } else {
        Some(std::slice::from_raw_parts(y, len))
    };
    let frequency = std::slice::from_raw_parts_mut(out_frequency, bins);
    let pxx = std::slice::from_raw_parts_mut(out_pxx, bins);
    let pyy = std::slice::from_raw_parts_mut(out_pyy, bins);
    let pxy_real = std::slice::from_raw_parts_mut(out_pxy_real, bins);
    let pxy_imag = std::slice::from_raw_parts_mut(out_pxy_imag, bins);
    ffi_guard(0, || {
        i32::from(kernels::welch_spectra_into(
            x,
            y,
            nfft,
            noverlap,
            sample_rate,
            frequency,
            pxx,
            pyy,
            pxy_real,
            pxy_imag,
        ))
    })
}

/// Time-major spectrogram power grid. Output sizes are derived from the
/// caller's `nfft`, `noverlap`, and input length.
///
/// # Safety
/// `data` addresses `len` f64s and outputs address the derived sizes.
#[no_mangle]
#[allow(clippy::too_many_arguments)]
pub unsafe extern "C" fn xy_spectrogram(
    data: *const f64,
    len: usize,
    nfft: usize,
    noverlap: usize,
    sample_rate: f64,
    out_frequency: *mut f64,
    out_time: *mut f64,
    out_power: *mut f64,
) -> i32 {
    if len == 0
        || data.is_null()
        || nfft == 0
        || noverlap >= nfft
        || out_frequency.is_null()
        || out_time.is_null()
        || out_power.is_null()
    {
        return 0;
    }
    let bins = nfft / 2 + 1;
    let segments = if len <= nfft {
        1
    } else {
        1 + (len - nfft) / (nfft - noverlap)
    };
    let Some(power_len) = bins.checked_mul(segments) else {
        return 0;
    };
    let data = std::slice::from_raw_parts(data, len);
    let frequency = std::slice::from_raw_parts_mut(out_frequency, bins);
    let time = std::slice::from_raw_parts_mut(out_time, segments);
    let power = std::slice::from_raw_parts_mut(out_power, power_len);
    ffi_guard(0, || {
        i32::from(kernels::spectrogram_into(
            data,
            nfft,
            noverlap,
            sample_rate,
            frequency,
            time,
            power,
        ))
    })
}

/// Lag correlation used by acorr/xcorr.
///
/// # Safety
/// Inputs address `len` f64s; outputs address `2 * max_lag + 1` f64s.
#[no_mangle]
#[allow(clippy::too_many_arguments)]
pub unsafe extern "C" fn xy_correlation(
    x: *const f64,
    y: *const f64,
    len: usize,
    max_lag: usize,
    normalize: i32,
    out_lag: *mut f64,
    out_correlation: *mut f64,
) -> i32 {
    if len == 0
        || x.is_null()
        || y.is_null()
        || !matches!(normalize, 0 | 1)
        || out_lag.is_null()
        || out_correlation.is_null()
    {
        return 0;
    }
    let Some(output_len) = max_lag
        .checked_mul(2)
        .and_then(|value| value.checked_add(1))
    else {
        return 0;
    };
    let x = std::slice::from_raw_parts(x, len);
    let y = std::slice::from_raw_parts(y, len);
    let lag = std::slice::from_raw_parts_mut(out_lag, output_len);
    let correlation = std::slice::from_raw_parts_mut(out_correlation, output_len);
    ffi_guard(0, || {
        i32::from(kernels::correlation_into(
            x,
            y,
            max_lag,
            normalize == 1,
            lag,
            correlation,
        ))
    })
}

/// Weighted empirical CDF. Returns the number of coalesced values or
/// `usize::MAX` when the inputs are invalid.
///
/// # Safety
/// Inputs address `len` readable f64s and outputs address `len` writable f64s.
#[no_mangle]
pub unsafe extern "C" fn xy_weighted_ecdf(
    values: *const f64,
    weights: *const f64,
    len: usize,
    out_values: *mut f64,
    out_cumulative: *mut f64,
) -> usize {
    if len == 0
        || values.is_null()
        || weights.is_null()
        || out_values.is_null()
        || out_cumulative.is_null()
    {
        return usize::MAX;
    }
    let values = std::slice::from_raw_parts(values, len);
    let weights = std::slice::from_raw_parts(weights, len);
    let output_values = std::slice::from_raw_parts_mut(out_values, len);
    let cumulative = std::slice::from_raw_parts_mut(out_cumulative, len);
    ffi_guard(usize::MAX, || {
        kernels::weighted_ecdf_into(values, weights, output_values, cumulative)
            .unwrap_or(usize::MAX)
    })
}

/// Expand indexed topology into independent filled triangles.
///
/// # Safety
/// All pointers address the element counts derived from adjacent length arguments.
#[no_mangle]
#[allow(clippy::too_many_arguments)]
pub unsafe extern "C" fn xy_indexed_triangles(
    x: *const f64,
    y: *const f64,
    vertex_count: usize,
    triangles: *const i64,
    face_count: usize,
    values: *const f64,
    value_len: usize,
    value_mode: u32,
    out_x0: *mut f64,
    out_y0: *mut f64,
    out_x1: *mut f64,
    out_y1: *mut f64,
    out_x2: *mut f64,
    out_y2: *mut f64,
    out_values: *mut f64,
) -> usize {
    let Some(index_count) = face_count.checked_mul(3) else {
        return usize::MAX;
    };
    if x.is_null()
        || y.is_null()
        || triangles.is_null()
        || (value_len > 0 && values.is_null())
        || out_x0.is_null()
        || out_y0.is_null()
        || out_x1.is_null()
        || out_y1.is_null()
        || out_x2.is_null()
        || out_y2.is_null()
        || out_values.is_null()
    {
        return usize::MAX;
    }
    let x = std::slice::from_raw_parts(x, vertex_count);
    let y = std::slice::from_raw_parts(y, vertex_count);
    let triangles = std::slice::from_raw_parts(triangles, index_count);
    let values = if value_len == 0 {
        &[][..]
    } else {
        std::slice::from_raw_parts(values, value_len)
    };
    let x0 = std::slice::from_raw_parts_mut(out_x0, face_count);
    let y0 = std::slice::from_raw_parts_mut(out_y0, face_count);
    let x1 = std::slice::from_raw_parts_mut(out_x1, face_count);
    let y1 = std::slice::from_raw_parts_mut(out_y1, face_count);
    let x2 = std::slice::from_raw_parts_mut(out_x2, face_count);
    let y2 = std::slice::from_raw_parts_mut(out_y2, face_count);
    let scalar = std::slice::from_raw_parts_mut(out_values, face_count);
    ffi_guard(usize::MAX, || {
        kernels::indexed_triangles_into(
            x, y, triangles, values, value_mode, x0, y0, x1, y1, x2, y2, scalar,
        )
        .unwrap_or(usize::MAX)
    })
}

/// Emit unique line segments for indexed triangle edges.
///
/// # Safety
/// Vertex inputs address `vertex_count`, topology addresses `face_count * 3`,
/// and each output addresses that same topology length.
#[no_mangle]
#[allow(clippy::too_many_arguments)]
pub unsafe extern "C" fn xy_triangle_edges(
    x: *const f64,
    y: *const f64,
    vertex_count: usize,
    triangles: *const i64,
    face_count: usize,
    out_x0: *mut f64,
    out_x1: *mut f64,
    out_y0: *mut f64,
    out_y1: *mut f64,
) -> usize {
    let Some(capacity) = face_count.checked_mul(3) else {
        return usize::MAX;
    };
    if x.is_null()
        || y.is_null()
        || triangles.is_null()
        || out_x0.is_null()
        || out_x1.is_null()
        || out_y0.is_null()
        || out_y1.is_null()
    {
        return usize::MAX;
    }
    let x = std::slice::from_raw_parts(x, vertex_count);
    let y = std::slice::from_raw_parts(y, vertex_count);
    let triangles = std::slice::from_raw_parts(triangles, capacity);
    let x0 = std::slice::from_raw_parts_mut(out_x0, capacity);
    let x1 = std::slice::from_raw_parts_mut(out_x1, capacity);
    let y0 = std::slice::from_raw_parts_mut(out_y0, capacity);
    let y1 = std::slice::from_raw_parts_mut(out_y1, capacity);
    ffi_guard(usize::MAX, || {
        kernels::triangle_edges_into(x, y, triangles, x0, x1, y0, y1).unwrap_or(usize::MAX)
    })
}

/// Delaunay topology for finite unique 2-D points. `out` addresses
/// `capacity * 3` writable i64 indices; returns the face count.
///
/// # Safety
/// `x` and `y` address `len` f64s; `out` addresses `capacity * 3` i64s.
#[no_mangle]
pub unsafe extern "C" fn xy_delaunay_triangles(
    x: *const f64,
    y: *const f64,
    len: usize,
    out: *mut i64,
    capacity: usize,
) -> usize {
    if len < 3 || x.is_null() || y.is_null() || out.is_null() {
        return usize::MAX;
    }
    let x = std::slice::from_raw_parts(x, len);
    let y = std::slice::from_raw_parts(y, len);
    ffi_guard(usize::MAX, || {
        let Some(triangles) = kernels::delaunay_triangles(x, y) else {
            return usize::MAX;
        };
        if triangles.len() > capacity {
            return usize::MAX;
        }
        let Some(output_len) = capacity.checked_mul(3) else {
            return usize::MAX;
        };
        let output = std::slice::from_raw_parts_mut(out, output_len);
        for (index, triangle) in triangles.iter().enumerate() {
            output[index * 3..index * 3 + 3].copy_from_slice(triangle);
        }
        triangles.len()
    })
}

/// Ear-clipping topology for a finite simple polygon. `capacity` is the
/// number of output faces, and `out` addresses `capacity * 3` i64 indices.
///
/// # Safety
/// `x` and `y` address `len` f64s; `out` addresses `capacity * 3` i64s.
#[no_mangle]
pub unsafe extern "C" fn xy_polygon_triangles(
    x: *const f64,
    y: *const f64,
    len: usize,
    out: *mut i64,
    capacity: usize,
) -> usize {
    if len < 3 || x.is_null() || y.is_null() || out.is_null() {
        return usize::MAX;
    }
    let x = std::slice::from_raw_parts(x, len);
    let y = std::slice::from_raw_parts(y, len);
    ffi_guard(usize::MAX, || {
        let Some(triangles) = kernels::polygon_triangles(x, y) else {
            return usize::MAX;
        };
        if triangles.len() > capacity {
            return usize::MAX;
        }
        let Some(output_len) = capacity.checked_mul(3) else {
            return usize::MAX;
        };
        let output = std::slice::from_raw_parts_mut(out, output_len);
        for (index, triangle) in triangles.iter().enumerate() {
            output[index * 3..index * 3 + 3].copy_from_slice(triangle);
        }
        triangles.len()
    })
}

/// Indexed triangular isoline extraction. `capacity == 0` queries the segment
/// count; otherwise all five outputs address `capacity` writable f64s.
///
/// # Safety
/// Inputs and outputs address the element counts described by their length arguments.
#[no_mangle]
#[allow(clippy::too_many_arguments)]
pub unsafe extern "C" fn xy_marching_triangles(
    x: *const f64,
    y: *const f64,
    z: *const f64,
    vertex_count: usize,
    triangles: *const i64,
    face_count: usize,
    levels: *const f64,
    level_count: usize,
    out_x0: *mut f64,
    out_x1: *mut f64,
    out_y0: *mut f64,
    out_y1: *mut f64,
    out_levels: *mut f64,
    capacity: usize,
) -> usize {
    let Some(index_count) = face_count.checked_mul(3) else {
        return usize::MAX;
    };
    if x.is_null()
        || y.is_null()
        || z.is_null()
        || triangles.is_null()
        || (level_count > 0 && levels.is_null())
        || (capacity > 0
            && (out_x0.is_null()
                || out_x1.is_null()
                || out_y0.is_null()
                || out_y1.is_null()
                || out_levels.is_null()))
    {
        return usize::MAX;
    }
    let x = std::slice::from_raw_parts(x, vertex_count);
    let y = std::slice::from_raw_parts(y, vertex_count);
    let z = std::slice::from_raw_parts(z, vertex_count);
    let triangles = std::slice::from_raw_parts(triangles, index_count);
    let levels = if level_count == 0 {
        &[][..]
    } else {
        std::slice::from_raw_parts(levels, level_count)
    };
    ffi_guard(usize::MAX, || {
        if capacity == 0 {
            return kernels::marching_triangles_into(
                x,
                y,
                z,
                triangles,
                levels,
                &mut [],
                &mut [],
                &mut [],
                &mut [],
                &mut [],
            )
            .unwrap_or(usize::MAX);
        }
        let x0 = std::slice::from_raw_parts_mut(out_x0, capacity);
        let x1 = std::slice::from_raw_parts_mut(out_x1, capacity);
        let y0 = std::slice::from_raw_parts_mut(out_y0, capacity);
        let y1 = std::slice::from_raw_parts_mut(out_y1, capacity);
        let out_levels = std::slice::from_raw_parts_mut(out_levels, capacity);
        kernels::marching_triangles_into(x, y, z, triangles, levels, x0, x1, y0, y1, out_levels)
            .unwrap_or(usize::MAX)
    })
}

/// Vector origins/components to instanced shaft + arrowhead segments.
/// Returns the written segment count or `usize::MAX` on invalid arguments.
///
/// # Safety
/// Inputs address `len` f64 values; outputs address `3 * len` writable f64s.
#[no_mangle]
#[allow(clippy::too_many_arguments)]
pub unsafe extern "C" fn xy_vector_segments(
    x: *const f64,
    y: *const f64,
    u: *const f64,
    v: *const f64,
    len: usize,
    scale: f64,
    pivot: u32,
    head_ratio: f64,
    out_x0: *mut f64,
    out_x1: *mut f64,
    out_y0: *mut f64,
    out_y1: *mut f64,
) -> usize {
    let Some(capacity) = len.checked_mul(3) else {
        return usize::MAX;
    };
    if len == 0 {
        return 0;
    }
    if x.is_null()
        || y.is_null()
        || u.is_null()
        || v.is_null()
        || out_x0.is_null()
        || out_x1.is_null()
        || out_y0.is_null()
        || out_y1.is_null()
    {
        return usize::MAX;
    }
    let x = std::slice::from_raw_parts(x, len);
    let y = std::slice::from_raw_parts(y, len);
    let u = std::slice::from_raw_parts(u, len);
    let v = std::slice::from_raw_parts(v, len);
    let x0 = std::slice::from_raw_parts_mut(out_x0, capacity);
    let x1 = std::slice::from_raw_parts_mut(out_x1, capacity);
    let y0 = std::slice::from_raw_parts_mut(out_y0, capacity);
    let y1 = std::slice::from_raw_parts_mut(out_y1, capacity);
    ffi_guard(usize::MAX, || {
        kernels::vector_segments_into(x, y, u, v, scale, pivot, head_ratio, x0, x1, y0, y1)
            .unwrap_or(usize::MAX)
    })
}

/// Regular-grid streamline integration. With `capacity == 0`, returns the
/// required segment count without writing outputs; otherwise writes four
/// parallel segment columns and returns the same count.
///
/// # Safety
/// Coordinate/vector buffers and optional outputs have the dimensions given
/// by `rows`, `cols`, and `capacity`.
#[no_mangle]
#[allow(clippy::too_many_arguments)]
pub unsafe extern "C" fn xy_streamlines(
    x_coords: *const f64,
    cols: usize,
    y_coords: *const f64,
    rows: usize,
    u: *const f64,
    v: *const f64,
    density: f64,
    max_steps: usize,
    out_x0: *mut f64,
    out_x1: *mut f64,
    out_y0: *mut f64,
    out_y1: *mut f64,
    capacity: usize,
) -> usize {
    let Some(len) = rows.checked_mul(cols) else {
        return usize::MAX;
    };
    if rows < 2
        || cols < 2
        || x_coords.is_null()
        || y_coords.is_null()
        || u.is_null()
        || v.is_null()
        || (capacity > 0
            && (out_x0.is_null() || out_x1.is_null() || out_y0.is_null() || out_y1.is_null()))
    {
        return usize::MAX;
    }
    let x_coords = std::slice::from_raw_parts(x_coords, cols);
    let y_coords = std::slice::from_raw_parts(y_coords, rows);
    let u = std::slice::from_raw_parts(u, len);
    let v = std::slice::from_raw_parts(v, len);
    ffi_guard(usize::MAX, || {
        let Some(segments) = kernels::streamlines(x_coords, y_coords, u, v, density, max_steps)
        else {
            return usize::MAX;
        };
        if capacity == 0 {
            return segments.len();
        }
        if capacity < segments.len() {
            return usize::MAX;
        }
        let x0 = std::slice::from_raw_parts_mut(out_x0, capacity);
        let x1 = std::slice::from_raw_parts_mut(out_x1, capacity);
        let y0 = std::slice::from_raw_parts_mut(out_y0, capacity);
        let y1 = std::slice::from_raw_parts_mut(out_y1, capacity);
        for (index, &(sx0, sx1, sy0, sy1)) in segments.iter().enumerate() {
            x0[index] = sx0;
            x1[index] = sx1;
            y0[index] = sy0;
            y1[index] = sy1;
        }
        segments.len()
    })
}

/// Marching-squares isoline extraction over a regular grid. The first call
/// may pass `capacity == 0` and null output pointers to query the required
/// segment count; a later call writes the five parallel f64 output arrays.
/// Returns the required/written segment count, or `usize::MAX` on invalid
/// arguments or a panic inside the kernel.
///
/// # Safety
/// `z` points to `rows * cols` readable f64s; `x_coords` has `cols` values;
/// `y_coords` has `rows` values; `levels` has `n_levels` values. When capacity
/// is nonzero, every output pointer points to `capacity` writable f64s.
#[no_mangle]
#[allow(clippy::too_many_arguments)]
pub unsafe extern "C" fn xy_marching_squares(
    z: *const f64,
    rows: usize,
    cols: usize,
    x_coords: *const f64,
    y_coords: *const f64,
    levels: *const f64,
    n_levels: usize,
    corner_mask: u8,
    out_x0: *mut f64,
    out_x1: *mut f64,
    out_y0: *mut f64,
    out_y1: *mut f64,
    out_levels: *mut f64,
    capacity: usize,
) -> usize {
    if rows < 2 || cols < 2 {
        return usize::MAX;
    }
    let z_len = match rows.checked_mul(cols) {
        Some(n) => n,
        None => return usize::MAX,
    };
    if z.is_null() || x_coords.is_null() || y_coords.is_null() {
        return usize::MAX;
    }
    if n_levels > 0 && levels.is_null() {
        return usize::MAX;
    }
    if capacity > 0
        && (out_x0.is_null()
            || out_x1.is_null()
            || out_y0.is_null()
            || out_y1.is_null()
            || out_levels.is_null())
    {
        return usize::MAX;
    }
    let z = std::slice::from_raw_parts(z, z_len);
    let x_coords = std::slice::from_raw_parts(x_coords, cols);
    let y_coords = std::slice::from_raw_parts(y_coords, rows);
    let levels = if n_levels == 0 {
        &[][..]
    } else {
        std::slice::from_raw_parts(levels, n_levels)
    };
    if !x_coords.windows(2).all(|pair| pair[1] > pair[0])
        || !y_coords.windows(2).all(|pair| pair[1] > pair[0])
        || !x_coords.iter().all(|value| value.is_finite())
        || !y_coords.iter().all(|value| value.is_finite())
        || !levels.iter().all(|value| value.is_finite())
    {
        return usize::MAX;
    }
    ffi_guard(usize::MAX, || {
        if capacity == 0 {
            let (empty_x0, empty_x1, empty_y0, empty_y1, empty_levels) = (
                &mut [][..],
                &mut [][..],
                &mut [][..],
                &mut [][..],
                &mut [][..],
            );
            kernels::marching_squares_into(
                z,
                rows,
                cols,
                x_coords,
                y_coords,
                levels,
                corner_mask != 0,
                empty_x0,
                empty_x1,
                empty_y0,
                empty_y1,
                empty_levels,
            )
        } else {
            let x0_out = std::slice::from_raw_parts_mut(out_x0, capacity);
            let x1_out = std::slice::from_raw_parts_mut(out_x1, capacity);
            let y0_out = std::slice::from_raw_parts_mut(out_y0, capacity);
            let y1_out = std::slice::from_raw_parts_mut(out_y1, capacity);
            let level_out = std::slice::from_raw_parts_mut(out_levels, capacity);
            kernels::marching_squares_into(
                z,
                rows,
                cols,
                x_coords,
                y_coords,
                levels,
                corner_mask != 0,
                x0_out,
                x1_out,
                y0_out,
                y1_out,
                level_out,
            )
        }
    })
}

/// 2D density aggregation (§5 Tier 2): additively bin points into a `w × h`
/// grid over the viewport. `out` must be `w * h` f32s (fully overwritten).
///
/// # Safety
/// `x`/`y` must point to `len` readable f64s; `out` to `w * h` writable f32s;
/// `w > 0 && h > 0` and finite increasing bounds.
#[no_mangle]
#[allow(clippy::too_many_arguments)]
pub unsafe extern "C" fn xy_bin_2d(
    x: *const f64,
    y: *const f64,
    len: usize,
    x0: f64,
    x1: f64,
    y0: f64,
    y1: f64,
    w: usize,
    h: usize,
    out: *mut f32,
) -> i32 {
    let bad = w == 0 || h == 0 || !finite_gt(x0, x1) || !finite_gt(y0, y1);
    if bad {
        return 0;
    }
    if out.is_null() {
        return 0;
    }
    let grid_len = match w.checked_mul(h) {
        Some(n) => n,
        None => return 0,
    };
    let (x, y) = if len == 0 {
        (&[][..], &[][..])
    } else {
        if x.is_null() || y.is_null() {
            return 0;
        }
        (
            std::slice::from_raw_parts(x, len),
            std::slice::from_raw_parts(y, len),
        )
    };
    let out = std::slice::from_raw_parts_mut(out, grid_len);
    ffi_guard(0, || {
        kernels::bin_2d(x, y, x0, x1, y0, y1, w, h, out);
        1
    })
}

/// f32-input density bin for the out-of-core spatial index (`_spatial.py`):
/// bins memmap'd f32 (lon, lat) directly into a `w*h` f32 grid, skipping the
/// f64 widening that dominates a windowed gather. Bit-identical to `xy_bin_2d`
/// over the same points cast to f64. Returns 1 on success, 0 on bad args.
///
/// # Safety
/// `x`/`y` must each point to `len` readable f32 (or be null iff `len == 0`);
/// `out` must point to `w*h` writable f32.
#[no_mangle]
pub unsafe extern "C" fn xy_bin_2d_f32(
    x: *const f32,
    y: *const f32,
    len: usize,
    x0: f64,
    x1: f64,
    y0: f64,
    y1: f64,
    w: usize,
    h: usize,
    out: *mut f32,
) -> i32 {
    let bad = w == 0 || h == 0 || !finite_gt(x0, x1) || !finite_gt(y0, y1);
    if bad || out.is_null() {
        return 0;
    }
    let grid_len = match w.checked_mul(h) {
        Some(n) => n,
        None => return 0,
    };
    let (x, y) = if len == 0 {
        (&[][..], &[][..])
    } else {
        if x.is_null() || y.is_null() {
            return 0;
        }
        (
            std::slice::from_raw_parts(x, len),
            std::slice::from_raw_parts(y, len),
        )
    };
    let out = std::slice::from_raw_parts_mut(out, grid_len);
    ffi_guard(0, || {
        kernels::bin_2d_f32(x, y, x0, x1, y0, y1, w, h, out);
        1
    })
}

/// Marshal the C-side color source for mean-color binning: exactly one of
/// `idx` (one LUT index per point, with `lut`/`lut_len` as 1..=256 RGBA8
/// entries) or `rgba` (straight-alpha RGBA8, 4 bytes per point) must be
/// non-null. Returns `None` for any other shape.
///
/// # Safety
/// Non-null pointers must address the documented lengths for `len` points.
unsafe fn color_source_from_raw<'a>(
    len: usize,
    idx: *const u8,
    rgba: *const u8,
    lut: *const u8,
    lut_len: usize,
) -> Option<kernels::BinColorSource<'a>> {
    match (idx.is_null(), rgba.is_null()) {
        (false, true) => {
            if lut.is_null() || lut_len == 0 || lut_len > 256 {
                return None;
            }
            Some(kernels::BinColorSource::Indexed {
                idx: std::slice::from_raw_parts(idx, len),
                lut: std::slice::from_raw_parts(lut as *const [u8; 4], lut_len),
            })
        }
        (true, false) => Some(kernels::BinColorSource::Rgba(std::slice::from_raw_parts(
            rgba,
            len * 4,
        ))),
        _ => None,
    }
}

/// Mean-color companion grid to `xy_bin_2d` (§5 Tier 2, LOD doc §2): fill
/// `out` (`w*h*4` straight-alpha RGBA8, row 0 = bottom) with each cell's
/// alpha-weighted mean point color (linear-light average, sRGB bytes out)
/// and mean point alpha. Cell membership is bit-identical to `xy_bin_2d`.
/// Returns 1 on success, 0 on invalid arguments.
///
/// # Safety
/// `x`/`y` must point to `len` readable f64s; the color source pointers must
/// satisfy `color_source_from_raw`; `out` must address `w*h*4` writable bytes.
#[no_mangle]
#[allow(clippy::too_many_arguments)]
pub unsafe extern "C" fn xy_bin_2d_mean_color(
    x: *const f64,
    y: *const f64,
    len: usize,
    idx: *const u8,
    rgba: *const u8,
    lut: *const u8,
    lut_len: usize,
    x0: f64,
    x1: f64,
    y0: f64,
    y1: f64,
    w: usize,
    h: usize,
    out: *mut u8,
) -> i32 {
    if w == 0 || h == 0 || !finite_gt(x0, x1) || !finite_gt(y0, y1) || out.is_null() {
        return 0;
    }
    let Some(grid_len) = w.checked_mul(h).and_then(|n| n.checked_mul(4)) else {
        return 0;
    };
    let out = std::slice::from_raw_parts_mut(out, grid_len);
    if len == 0 {
        out.fill(0);
        return 1;
    }
    if x.is_null() || y.is_null() {
        return 0;
    }
    let Some(colors) = color_source_from_raw(len, idx, rgba, lut, lut_len) else {
        return 0;
    };
    let x = std::slice::from_raw_parts(x, len);
    let y = std::slice::from_raw_parts(y, len);
    ffi_guard(0, || {
        kernels::bin_2d_mean_color(x, y, &colors, x0, x1, y0, y1, w, h, out);
        1
    })
}

/// Native PNG rasterizer (dossier Phase 3). Paints a Python-built display list
/// (`cmd[0..cmd_len]`, see `raster.rs`/`_raster.py`) into a caller-owned
/// straight-alpha RGBA8 framebuffer `out` of `w*h*4` bytes. Returns 1 on
/// success, 0 on a malformed command buffer or size mismatch (output undefined).
///
/// # Safety
/// `cmd` must point to `cmd_len` readable bytes (or be null iff `cmd_len == 0`);
/// `out` must point to `w*h*4` writable bytes.
#[no_mangle]
pub unsafe extern "C" fn xy_rasterize(
    cmd: *const u8,
    cmd_len: usize,
    out: *mut u8,
    w: usize,
    h: usize,
) -> i32 {
    if out.is_null() || w == 0 || h == 0 {
        return 0;
    }
    let out_len = match w.checked_mul(h).and_then(|n| n.checked_mul(4)) {
        Some(n) => n,
        None => return 0,
    };
    let cmds = if cmd_len == 0 {
        &[][..]
    } else if cmd.is_null() {
        return 0;
    } else {
        std::slice::from_raw_parts(cmd, cmd_len)
    };
    let out = std::slice::from_raw_parts_mut(out, out_len);
    ffi_guard(0, || raster::rasterize_into(cmds, w, h, out) as i32)
}

/// Rasterize a display list that may reference an immutable external byte
/// arena. The arena is borrowed only for this synchronous call; no pointer is
/// retained by Rust. This keeps large already-built grids out of the command
/// buffer without changing their ownership.
///
/// # Safety
/// Pointer contracts match `xy_rasterize`; `data` must point to `data_len`
/// readable bytes, or be null iff `data_len == 0`.
#[no_mangle]
pub unsafe extern "C" fn xy_rasterize_data(
    cmd: *const u8,
    cmd_len: usize,
    data: *const u8,
    data_len: usize,
    out: *mut u8,
    w: usize,
    h: usize,
) -> i32 {
    if out.is_null() || w == 0 || h == 0 {
        return 0;
    }
    let out_len = match w.checked_mul(h).and_then(|n| n.checked_mul(4)) {
        Some(n) => n,
        None => return 0,
    };
    let cmds = if cmd_len == 0 {
        &[][..]
    } else if cmd.is_null() {
        return 0;
    } else {
        std::slice::from_raw_parts(cmd, cmd_len)
    };
    let arena = if data_len == 0 {
        &[][..]
    } else if data.is_null() {
        return 0;
    } else {
        std::slice::from_raw_parts(data, data_len)
    };
    let out = std::slice::from_raw_parts_mut(out, out_len);
    ffi_guard(0, || {
        raster::rasterize_data_into(cmds, arena, w, h, out) as i32
    })
}

/// Rasterize with multiple immutable arenas, used by static export to borrow
/// canonical arrays alongside the ordinary owned payload blob.
///
/// # Safety
/// `span_ptrs` and `span_lens` must each contain `span_count` readable entries;
/// every non-empty span pointer must address its declared readable byte range.
/// Other pointer contracts match `xy_rasterize`.
#[no_mangle]
pub unsafe extern "C" fn xy_rasterize_spans(
    cmd: *const u8,
    cmd_len: usize,
    span_ptrs: *const *const u8,
    span_lens: *const usize,
    span_count: usize,
    out: *mut u8,
    w: usize,
    h: usize,
) -> i32 {
    if out.is_null() || w == 0 || h == 0 {
        return 0;
    }
    let out_len = match w.checked_mul(h).and_then(|n| n.checked_mul(4)) {
        Some(n) => n,
        None => return 0,
    };
    let cmds = if cmd_len == 0 {
        &[][..]
    } else if cmd.is_null() {
        return 0;
    } else {
        std::slice::from_raw_parts(cmd, cmd_len)
    };
    let Some(spans) = borrowed_byte_spans(span_ptrs, span_lens, span_count) else {
        return 0;
    };
    let out = std::slice::from_raw_parts_mut(out, out_len);
    ffi_guard(0, || {
        raster::rasterize_spans_into(cmds, &spans, w, h, out) as i32
    })
}

/// Fused native raster + fast PNG encoder. Returns the PNG byte count written
/// to `out`, or `usize::MAX` when the command stream is malformed, dimensions
/// overflow, or `out_capacity` is insufficient.
///
/// # Safety
/// Pointer contracts match `xy_rasterize`; `out` must point to
/// `out_capacity` writable bytes.
#[no_mangle]
pub unsafe extern "C" fn xy_rasterize_png(
    cmd: *const u8,
    cmd_len: usize,
    out: *mut u8,
    out_capacity: usize,
    w: usize,
    h: usize,
) -> usize {
    if out.is_null() || out_capacity == 0 || w == 0 || h == 0 {
        return usize::MAX;
    }
    let cmds = if cmd_len == 0 {
        &[][..]
    } else if cmd.is_null() {
        return usize::MAX;
    } else {
        std::slice::from_raw_parts(cmd, cmd_len)
    };
    let out = std::slice::from_raw_parts_mut(out, out_capacity);
    ffi_guard(usize::MAX, || {
        raster::rasterize_png_into(cmds, w, h, out).unwrap_or(usize::MAX)
    })
}

/// Fused PNG rasterizer with the synchronous external arena accepted by
/// `xy_rasterize_data`.
///
/// # Safety
/// Pointer contracts match `xy_rasterize_data`; `out` must point to
/// `out_capacity` writable bytes.
#[no_mangle]
pub unsafe extern "C" fn xy_rasterize_png_data(
    cmd: *const u8,
    cmd_len: usize,
    data: *const u8,
    data_len: usize,
    out: *mut u8,
    out_capacity: usize,
    w: usize,
    h: usize,
) -> usize {
    if out.is_null() || out_capacity == 0 || w == 0 || h == 0 {
        return usize::MAX;
    }
    let cmds = if cmd_len == 0 {
        &[][..]
    } else if cmd.is_null() {
        return usize::MAX;
    } else {
        std::slice::from_raw_parts(cmd, cmd_len)
    };
    let arena = if data_len == 0 {
        &[][..]
    } else if data.is_null() {
        return usize::MAX;
    } else {
        std::slice::from_raw_parts(data, data_len)
    };
    let out = std::slice::from_raw_parts_mut(out, out_capacity);
    ffi_guard(usize::MAX, || {
        raster::rasterize_png_data_into(cmds, arena, w, h, out).unwrap_or(usize::MAX)
    })
}

/// Fused PNG rasterizer backed by multiple synchronous immutable arenas.
///
/// # Safety
/// Span contracts match `xy_rasterize_spans`; output contracts match
/// `xy_rasterize_png`.
#[no_mangle]
pub unsafe extern "C" fn xy_rasterize_png_spans(
    cmd: *const u8,
    cmd_len: usize,
    span_ptrs: *const *const u8,
    span_lens: *const usize,
    span_count: usize,
    out: *mut u8,
    out_capacity: usize,
    w: usize,
    h: usize,
) -> usize {
    if out.is_null() || out_capacity == 0 || w == 0 || h == 0 {
        return usize::MAX;
    }
    let cmds = if cmd_len == 0 {
        &[][..]
    } else if cmd.is_null() {
        return usize::MAX;
    } else {
        std::slice::from_raw_parts(cmd, cmd_len)
    };
    let Some(spans) = borrowed_byte_spans(span_ptrs, span_lens, span_count) else {
        return usize::MAX;
    };
    let out = std::slice::from_raw_parts_mut(out, out_capacity);
    ffi_guard(usize::MAX, || {
        raster::rasterize_png_spans_into(cmds, &spans, w, h, out).unwrap_or(usize::MAX)
    })
}

/// Native heatmap scalar-to-RGBA mapper used by the static raster path.
/// Returns 1 on success and 0 on invalid dimensions or pointers.
///
/// # Safety
/// `raw` contains `w*h` readable f64 values, `stops` contains
/// `stop_count*3` readable bytes, and `out` contains `w*h*4` writable bytes.
#[no_mangle]
pub unsafe extern "C" fn xy_heatmap_rgba(
    raw: *const f64,
    w: usize,
    h: usize,
    stops: *const u8,
    stop_count: usize,
    alpha: u8,
    out: *mut u8,
) -> i32 {
    let Some(len) = w.checked_mul(h) else {
        return 0;
    };
    if len == 0 || stop_count == 0 || raw.is_null() || stops.is_null() || out.is_null() {
        return 0;
    }
    let Some(out_len) = len.checked_mul(4) else {
        return 0;
    };
    let Some(stop_len) = stop_count.checked_mul(3) else {
        return 0;
    };
    let raw = std::slice::from_raw_parts(raw, len);
    let stop_bytes = std::slice::from_raw_parts(stops, stop_len);
    let stops = std::slice::from_raw_parts(stop_bytes.as_ptr().cast::<[u8; 3]>(), stop_count);
    let out = std::slice::from_raw_parts_mut(out, out_len);
    ffi_guard(0, || {
        kernels::heatmap_rgba_into(raw, w, h, stops, alpha, out) as i32
    })
}

/// Native log-u8 density colormap used by the static raster path. Returns 1
/// on success and 0 on invalid dimensions, values, or pointers.
///
/// # Safety
/// `encoded` contains `w*h` readable bytes, `stops` contains `stop_count*3`
/// readable bytes, and `out` contains `w*h*4` writable bytes.
#[no_mangle]
pub unsafe extern "C" fn xy_density_rgba(
    encoded: *const u8,
    w: usize,
    h: usize,
    maximum: f64,
    stops: *const u8,
    stop_count: usize,
    opacity: f64,
    out: *mut u8,
) -> i32 {
    let Some(len) = w.checked_mul(h) else {
        return 0;
    };
    let Some(out_len) = len.checked_mul(4) else {
        return 0;
    };
    let Some(stop_len) = stop_count.checked_mul(3) else {
        return 0;
    };
    if len == 0 || stop_count == 0 || encoded.is_null() || stops.is_null() || out.is_null() {
        return 0;
    }
    let encoded = std::slice::from_raw_parts(encoded, len);
    let stop_bytes = std::slice::from_raw_parts(stops, stop_len);
    let stops = std::slice::from_raw_parts(stop_bytes.as_ptr().cast::<[u8; 3]>(), stop_count);
    let out = std::slice::from_raw_parts_mut(out, out_len);
    ffi_guard(0, || {
        kernels::density_rgba_into(encoded, w, h, maximum, stops, opacity, out) as i32
    })
}

/// Log-encode a density grid into the one-byte wire/texture representation.
/// Returns 1 on success and writes the original grid maximum.
///
/// # Safety
/// `grid` addresses `len` readable f32 values, `out` addresses `len` writable
/// bytes, and `out_max` addresses one writable f64.
#[no_mangle]
pub unsafe extern "C" fn xy_density_log_u8(
    grid: *const f32,
    len: usize,
    out: *mut u8,
    out_max: *mut f64,
) -> i32 {
    if out_max.is_null() || (len > 0 && (grid.is_null() || out.is_null())) {
        return 0;
    }
    let grid = if len == 0 {
        &[][..]
    } else {
        std::slice::from_raw_parts(grid, len)
    };
    let out = if len == 0 {
        &mut [][..]
    } else {
        std::slice::from_raw_parts_mut(out, len)
    };
    ffi_guard(0, || {
        *out_max = kernels::density_log_u8_into(grid, out);
        1
    })
}

/// Fused density scan (§5 Tier 2): one pass writing BOTH the count grid
/// (bin_2d semantics: half-open finite window) and the ascending in-window
/// row indices (range_indices semantics: inclusive window). Each output is
/// bitwise identical to its standalone kernel. Returns the index count, or
/// `usize::MAX` on invalid arguments.
///
/// # Safety
/// `x`/`y` must point to `len` readable f64s; `grid` to `w*h` writable f32s;
/// `idx` to `len` writable u32s.
#[no_mangle]
pub unsafe extern "C" fn xy_bin_2d_indices(
    x: *const f64,
    y: *const f64,
    len: usize,
    x0: f64,
    x1: f64,
    y0: f64,
    y1: f64,
    w: usize,
    h: usize,
    grid: *mut f32,
    idx: *mut u32,
) -> usize {
    let bad = w == 0 || h == 0 || !finite_gt(x0, x1) || !finite_gt(y0, y1);
    if bad || grid.is_null() {
        return usize::MAX;
    }
    // u32 index ceiling + unwrappable grid size — see xy_m4_indices.
    if len > u32::MAX as usize {
        return usize::MAX;
    }
    let grid_len = match w.checked_mul(h) {
        Some(n) => n,
        None => return usize::MAX,
    };
    let (x, y, idx) = if len == 0 {
        (&[][..], &[][..], &mut [][..])
    } else {
        if x.is_null() || y.is_null() || idx.is_null() {
            return usize::MAX;
        }
        (
            std::slice::from_raw_parts(x, len),
            std::slice::from_raw_parts(y, len),
            std::slice::from_raw_parts_mut(idx, len),
        )
    };
    let grid = std::slice::from_raw_parts_mut(grid, grid_len);
    if len == 0 {
        grid.fill(0.0);
        return 0;
    }
    ffi_guard(usize::MAX, || {
        kernels::bin_2d_indices(x, y, x0, x1, y0, y1, w, h, grid, idx)
    })
}

/// Full-domain density first paint: one traversal writes the `bin_2d` grid
/// and samples implicit row ids `0..len` with the same SplitMix predicate as
/// `xy_sample_range_indices`. Returns the exact sample length; rows are copied
/// only when `capacity` is sufficient. `usize::MAX` reports invalid arguments.
///
/// # Safety
/// `x`/`y` must point to `len` readable f64s; `grid` to `w*h` writable f32s.
/// When `capacity > 0`, `out` must point to `capacity` writable u32s.
#[no_mangle]
#[allow(clippy::too_many_arguments)]
pub unsafe extern "C" fn xy_bin_2d_sample_range(
    x: *const f64,
    y: *const f64,
    len: usize,
    x0: f64,
    x1: f64,
    y0: f64,
    y1: f64,
    w: usize,
    h: usize,
    seed: u64,
    threshold: u64,
    grid: *mut f32,
    out: *mut u32,
    capacity: usize,
) -> usize {
    if w == 0
        || h == 0
        || !finite_gt(x0, x1)
        || !finite_gt(y0, y1)
        || grid.is_null()
        || len > u32::MAX as usize
        || capacity > len
        || (capacity > 0 && out.is_null())
    {
        return usize::MAX;
    }
    let grid_len = match w.checked_mul(h) {
        Some(value) => value,
        None => return usize::MAX,
    };
    let (x, y) = if len == 0 {
        (&[][..], &[][..])
    } else {
        if x.is_null() || y.is_null() {
            return usize::MAX;
        }
        (
            std::slice::from_raw_parts(x, len),
            std::slice::from_raw_parts(y, len),
        )
    };
    let grid = std::slice::from_raw_parts_mut(grid, grid_len);
    if len == 0 {
        grid.fill(0.0);
        return 0;
    }
    let out_rows = if capacity == 0 {
        &mut [][..]
    } else {
        std::slice::from_raw_parts_mut(out, capacity)
    };
    ffi_guard(usize::MAX, || {
        kernels::bin_2d_sample_range(x, y, x0, x1, y0, y1, w, h, seed, threshold, grid, out_rows)
    })
}

/// Categorical counterpart to [`xy_bin_2d_sample_range`]. Exact factorization
/// counts define the per-code sampling thresholds and avoid a recount; the
/// resulting grid and sampled rows match the standalone bin and counted
/// stratified sampler. Returns `usize::MAX` on malformed arguments/codes.
///
/// # Safety
/// `x`/`y`/`groups` must point to `len` readable values and `counts` to
/// `n_groups` readable u64 values. Remaining output contracts match
/// [`xy_bin_2d_sample_range`].
#[no_mangle]
#[allow(clippy::too_many_arguments)]
pub unsafe extern "C" fn xy_bin_2d_stratified_sample_range_u8_counted(
    x: *const f64,
    y: *const f64,
    groups: *const u8,
    len: usize,
    counts: *const u64,
    n_groups: usize,
    x0: f64,
    x1: f64,
    y0: f64,
    y1: f64,
    w: usize,
    h: usize,
    seed: u64,
    fraction: f64,
    min_count: u64,
    grid: *mut f32,
    out: *mut u32,
    capacity: usize,
) -> usize {
    if w == 0
        || h == 0
        || !finite_gt(x0, x1)
        || !finite_gt(y0, y1)
        || !fraction.is_finite()
        || fraction <= 0.0
        || grid.is_null()
        || counts.is_null()
        || n_groups == 0
        || n_groups > 256
        || len > u32::MAX as usize
        || capacity > len
        || (capacity > 0 && out.is_null())
    {
        return usize::MAX;
    }
    let grid_len = match w.checked_mul(h) {
        Some(value) => value,
        None => return usize::MAX,
    };
    let counts = std::slice::from_raw_parts(counts, n_groups);
    let (x, y, groups) = if len == 0 {
        (&[][..], &[][..], &[][..])
    } else {
        if x.is_null() || y.is_null() || groups.is_null() {
            return usize::MAX;
        }
        (
            std::slice::from_raw_parts(x, len),
            std::slice::from_raw_parts(y, len),
            std::slice::from_raw_parts(groups, len),
        )
    };
    let grid = std::slice::from_raw_parts_mut(grid, grid_len);
    ffi_guard(usize::MAX, || {
        let Some(selected) = kernels::bin_2d_stratified_sample_range_u8_counted(
            x, y, groups, counts, x0, x1, y0, y1, w, h, seed, fraction, min_count, grid,
        ) else {
            return usize::MAX;
        };
        let written = selected.len();
        if written > 0 && written <= capacity {
            std::ptr::copy_nonoverlapping(selected.as_ptr(), out, written);
        }
        written
    })
}

/// Non-decreasing + NaN-poisoned check (`next >= prev` for every pair; any
/// NaN fails its pairs) — the line/area sorted-ingest predicate (§28).
/// Returns 1 when sorted, 0 when not. Null `data` with `len > 0` returns 0
/// (callers then sort, which is always safe). Empty and single-element
/// inputs are sorted.
///
/// # Safety
/// `data` must point to `len` readable f64s (may be null only when `len == 0`).
#[no_mangle]
pub unsafe extern "C" fn xy_is_sorted(data: *const f64, len: usize) -> i32 {
    if len < 2 {
        return 1;
    }
    if data.is_null() {
        return 0;
    }
    let data = std::slice::from_raw_parts(data, len);
    ffi_guard(0, || i32::from(kernels::is_sorted_f64(data)))
}

/// NaN-skipping min/max (autorange primitive). Returns 1 and writes the result,
/// or 0 if the input is empty / all-NaN.
///
/// # Safety
/// `data` must point to `len` readable f64s; out pointers to one writable f64.
#[no_mangle]
pub unsafe extern "C" fn xy_min_max(
    data: *const f64,
    len: usize,
    out_min: *mut f64,
    out_max: *mut f64,
) -> i32 {
    if len == 0 {
        return 0;
    }
    if data.is_null() || out_min.is_null() || out_max.is_null() {
        return 0;
    }
    let data = std::slice::from_raw_parts(data, len);
    match ffi_guard(None, || kernels::min_max(data)) {
        Some((mn, mx)) => {
            *out_min = mn;
            *out_max = mx;
            1
        }
        None => 0,
    }
}

/// Uniform fixed-bin histogram. Returns the count of finite in-range values, or
/// `usize::MAX` on invalid arguments. `out_counts` must hold `n_bins` f64s.
///
/// # Safety
/// `data` must point to `len` readable f64s; `out_counts` to `n_bins` writable
/// f64s; `n_bins > 0` and `lo`/`hi` finite increasing.
#[no_mangle]
pub unsafe extern "C" fn xy_histogram_uniform(
    data: *const f64,
    len: usize,
    lo: f64,
    hi: f64,
    n_bins: usize,
    density: i32,
    out_counts: *mut f64,
) -> usize {
    let bad = n_bins == 0 || !finite_gt(lo, hi);
    if bad {
        return usize::MAX;
    }
    if out_counts.is_null() {
        return usize::MAX;
    }
    let data = if len == 0 {
        &[][..]
    } else {
        if data.is_null() {
            return usize::MAX;
        }
        std::slice::from_raw_parts(data, len)
    };
    let out = std::slice::from_raw_parts_mut(out_counts, n_bins);
    let total = match ffi_guard(None, || Some(kernels::histogram_uniform(data, lo, hi, out))) {
        Some(t) => t,
        None => return usize::MAX,
    };
    if density != 0 && total > 0 {
        let bin_w = (hi - lo) / n_bins as f64;
        let denom = total as f64 * bin_w;
        for c in out.iter_mut() {
            *c /= denom;
        }
    }
    total as usize
}

/// Normalize f64 values into f32 `[0,1]`. `nan_mode=0` maps non-finite values to
/// 0.0; `nan_mode=1` maps them to f32 NaN. Returns 1 on success (including the
/// empty no-op), 0 on null arguments or a non-finite/inverted domain — the
/// former silent-void failure left the output buffer uninitialized with no way
/// to detect it.
///
/// # Safety
/// `data` must point to `len` readable f64s; `out` to `len` writable f32s.
#[no_mangle]
pub unsafe extern "C" fn xy_normalize_f32(
    data: *const f64,
    len: usize,
    lo: f64,
    hi: f64,
    nan_mode: i32,
    out: *mut f32,
) -> i32 {
    if len == 0 {
        return 1;
    }
    if data.is_null() || out.is_null() || !finite_gt(lo, hi) {
        return 0;
    }
    let data = std::slice::from_raw_parts(data, len);
    let out = std::slice::from_raw_parts_mut(out, len);
    let nan_value = if nan_mode == 1 { f32::NAN } else { 0.0 };
    ffi_guard(0, || {
        kernels::normalize_f32_into(data, lo, hi, nan_value, out);
        1
    })
}

/// Deterministic sampling mask (§5/§17): `out[i] = 1` iff
/// `splitmix64(ids[i] + seed) <= threshold`. Bit-identical to
/// `xy.lod.hash_row_ids` thresholding, fused into one pass.
/// Returns 1 on success (including the empty no-op), 0 on null arguments.
///
/// # Safety
/// `ids` must point to `len` readable u64s; `out` to `len` writable u8s.
#[no_mangle]
pub unsafe extern "C" fn xy_sample_mask(
    ids: *const u64,
    len: usize,
    seed: u64,
    threshold: u64,
    out: *mut u8,
) -> i32 {
    if len == 0 {
        return 1;
    }
    if ids.is_null() || out.is_null() {
        return 0;
    }
    let ids = std::slice::from_raw_parts(ids, len);
    let out = std::slice::from_raw_parts_mut(out, len);
    ffi_guard(0, || {
        kernels::sample_mask(ids, seed, threshold, out);
        1
    })
}

/// `xy_sample_mask` over u32 row indices: each id widens to u64 in-register,
/// bit-identical to widening the full array first without that allocation.
///
/// # Safety
/// `ids` must point to `len` readable u32s; `out` to `len` writable u8s.
#[no_mangle]
pub unsafe extern "C" fn xy_sample_mask_u32(
    ids: *const u32,
    len: usize,
    seed: u64,
    threshold: u64,
    out: *mut u8,
) -> i32 {
    if len == 0 {
        return 1;
    }
    if ids.is_null() || out.is_null() {
        return 0;
    }
    let ids = std::slice::from_raw_parts(ids, len);
    let out = std::slice::from_raw_parts_mut(out, len);
    ffi_guard(0, || {
        kernels::sample_mask(ids, seed, threshold, out);
        1
    })
}

/// Deterministically sample implicit row ids `0..size`. Returns the required
/// output length; values are written only when `capacity` is sufficient.
/// `usize::MAX` reports an invalid size/pointer combination.
///
/// # Safety
/// When `capacity > 0`, `out` must point to `capacity` writable u32 values.
#[no_mangle]
pub unsafe extern "C" fn xy_sample_range_indices(
    size: usize,
    seed: u64,
    threshold: u64,
    out: *mut u32,
    capacity: usize,
) -> usize {
    if size > u32::MAX as usize || (capacity > 0 && out.is_null()) {
        return usize::MAX;
    }
    let output = if capacity == 0 {
        &mut [][..]
    } else {
        std::slice::from_raw_parts_mut(out, capacity)
    };
    ffi_guard(usize::MAX, || {
        kernels::sample_range_indices_into(size, seed, threshold, output)
    })
}

/// Category-stratified sampling for implicit row ids `0..len` with compact
/// u8 group codes. Returns the required output length; ascending row indices
/// are written only when `capacity` is sufficient. `usize::MAX` reports
/// invalid arguments or a group code outside `0..n_groups`.
///
/// # Safety
/// `groups` must point to `len` readable u8 values. When `capacity > 0`,
/// `out` must point to `capacity` writable u32 values.
#[no_mangle]
#[allow(clippy::too_many_arguments)]
pub unsafe extern "C" fn xy_stratified_sample_range_u8(
    groups: *const u8,
    len: usize,
    n_groups: usize,
    seed: u64,
    fraction: f64,
    min_count: u64,
    out: *mut u32,
    capacity: usize,
) -> usize {
    if len > u32::MAX as usize
        || n_groups == 0
        || n_groups > 256
        || !fraction.is_finite()
        || fraction <= 0.0
        || (len > 0 && groups.is_null())
        || (capacity > 0 && out.is_null())
    {
        return usize::MAX;
    }
    let groups = if len == 0 {
        &[][..]
    } else {
        std::slice::from_raw_parts(groups, len)
    };
    ffi_guard(usize::MAX, || {
        let Some(selected) =
            kernels::stratified_sample_range_u8(groups, n_groups, seed, fraction, min_count)
        else {
            return usize::MAX;
        };
        if !selected.is_empty() && selected.len() <= capacity {
            let output = std::slice::from_raw_parts_mut(out, capacity);
            output[..selected.len()].copy_from_slice(&selected);
        }
        selected.len()
    })
}

/// Count-reusing variant of [`xy_stratified_sample_range_u8`]. `counts`
/// contains `n_groups` exact per-code counts from compact factorization,
/// avoiding a source-sized recount before sampling.
///
/// # Safety
/// `counts` must point to `n_groups` readable u64 values. Other spans follow
/// [`xy_stratified_sample_range_u8`].
#[no_mangle]
#[allow(clippy::too_many_arguments)]
pub unsafe extern "C" fn xy_stratified_sample_range_u8_counted(
    groups: *const u8,
    len: usize,
    counts: *const u64,
    n_groups: usize,
    seed: u64,
    fraction: f64,
    min_count: u64,
    out: *mut u32,
    capacity: usize,
) -> usize {
    if len > u32::MAX as usize
        || n_groups == 0
        || n_groups > 256
        || !fraction.is_finite()
        || fraction <= 0.0
        || counts.is_null()
        || (len > 0 && groups.is_null())
        || (capacity > 0 && out.is_null())
    {
        return usize::MAX;
    }
    let groups = if len == 0 {
        &[][..]
    } else {
        std::slice::from_raw_parts(groups, len)
    };
    let counts = std::slice::from_raw_parts(counts, n_groups);
    ffi_guard(usize::MAX, || {
        let Some(selected) =
            kernels::stratified_sample_range_u8_counted(groups, counts, seed, fraction, min_count)
        else {
            return usize::MAX;
        };
        if !selected.is_empty() && selected.len() <= capacity {
            let output = std::slice::from_raw_parts_mut(out, capacity);
            output[..selected.len()].copy_from_slice(&selected);
        }
        selected.len()
    })
}

/// Category-stratified sampling mask (§5/§17): per-category keep fractions
/// scale as `min(1, fraction * sqrt(len / count))` and every category keeps at
/// least `min(min_count, count)` of its lowest-hash rows. Bit-identical to the
/// per-category NumPy reference in `xy.lod` (parity-tested), fused
/// into one pass instead of O(len · n_groups) rescans.
///
/// Returns 1 on success (including the empty no-op), 0 on null arguments, a
/// non-finite or non-positive `fraction`, or a group code `>= n_groups`
/// (output undefined).
///
/// # Safety
/// `ids` must point to `len` readable u64s, `groups` to `len` readable u32s,
/// `out` to `len` writable u8s.
#[no_mangle]
#[allow(clippy::too_many_arguments)]
pub unsafe extern "C" fn xy_stratified_sample_mask(
    ids: *const u64,
    groups: *const u32,
    len: usize,
    n_groups: usize,
    seed: u64,
    fraction: f64,
    min_count: u64,
    out: *mut u8,
) -> i32 {
    if len == 0 {
        return 1;
    }
    if ids.is_null()
        || groups.is_null()
        || out.is_null()
        || n_groups == 0
        || !fraction.is_finite()
        || fraction <= 0.0
    {
        return 0;
    }
    let ids = std::slice::from_raw_parts(ids, len);
    let groups = std::slice::from_raw_parts(groups, len);
    let out = std::slice::from_raw_parts_mut(out, len);
    ffi_guard(0, || {
        kernels::stratified_sample_mask(ids, groups, n_groups, seed, fraction, min_count, out)
            as i32
    })
}

/// `xy_stratified_sample_mask` over u32 row indices, with the same in-register
/// widening contract as [`xy_sample_mask_u32`].
///
/// # Safety
/// `ids` and `groups` must point to `len` readable u32s; `out` to `len`
/// writable u8s.
#[no_mangle]
#[allow(clippy::too_many_arguments)]
pub unsafe extern "C" fn xy_stratified_sample_mask_u32(
    ids: *const u32,
    groups: *const u32,
    len: usize,
    n_groups: usize,
    seed: u64,
    fraction: f64,
    min_count: u64,
    out: *mut u8,
) -> i32 {
    if len == 0 {
        return 1;
    }
    if ids.is_null()
        || groups.is_null()
        || out.is_null()
        || n_groups == 0
        || !fraction.is_finite()
        || fraction <= 0.0
    {
        return 0;
    }
    let ids = std::slice::from_raw_parts(ids, len);
    let groups = std::slice::from_raw_parts(groups, len);
    let out = std::slice::from_raw_parts_mut(out, len);
    ffi_guard(0, || {
        kernels::stratified_sample_mask(ids, groups, n_groups, seed, fraction, min_count, out)
            as i32
    })
}

/// Return the number of rows valid across `n_columns` f64 columns. A set bit
/// in `positive_mask` requires that column to be positive as well as finite.
/// With zero capacity this is an allocation-free parallel query; otherwise
/// ascending u32 row IDs are written when they fit. `usize::MAX` is invalid.
///
/// # Safety
/// `columns` must point to `n_columns` readable pointers, each addressing
/// `len` f64 values (a null data pointer is allowed only when `len == 0`).
/// When `capacity > 0`, `out` must address that many writable u32 values.
#[no_mangle]
pub unsafe extern "C" fn xy_valid_indices_f64(
    columns: *const *const f64,
    n_columns: usize,
    len: usize,
    positive_mask: u64,
    out: *mut u32,
    capacity: usize,
) -> usize {
    if columns.is_null()
        || n_columns == 0
        || n_columns > 64
        || len > u32::MAX as usize
        || capacity > len
        || (capacity > 0 && out.is_null())
        || (n_columns < 64 && positive_mask >> n_columns != 0)
    {
        return usize::MAX;
    }
    let pointers = std::slice::from_raw_parts(columns, n_columns);
    let mut slices = Vec::with_capacity(n_columns);
    for &pointer in pointers {
        if len > 0 && pointer.is_null() {
            return usize::MAX;
        }
        slices.push(if len == 0 {
            &[][..]
        } else {
            std::slice::from_raw_parts(pointer, len)
        });
    }
    ffi_guard(usize::MAX, || {
        if capacity == 0 {
            kernels::valid_row_count_f64(&slices, positive_mask).unwrap_or(usize::MAX)
        } else {
            let output = std::slice::from_raw_parts_mut(out, capacity);
            if capacity == len {
                kernels::valid_row_indices_parallel_f64(&slices, positive_mask, output)
                    .unwrap_or(usize::MAX)
            } else {
                kernels::valid_row_indices_f64(&slices, positive_mask, output).unwrap_or(usize::MAX)
            }
        }
    })
}

/// Canonical row indices inside an inclusive rectangular window. Returns the
/// count written. `out` must hold `len` u32s.
///
/// # Safety
/// `x`/`y` must point to `len` readable f64s; `out` to `len` writable u32s.
#[no_mangle]
pub unsafe extern "C" fn xy_range_indices(
    x: *const f64,
    y: *const f64,
    len: usize,
    lo_x: f64,
    hi_x: f64,
    lo_y: f64,
    hi_y: f64,
    out: *mut u32,
) -> usize {
    if !finite_ordered(lo_x, hi_x) || !finite_ordered(lo_y, hi_y) {
        return usize::MAX;
    }
    // u32 index ceiling — see xy_m4_indices.
    if len > u32::MAX as usize {
        return usize::MAX;
    }
    if len == 0 {
        return 0;
    }
    if x.is_null() || y.is_null() || out.is_null() {
        return usize::MAX;
    }
    let x = std::slice::from_raw_parts(x, len);
    let y = std::slice::from_raw_parts(y, len);
    let out = std::slice::from_raw_parts_mut(out, len);
    ffi_guard(usize::MAX, || {
        kernels::range_indices(x, y, lo_x, hi_x, lo_y, hi_y, out)
    })
}

/// Canonical row ids from `rows` that fall inside the rectangular window —
/// the row-restricted twin of `xy_range_indices`, shaped like
/// `xy_polygon_select`. Returns the count written; `out` must hold `n_rows`
/// u32s. Row ids must be < `len`; an out-of-range id returns the error
/// sentinel on every target, including panic-abort ones where the kernel's own
/// indexing panic could not (see `kernels::range_scan_rows`).
///
/// # Safety
/// `x`/`y` must point to `len` readable f64s, `rows` to `n_rows` readable
/// u32s, and `out` to `n_rows` writable u32s.
#[no_mangle]
#[allow(clippy::too_many_arguments)]
pub unsafe extern "C" fn xy_range_indices_rows(
    x: *const f64,
    y: *const f64,
    len: usize,
    rows: *const u32,
    n_rows: usize,
    lo_x: f64,
    hi_x: f64,
    lo_y: f64,
    hi_y: f64,
    out: *mut u32,
) -> usize {
    if !finite_ordered(lo_x, hi_x) || !finite_ordered(lo_y, hi_y) {
        return usize::MAX;
    }
    // u32 index ceiling — see xy_m4_indices.
    if len > u32::MAX as usize {
        return usize::MAX;
    }
    if n_rows == 0 {
        return 0;
    }
    if x.is_null() || y.is_null() || rows.is_null() || out.is_null() {
        return usize::MAX;
    }
    let x = std::slice::from_raw_parts(x, len);
    let y = std::slice::from_raw_parts(y, len);
    let rows = std::slice::from_raw_parts(rows, n_rows);
    let out = std::slice::from_raw_parts_mut(out, n_rows);
    ffi_guard(usize::MAX, || {
        kernels::range_indices_rows(x, y, rows, lo_x, hi_x, lo_y, hi_y, out).unwrap_or(usize::MAX)
    })
}

/// Canonical row ids from `rows` that fall inside the lasso polygon, by
/// even-odd ray casting. Returns the count written; `out` must hold
/// `n_rows` u32s. A polygon of fewer than 3 vertices selects nothing. Row ids
/// must be < `len`; an out-of-range id returns the error sentinel on every
/// target (see `kernels::range_scan_rows`).
///
/// # Safety
/// `x`/`y` must point to `len` readable f64s, `rows` to `n_rows` readable
/// u32s, `poly_x`/`poly_y` to `n_poly` readable f64s, and `out` to `n_rows`
/// writable u32s.
#[no_mangle]
#[allow(clippy::too_many_arguments)]
pub unsafe extern "C" fn xy_polygon_select(
    x: *const f64,
    y: *const f64,
    len: usize,
    rows: *const u32,
    n_rows: usize,
    poly_x: *const f64,
    poly_y: *const f64,
    n_poly: usize,
    out: *mut u32,
) -> usize {
    // u32 index ceiling — see xy_m4_indices.
    if len > u32::MAX as usize {
        return usize::MAX;
    }
    if n_rows == 0 {
        return 0;
    }
    // Fewer than three vertices encloses nothing. Answer before building any
    // slice: `from_raw_parts` requires a non-null, aligned pointer even at
    // length zero, so a caller passing null for an empty polygon must not
    // reach the constructions below.
    if n_poly < 3 {
        return 0;
    }
    if x.is_null() || y.is_null() || rows.is_null() || out.is_null() {
        return usize::MAX;
    }
    if poly_x.is_null() || poly_y.is_null() {
        return usize::MAX;
    }
    let x = std::slice::from_raw_parts(x, len);
    let y = std::slice::from_raw_parts(y, len);
    let rows = std::slice::from_raw_parts(rows, n_rows);
    let poly_x = std::slice::from_raw_parts(poly_x, n_poly);
    let poly_y = std::slice::from_raw_parts(poly_y, n_poly);
    let out = std::slice::from_raw_parts_mut(out, n_rows);
    ffi_guard(usize::MAX, || {
        kernels::polygon_select(x, y, rows, poly_x, poly_y, out).unwrap_or(usize::MAX)
    })
}

/// Per-point local log density for a subset. Returns 1 on success, 0 on invalid
/// grid/window arguments.
///
/// # Safety
/// `x`/`y` must point to `len` readable f64s; `out` to `len` writable f32s.
#[no_mangle]
#[allow(clippy::too_many_arguments)]
pub unsafe extern "C" fn xy_local_log_density(
    x: *const f64,
    y: *const f64,
    len: usize,
    lo_x: f64,
    hi_x: f64,
    lo_y: f64,
    hi_y: f64,
    w: usize,
    h: usize,
    out: *mut f32,
) -> i32 {
    let bad = w == 0 || h == 0 || !finite_gt(lo_x, hi_x) || !finite_gt(lo_y, hi_y);
    if bad {
        return 0;
    }
    if len == 0 {
        return 1;
    }
    if x.is_null() || y.is_null() || out.is_null() {
        return 0;
    }
    let x = std::slice::from_raw_parts(x, len);
    let y = std::slice::from_raw_parts(y, len);
    let out = std::slice::from_raw_parts_mut(out, len);
    ffi_guard(0, || {
        kernels::local_log_density(x, y, lo_x, hi_x, lo_y, hi_y, w, h, out);
        1
    })
}

// -- tile pyramid (§5 Tier 3): opaque u64 handles, engine doc §3.3 ------------

/// Build a count pyramid over the given bounds. Returns a nonzero handle, or
/// 0 on invalid arguments. The handle must be released with xy_pyramid_free.
/// # Safety
/// `x`/`y` must point to `len` readable f64s.
#[no_mangle]
pub unsafe extern "C" fn xy_pyramid_build(
    x: *const f64,
    y: *const f64,
    len: usize,
    x0: f64,
    x1: f64,
    y0: f64,
    y1: f64,
    base_dim: u32,
) -> u64 {
    if x.is_null() || y.is_null() || len == 0 {
        return 0;
    }
    let x = std::slice::from_raw_parts(x, len);
    let y = std::slice::from_raw_parts(y, len);
    ffi_guard(0, || {
        match tiles::build(x, y, x0, x1, y0, y1, base_dim as usize) {
            Some(p) => tiles::reg_insert(p),
            None => 0,
        }
    })
}

/// Build a pyramid with mean-color planes (LOD doc §2/§4.1) for a
/// channel-bearing trace. Same handle registry and geometry as
/// `xy_pyramid_build`; color source as in `xy_bin_2d_mean_color`. Returns the
/// handle, or 0 on invalid arguments.
///
/// # Safety
/// `x`/`y` must point to `len` readable f64s; color source pointers must
/// satisfy `color_source_from_raw`.
#[no_mangle]
#[allow(clippy::too_many_arguments)]
pub unsafe extern "C" fn xy_pyramid_build_color(
    x: *const f64,
    y: *const f64,
    len: usize,
    idx: *const u8,
    rgba: *const u8,
    lut: *const u8,
    lut_len: usize,
    x0: f64,
    x1: f64,
    y0: f64,
    y1: f64,
    base_dim: u32,
) -> u64 {
    if x.is_null() || y.is_null() || len == 0 {
        return 0;
    }
    let Some(colors) = color_source_from_raw(len, idx, rgba, lut, lut_len) else {
        return 0;
    };
    let x = std::slice::from_raw_parts(x, len);
    let y = std::slice::from_raw_parts(y, len);
    ffi_guard(0, || {
        match tiles::build_color(x, y, &colors, x0, x1, y0, y1, base_dim as usize) {
            Some(p) => tiles::reg_insert(p),
            None => 0,
        }
    })
}

/// Increment a live pyramid from an appended point batch. Returns 1 when the
/// update was applied, or 0 for a stale/busy handle, invalid pointers/lengths,
/// or a finite point outside the pyramid's original domain. A rejected update
/// never partially mutates the pyramid.
///
/// # Safety
/// `x`/`y` must point to `len` readable f64s (or may be null when `len == 0`).
#[no_mangle]
pub unsafe extern "C" fn xy_pyramid_append(
    handle: u64,
    x: *const f64,
    y: *const f64,
    len: usize,
) -> i32 {
    if len > 0 && (x.is_null() || y.is_null()) {
        return 0;
    }
    let x = if len == 0 {
        &[][..]
    } else {
        std::slice::from_raw_parts(x, len)
    };
    let y = if len == 0 {
        &[][..]
    } else {
        std::slice::from_raw_parts(y, len)
    };
    ffi_guard(0, || {
        tiles::reg_append(handle, x, y).unwrap_or(false) as i32
    })
}

/// Approximate in-window count from the finest level. 1 on success, 0 on a
/// stale/invalid handle or bad arguments.
/// # Safety
/// `out_count` must point to a writable f64.
#[no_mangle]
pub unsafe extern "C" fn xy_pyramid_count(
    handle: u64,
    lo_x: f64,
    hi_x: f64,
    lo_y: f64,
    hi_y: f64,
    out_count: *mut f64,
) -> i32 {
    if out_count.is_null() || !finite_gt(lo_x, hi_x) || !finite_gt(lo_y, hi_y) {
        return 0;
    }
    ffi_guard(0, || {
        match tiles::reg_with(handle, |p| tiles::count(p, lo_x, hi_x, lo_y, hi_y)) {
            Some(c) => {
                *out_count = c;
                1
            }
            None => 0,
        }
    })
}

/// Compose the window into a w×h grid. Returns the level used (>= 0),
/// -1 on stale handle/bad args, -2 when the window outresolves the pyramid
/// (caller must fall back to an exact re-bin and disclose it, §28).
/// # Safety
/// `out` must point to `w * h` writable f32s.
#[no_mangle]
pub unsafe extern "C" fn xy_pyramid_compose(
    handle: u64,
    lo_x: f64,
    hi_x: f64,
    lo_y: f64,
    hi_y: f64,
    w: usize,
    h: usize,
    max_upsample: usize,
    out: *mut f32,
) -> i32 {
    if out.is_null() || w == 0 || h == 0 || !finite_gt(lo_x, hi_x) || !finite_gt(lo_y, hi_y) {
        return -1;
    }
    let out_len = match w.checked_mul(h) {
        Some(n) => n,
        None => return -1,
    };
    let out = std::slice::from_raw_parts_mut(out, out_len);
    let max_upsample = max_upsample.max(1);
    ffi_guard(-1, || {
        match tiles::reg_with(handle, |p| {
            tiles::compose(p, lo_x, hi_x, lo_y, hi_y, w, h, max_upsample, out)
        }) {
            Some(Some(level)) => level as i32,
            Some(None) => -2,
            None => -1,
        }
    })
}

/// `xy_pyramid_compose` plus the mean-color plane: fills `out` with the same
/// f32 counts (bit-identical) and `out_rgba` (`w*h*4`, straight-alpha RGBA8)
/// with the composed mean colors. Returns the level used (>= 0), -1 for bad
/// arguments/stale handle, or -2 when the window outresolves the pyramid OR
/// the pyramid carries no color planes — the caller re-bins exactly either way.
///
/// # Safety
/// `out` must address `w*h` writable f32s and `out_rgba` `w*h*4` writable bytes.
#[no_mangle]
#[allow(clippy::too_many_arguments)]
pub unsafe extern "C" fn xy_pyramid_compose_color(
    handle: u64,
    lo_x: f64,
    hi_x: f64,
    lo_y: f64,
    hi_y: f64,
    w: usize,
    h: usize,
    max_upsample: usize,
    out: *mut f32,
    out_rgba: *mut u8,
) -> i32 {
    if out.is_null()
        || out_rgba.is_null()
        || w == 0
        || h == 0
        || !finite_gt(lo_x, hi_x)
        || !finite_gt(lo_y, hi_y)
    {
        return -1;
    }
    let Some(out_len) = w.checked_mul(h) else {
        return -1;
    };
    // Zero forgives a caller that forgot the knob; the count-only entry point
    // applies the same floor.
    let max_upsample = max_upsample.max(1);
    let out = std::slice::from_raw_parts_mut(out, out_len);
    let out_rgba = std::slice::from_raw_parts_mut(out_rgba, out_len * 4);
    ffi_guard(-1, || {
        match tiles::reg_with(handle, |p| {
            tiles::compose_color(p, lo_x, hi_x, lo_y, hi_y, w, h, max_upsample, out, out_rgba)
        }) {
            Some(Some(level)) => level as i32,
            Some(None) => -2,
            None => -1,
        }
    })
}

/// Release a pyramid. 1 if it existed, 0 for stale/unknown handles.
/// # Safety
/// No pointer arguments; safe for any handle value.
#[no_mangle]
pub unsafe extern "C" fn xy_pyramid_free(handle: u64) -> i32 {
    ffi_guard(0, || if tiles::reg_remove(handle) { 1 } else { 0 })
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    #[cfg(panic = "unwind")]
    fn ffi_guard_maps_panic_to_sentinel() {
        // A panic anywhere behind the C ABI must become the entry point's
        // error sentinel, never an unwind across `extern "C"` (which would
        // abort the embedding interpreter).
        let hook = std::panic::take_hook();
        std::panic::set_hook(Box::new(|_| {})); // silence the expected panic
        let got = ffi_guard(usize::MAX, || panic!("deliberate test panic"));
        std::panic::set_hook(hook);
        assert_eq!(got, usize::MAX);
        assert_eq!(ffi_guard(0i32, || 1i32), 1);
    }

    #[test]
    fn transition_key_ffi_reports_duplicate_rows_and_invalid_data() {
        let values = [7i16, -2, 7];
        let mut low = [0u32; 3];
        let mut high = [0u32; 3];
        let mut first = usize::MAX;
        let mut index = usize::MAX;
        unsafe {
            assert_eq!(
                xy_transition_keys_fixed(
                    values.as_ptr().cast(),
                    values.len(),
                    std::mem::size_of::<i16>(),
                    transition::KIND_SIGNED,
                    0,
                    low.as_mut_ptr(),
                    high.as_mut_ptr(),
                    &mut first,
                    &mut index,
                ),
                2
            );
        }
        assert_eq!((first, index), (0, 2));

        let nonfinite = f64::INFINITY;
        first = usize::MAX;
        index = usize::MAX;
        unsafe {
            assert_eq!(
                xy_transition_keys_fixed(
                    (&nonfinite as *const f64).cast(),
                    1,
                    std::mem::size_of::<f64>(),
                    transition::KIND_FLOAT64,
                    0,
                    low.as_mut_ptr(),
                    high.as_mut_ptr(),
                    &mut first,
                    &mut index,
                ),
                1
            );
        }
        assert_eq!((first, index), (0, 0));
    }

    #[test]
    fn transition_key_ffi_accepts_empty_null_spans_and_rejects_bad_pointers() {
        unsafe {
            assert_eq!(
                xy_transition_keys_fixed(
                    std::ptr::null(),
                    0,
                    0,
                    transition::KIND_BYTES,
                    0,
                    std::ptr::null_mut(),
                    std::ptr::null_mut(),
                    std::ptr::null_mut(),
                    std::ptr::null_mut(),
                ),
                0
            );
            assert_eq!(
                xy_transition_keys_fixed(
                    std::ptr::null(),
                    1,
                    1,
                    transition::KIND_BYTES,
                    0,
                    std::ptr::null_mut(),
                    std::ptr::null_mut(),
                    std::ptr::null_mut(),
                    std::ptr::null_mut(),
                ),
                4
            );
            // A layout the caller should never send is status 4, not the
            // status-1 "declined this data" that means "use the oracle".
            let row = [0u8; 3];
            let mut low = [0u32];
            let mut high = [0u32];
            let mut first = usize::MAX;
            let mut index = usize::MAX;
            assert_eq!(
                xy_transition_keys_fixed(
                    row.as_ptr(),
                    1,
                    3,
                    transition::KIND_UNICODE,
                    0,
                    low.as_mut_ptr(),
                    high.as_mut_ptr(),
                    &mut first,
                    &mut index,
                ),
                4
            );
            assert_eq!((first, index), (usize::MAX, usize::MAX));
            assert_eq!(
                xy_transition_keys_fixed(
                    row.as_ptr(),
                    1,
                    1,
                    transition::KIND_BYTES,
                    7,
                    low.as_mut_ptr(),
                    high.as_mut_ptr(),
                    &mut first,
                    &mut index,
                ),
                4
            );
        }
    }

    #[test]
    #[cfg(target_pointer_width = "64")]
    fn index_emitting_entry_points_reject_u32_overflowing_len() {
        // Emitted row indices are u32: a column longer than u32::MAX would
        // wrap into valid-looking wrong rows. The guards fire before any
        // pointer is dereferenced, so tiny arrays with an absurd `len` are
        // safe to pass here.
        let x = [0.0f64, 1.0];
        let y = [0.0f64, 1.0];
        let too_long = u32::MAX as usize + 1;
        let groups = [0u8, 0];
        let counts = [2u64];
        let validity_columns = [x.as_ptr(), y.as_ptr()];
        let mut idx = [0u32; 8];
        let mut grid = [0f32; 4];
        unsafe {
            assert_eq!(
                xy_m4_indices(
                    x.as_ptr(),
                    y.as_ptr(),
                    too_long,
                    0.0,
                    1.0,
                    2,
                    idx.as_mut_ptr()
                ),
                usize::MAX
            );
            assert_eq!(
                xy_range_indices(
                    x.as_ptr(),
                    y.as_ptr(),
                    too_long,
                    0.0,
                    1.0,
                    0.0,
                    1.0,
                    idx.as_mut_ptr()
                ),
                usize::MAX
            );
            assert_eq!(
                xy_valid_indices_f64(
                    validity_columns.as_ptr(),
                    validity_columns.len(),
                    too_long,
                    0,
                    idx.as_mut_ptr(),
                    idx.len(),
                ),
                usize::MAX
            );
            assert_eq!(
                xy_bin_2d_indices(
                    x.as_ptr(),
                    y.as_ptr(),
                    too_long,
                    0.0,
                    1.0,
                    0.0,
                    1.0,
                    2,
                    2,
                    grid.as_mut_ptr(),
                    idx.as_mut_ptr()
                ),
                usize::MAX
            );
            assert_eq!(
                xy_bin_2d_sample_range(
                    x.as_ptr(),
                    y.as_ptr(),
                    too_long,
                    0.0,
                    1.0,
                    0.0,
                    1.0,
                    2,
                    2,
                    0,
                    0,
                    grid.as_mut_ptr(),
                    idx.as_mut_ptr(),
                    idx.len(),
                ),
                usize::MAX
            );
            assert_eq!(
                xy_bin_2d_stratified_sample_range_u8_counted(
                    x.as_ptr(),
                    y.as_ptr(),
                    groups.as_ptr(),
                    too_long,
                    counts.as_ptr(),
                    1,
                    0.0,
                    1.0,
                    0.0,
                    1.0,
                    2,
                    2,
                    0,
                    0.5,
                    1,
                    grid.as_mut_ptr(),
                    idx.as_mut_ptr(),
                    idx.len(),
                ),
                usize::MAX
            );
        }
    }

    #[test]
    #[cfg(target_pointer_width = "64")]
    fn grid_size_products_that_overflow_are_rejected() {
        // In release builds an unchecked `w * h` wraps silently and the slice
        // built from it is shorter than the kernel-side expectation; every
        // grid entry point must refuse instead (xy_rasterize already did).
        let x = [0.5f64];
        let y = [0.5f64];
        let groups = [0u8];
        let counts = [1u64];
        let huge = usize::MAX / 2 + 1; // huge * 2 wraps to 0
        let mut grid = [0f32; 4];
        let mut idx = [0u32; 4];
        unsafe {
            assert_eq!(
                xy_bin_2d(
                    x.as_ptr(),
                    y.as_ptr(),
                    1,
                    0.0,
                    1.0,
                    0.0,
                    1.0,
                    huge,
                    2,
                    grid.as_mut_ptr()
                ),
                0
            );
            assert_eq!(
                xy_bin_2d_indices(
                    x.as_ptr(),
                    y.as_ptr(),
                    1,
                    0.0,
                    1.0,
                    0.0,
                    1.0,
                    huge,
                    2,
                    grid.as_mut_ptr(),
                    idx.as_mut_ptr()
                ),
                usize::MAX
            );
            assert_eq!(
                xy_bin_2d_sample_range(
                    x.as_ptr(),
                    y.as_ptr(),
                    1,
                    0.0,
                    1.0,
                    0.0,
                    1.0,
                    huge,
                    2,
                    0,
                    0,
                    grid.as_mut_ptr(),
                    idx.as_mut_ptr(),
                    1,
                ),
                usize::MAX
            );
            assert_eq!(
                xy_bin_2d_stratified_sample_range_u8_counted(
                    x.as_ptr(),
                    y.as_ptr(),
                    groups.as_ptr(),
                    1,
                    counts.as_ptr(),
                    1,
                    0.0,
                    1.0,
                    0.0,
                    1.0,
                    huge,
                    2,
                    0,
                    0.5,
                    1,
                    grid.as_mut_ptr(),
                    idx.as_mut_ptr(),
                    1,
                ),
                usize::MAX
            );
            assert_eq!(
                xy_m4_indices(
                    x.as_ptr(),
                    y.as_ptr(),
                    1,
                    0.0,
                    1.0,
                    usize::MAX / 4 + 1, // n_buckets * 4 wraps
                    idx.as_mut_ptr()
                ),
                usize::MAX
            );
            assert_eq!(
                xy_pyramid_compose(0, 0.0, 1.0, 0.0, 1.0, huge, 2, 2, grid.as_mut_ptr()),
                -1
            );
        }
    }
}
