#include "EXTERN.h"
#include "perl.h"
#include "XSUB.h"

/* ============================================
   Custom op structures
   ============================================ */

static XOP identity_xop;
static XOP always_xop;
static XOP clamp_xop;
static XOP nvl_xop;
static XOP coalesce_xop;

/* Type predicate custom ops - blazing fast, single SV flag check */
static XOP is_ref_xop;
static XOP is_array_xop;
static XOP is_hash_xop;
static XOP is_code_xop;
static XOP is_defined_xop;

/* String predicate custom ops - direct SvPV/SvCUR access */
static XOP is_empty_xop;
static XOP starts_with_xop;
static XOP ends_with_xop;

/* ============================================
   Memoization structures
   ============================================ */

typedef struct {
    SV *func;           /* Original coderef */
    HV *cache;          /* Result cache */
    IV hits;            /* Cache hits (stats) */
    IV misses;          /* Cache misses (stats) */
} MemoizedFunc;

static MemoizedFunc *g_memos = NULL;
static IV g_memo_size = 0;
static IV g_memo_count = 0;

/* ============================================
   Lazy evaluation structures
   ============================================ */

typedef struct {
    SV *thunk;          /* Deferred computation (coderef) */
    SV *value;          /* Cached result */
    bool forced;        /* Has been evaluated? */
} LazyValue;

static LazyValue *g_lazies = NULL;
static IV g_lazy_size = 0;
static IV g_lazy_count = 0;

/* ============================================
   Always (constant) structures
   ============================================ */

static SV **g_always_values = NULL;
static IV g_always_size = 0;
static IV g_always_count = 0;

/* ============================================
   Once (execute once) structures
   ============================================ */

typedef struct {
    SV *func;           /* Original function */
    SV *result;         /* Cached result */
    bool called;        /* Has been called? */
} OnceFunc;

static OnceFunc *g_onces = NULL;
static IV g_once_size = 0;
static IV g_once_count = 0;

/* ============================================
   Partial application structures
   ============================================ */

typedef struct {
    SV *func;           /* Original function */
    AV *bound_args;     /* Pre-bound arguments */
} PartialFunc;

static PartialFunc *g_partials = NULL;
static IV g_partial_size = 0;
static IV g_partial_count = 0;

/* ============================================
   Forward declarations
   ============================================ */

static XS(xs_memo_call);
static XS(xs_compose_call);
static XS(xs_always_call);
static XS(xs_negate_call);
static XS(xs_once_call);
static XS(xs_partial_call);

/* ============================================
   Utility functions
   ============================================ */

static void ensure_memo_capacity(IV needed) {
    if (needed >= g_memo_size) {
        IV new_size = g_memo_size ? g_memo_size * 2 : 16;
        while (new_size <= needed) new_size *= 2;
        Renew(g_memos, new_size, MemoizedFunc);
        g_memo_size = new_size;
    }
}

static void ensure_lazy_capacity(IV needed) {
    if (needed >= g_lazy_size) {
        IV new_size = g_lazy_size ? g_lazy_size * 2 : 16;
        while (new_size <= needed) new_size *= 2;
        Renew(g_lazies, new_size, LazyValue);
        g_lazy_size = new_size;
    }
}

static void ensure_always_capacity(IV needed) {
    if (needed >= g_always_size) {
        IV new_size = g_always_size ? g_always_size * 2 : 16;
        while (new_size <= needed) new_size *= 2;
        Renew(g_always_values, new_size, SV*);
        g_always_size = new_size;
    }
}

static void ensure_once_capacity(IV needed) {
    if (needed >= g_once_size) {
        IV new_size = g_once_size ? g_once_size * 2 : 16;
        while (new_size <= needed) new_size *= 2;
        Renew(g_onces, new_size, OnceFunc);
        g_once_size = new_size;
    }
}

static void ensure_partial_capacity(IV needed) {
    if (needed >= g_partial_size) {
        IV new_size = g_partial_size ? g_partial_size * 2 : 16;
        while (new_size <= needed) new_size *= 2;
        Renew(g_partials, new_size, PartialFunc);
        g_partial_size = new_size;
    }
}

/* Build cache key from stack arguments */
static SV* build_cache_key(pTHX_ SV **args, IV count) {
    SV *key = newSVpvs("");
    IV i;
    for (i = 0; i < count; i++) {
        if (i > 0) sv_catpvs(key, "\x00");
        if (SvOK(args[i])) {
            STRLEN len;
            const char *pv = SvPV(args[i], len);
            sv_catpvn(key, pv, len);
        } else {
            sv_catpvs(key, "\x01UNDEF\x01");
        }
    }
    return key;
}

/* ============================================
   Custom OP implementations - fastest path
   ============================================ */

/* identity: just return the top of stack */
static OP* pp_identity(pTHX) {
    /* Value already on stack, nothing to do */
    return NORMAL;
}

/* always: push stored value from op_targ index */
static OP* pp_always(pTHX) {
    dSP;
    IV idx = PL_op->op_targ;
    XPUSHs(g_always_values[idx]);
    RETURN;
}

/* clamp: 3 values on stack, return clamped */
static OP* pp_clamp(pTHX) {
    dSP;
    SV *max_sv = POPs;
    SV *min_sv = POPs;
    SV *val_sv = TOPs;  /* Peek, will replace */

    NV value = SvNV(val_sv);
    NV min = SvNV(min_sv);
    NV max = SvNV(max_sv);

    if (value < min) {
        SETs(min_sv);
    } else if (value > max) {
        SETs(max_sv);
    }
    /* else TOPs already has value */

    RETURN;
}

/* nvl: 2 values on stack, return first if defined */
static OP* pp_nvl(pTHX) {
    dSP;
    SV *def_sv = POPs;
    SV *val_sv = TOPs;

    if (!SvOK(val_sv)) {
        SETs(def_sv);
    }
    RETURN;
}

/* ============================================
   Type predicate custom ops - blazing fast!
   These are the fastest possible type checks:
   single SV flag check, no function call overhead
   ============================================ */

/* is_ref: check if value is a reference */
static OP* pp_is_ref(pTHX) {
    dSP;
    SV *sv = TOPs;
    SETs(SvROK(sv) ? &PL_sv_yes : &PL_sv_no);
    RETURN;
}

/* is_array: check if value is an arrayref */
static OP* pp_is_array(pTHX) {
    dSP;
    SV *sv = TOPs;
    SETs((SvROK(sv) && SvTYPE(SvRV(sv)) == SVt_PVAV) ? &PL_sv_yes : &PL_sv_no);
    RETURN;
}

/* is_hash: check if value is a hashref */
static OP* pp_is_hash(pTHX) {
    dSP;
    SV *sv = TOPs;
    SETs((SvROK(sv) && SvTYPE(SvRV(sv)) == SVt_PVHV) ? &PL_sv_yes : &PL_sv_no);
    RETURN;
}

/* is_code: check if value is a coderef */
static OP* pp_is_code(pTHX) {
    dSP;
    SV *sv = TOPs;
    SETs((SvROK(sv) && SvTYPE(SvRV(sv)) == SVt_PVCV) ? &PL_sv_yes : &PL_sv_no);
    RETURN;
}

/* is_defined: check if value is defined */
static OP* pp_is_defined(pTHX) {
    dSP;
    SV *sv = TOPs;
    SETs(SvOK(sv) ? &PL_sv_yes : &PL_sv_no);
    RETURN;
}

/* ============================================
   String predicate custom ops - blazing fast!
   Direct SvPV/SvCUR access, no function overhead
   ============================================ */

/* is_empty: check if string is undefined or empty */
static OP* pp_is_empty(pTHX) {
    dSP;
    SV *sv = TOPs;
    /* Empty if: undefined OR length is 0 */
    if (!SvOK(sv)) {
        SETs(&PL_sv_yes);
    } else {
        STRLEN len;
        SvPV(sv, len);
        SETs(len == 0 ? &PL_sv_yes : &PL_sv_no);
    }
    RETURN;
}

/* starts_with: check if string starts with prefix */
static OP* pp_starts_with(pTHX) {
    dSP;
    SV *prefix_sv = POPs;
    SV *str_sv = TOPs;

    if (!SvOK(str_sv) || !SvOK(prefix_sv)) {
        SETs(&PL_sv_no);
        RETURN;
    }

    STRLEN str_len, prefix_len;
    const char *str = SvPV(str_sv, str_len);
    const char *prefix = SvPV(prefix_sv, prefix_len);

    if (prefix_len > str_len) {
        SETs(&PL_sv_no);
    } else if (prefix_len == 0) {
        SETs(&PL_sv_yes);  /* Empty prefix always matches */
    } else {
        SETs(memcmp(str, prefix, prefix_len) == 0 ? &PL_sv_yes : &PL_sv_no);
    }
    RETURN;
}

/* ends_with: check if string ends with suffix */
static OP* pp_ends_with(pTHX) {
    dSP;
    SV *suffix_sv = POPs;
    SV *str_sv = TOPs;

    if (!SvOK(str_sv) || !SvOK(suffix_sv)) {
        SETs(&PL_sv_no);
        RETURN;
    }

    STRLEN str_len, suffix_len;
    const char *str = SvPV(str_sv, str_len);
    const char *suffix = SvPV(suffix_sv, suffix_len);

    if (suffix_len > str_len) {
        SETs(&PL_sv_no);
    } else if (suffix_len == 0) {
        SETs(&PL_sv_yes);  /* Empty suffix always matches */
    } else {
        const char *str_end = str + str_len - suffix_len;
        SETs(memcmp(str_end, suffix, suffix_len) == 0 ? &PL_sv_yes : &PL_sv_no);
    }
    RETURN;
}



/* ============================================
   Call checkers - replace function calls with custom ops
   ============================================ */

/* identity call checker - replaces identity($x) with just $x */
static OP* identity_call_checker(pTHX_ OP *entersubop, GV *namegv, SV *ckobj) {
    OP *pushop, *argop, *cvop;

    PERL_UNUSED_ARG(namegv);
    PERL_UNUSED_ARG(ckobj);

    /* Get the argument list */
    pushop = cUNOPx(entersubop)->op_first;
    if (!OpHAS_SIBLING(pushop)) {
        pushop = cUNOPx(pushop)->op_first;
    }

    /* Find first real arg (skip pushmark) */
    argop = OpSIBLING(pushop);

    /* Find the cv op (last sibling) */
    cvop = argop;
    while (OpHAS_SIBLING(cvop)) {
        cvop = OpSIBLING(cvop);
    }

    /* Check for exactly one argument */
    if (argop != cvop && OpSIBLING(argop) == cvop) {
        /* Single arg - just return the arg itself */
        OP *arg = argop;

        /* Detach arg from list */
        OpMORESIB_set(pushop, cvop);
        OpLASTSIB_set(arg, NULL);

        op_free(entersubop);
        return arg;  /* Just return the argument op directly! */
    }

    /* Fall through to XS for edge cases */
    return entersubop;
}

/* clamp call checker - replaces clamp($v, $min, $max) with custom op */
static OP* clamp_call_checker(pTHX_ OP *entersubop, GV *namegv, SV *ckobj) {
    OP *pushop, *arg1, *arg2, *arg3, *cvop;

    PERL_UNUSED_ARG(namegv);
    PERL_UNUSED_ARG(ckobj);

    /* Get the argument list */
    pushop = cUNOPx(entersubop)->op_first;
    if (!OpHAS_SIBLING(pushop)) {
        pushop = cUNOPx(pushop)->op_first;
    }

    /* Find args (skip pushmark) */
    arg1 = OpSIBLING(pushop);  /* value */
    if (!arg1) return entersubop;

    arg2 = OpSIBLING(arg1);    /* min */
    if (!arg2) return entersubop;

    arg3 = OpSIBLING(arg2);    /* max */
    if (!arg3) return entersubop;

    cvop = OpSIBLING(arg3);    /* cv op (should be last) */
    if (!cvop || OpHAS_SIBLING(cvop)) return entersubop;

    /* Build custom op with 3 args on stack */
    OP *newop = newUNOP(OP_NULL, 0, NULL);
    newop->op_type = OP_CUSTOM;
    newop->op_ppaddr = pp_clamp;
    newop->op_flags = OPf_WANT_SCALAR;

    /* Detach args from list */
    OpMORESIB_set(pushop, cvop);
    OpLASTSIB_set(arg3, NULL);
    OpMORESIB_set(arg2, arg3);
    OpMORESIB_set(arg1, arg2);

    /* Build list op to evaluate args */
    OP *listop = op_prepend_elem(OP_LIST, arg1, NULL);
    listop = op_append_elem(OP_LIST, listop, arg2);
    listop = op_append_elem(OP_LIST, listop, arg3);
    listop = op_append_elem(OP_LIST, listop, newop);

    op_free(entersubop);
    return listop;
}

/* Generic call checker for single-arg type predicates */
static OP* type_predicate_call_checker(pTHX_ OP *entersubop, GV *namegv, SV *ckobj, OP* (*pp_func)(pTHX)) {
    OP *pushop, *argop, *cvop;

    PERL_UNUSED_ARG(namegv);
    PERL_UNUSED_ARG(ckobj);

    /* Get the argument list */
    pushop = cUNOPx(entersubop)->op_first;
    if (!OpHAS_SIBLING(pushop)) {
        pushop = cUNOPx(pushop)->op_first;
    }

    /* Find first real arg (skip pushmark) */
    argop = OpSIBLING(pushop);

    /* Find the cv op (last sibling) */
    cvop = argop;
    while (OpHAS_SIBLING(cvop)) {
        cvop = OpSIBLING(cvop);
    }

    /* Check for exactly one argument */
    if (argop != cvop && OpSIBLING(argop) == cvop) {
        OP *arg = argop;

        /* Detach arg from list */
        OpMORESIB_set(pushop, cvop);
        OpLASTSIB_set(arg, NULL);

        /* Create custom op */
        OP *newop = newUNOP(OP_NULL, 0, arg);
        newop->op_type = OP_CUSTOM;
        newop->op_ppaddr = pp_func;
        newop->op_flags = OPf_WANT_SCALAR;

        op_free(entersubop);
        return newop;
    }

    /* Fall through to XS for edge cases */
    return entersubop;
}

/* Individual call checkers for each type predicate */
static OP* is_ref_call_checker(pTHX_ OP *entersubop, GV *namegv, SV *ckobj) {
    return type_predicate_call_checker(aTHX_ entersubop, namegv, ckobj, pp_is_ref);
}

static OP* is_array_call_checker(pTHX_ OP *entersubop, GV *namegv, SV *ckobj) {
    return type_predicate_call_checker(aTHX_ entersubop, namegv, ckobj, pp_is_array);
}

static OP* is_hash_call_checker(pTHX_ OP *entersubop, GV *namegv, SV *ckobj) {
    return type_predicate_call_checker(aTHX_ entersubop, namegv, ckobj, pp_is_hash);
}

static OP* is_code_call_checker(pTHX_ OP *entersubop, GV *namegv, SV *ckobj) {
    return type_predicate_call_checker(aTHX_ entersubop, namegv, ckobj, pp_is_code);
}

static OP* is_defined_call_checker(pTHX_ OP *entersubop, GV *namegv, SV *ckobj) {
    return type_predicate_call_checker(aTHX_ entersubop, namegv, ckobj, pp_is_defined);
}

/* String predicate call checkers */
static OP* is_empty_call_checker(pTHX_ OP *entersubop, GV *namegv, SV *ckobj) {
    return type_predicate_call_checker(aTHX_ entersubop, namegv, ckobj, pp_is_empty);
}

/* Generic two-arg string predicate call checker */
static OP* two_arg_string_call_checker(pTHX_ OP *entersubop, GV *namegv, SV *ckobj, OP* (*pp_func)(pTHX)) {
    OP *pushop, *arg1, *arg2, *cvop;

    PERL_UNUSED_ARG(namegv);
    PERL_UNUSED_ARG(ckobj);

    /* Get the argument list */
    pushop = cUNOPx(entersubop)->op_first;
    if (!OpHAS_SIBLING(pushop)) {
        pushop = cUNOPx(pushop)->op_first;
    }

    /* Find args (skip pushmark) */
    arg1 = OpSIBLING(pushop);  /* string */
    if (!arg1) return entersubop;

    arg2 = OpSIBLING(arg1);    /* prefix/suffix */
    if (!arg2) return entersubop;

    cvop = OpSIBLING(arg2);    /* cv op (should be last) */
    if (!cvop || OpHAS_SIBLING(cvop)) return entersubop;

    /* Build custom op with 2 args on stack */
    OP *newop = newUNOP(OP_NULL, 0, NULL);
    newop->op_type = OP_CUSTOM;
    newop->op_ppaddr = pp_func;
    newop->op_flags = OPf_WANT_SCALAR;

    /* Detach args from list */
    OpMORESIB_set(pushop, cvop);
    OpLASTSIB_set(arg2, NULL);
    OpMORESIB_set(arg1, arg2);

    /* Build list op to evaluate args */
    OP *listop = op_prepend_elem(OP_LIST, arg1, NULL);
    listop = op_append_elem(OP_LIST, listop, arg2);
    listop = op_append_elem(OP_LIST, listop, newop);

    op_free(entersubop);
    return listop;
}

static OP* starts_with_call_checker(pTHX_ OP *entersubop, GV *namegv, SV *ckobj) {
    return two_arg_string_call_checker(aTHX_ entersubop, namegv, ckobj, pp_starts_with);
}

static OP* ends_with_call_checker(pTHX_ OP *entersubop, GV *namegv, SV *ckobj) {
    return two_arg_string_call_checker(aTHX_ entersubop, namegv, ckobj, pp_ends_with);
}

/* ============================================
   Memo implementation
   ============================================ */

static XS(xs_memo) {
    dXSARGS;
    if (items != 1) croak("Usage: util::memo(\\&func)");

    SV *func = ST(0);
    if (!SvROK(func) || SvTYPE(SvRV(func)) != SVt_PVCV) {
        croak("util::memo requires a coderef");
    }

    /* Allocate memo slot */
    IV idx = g_memo_count++;
    ensure_memo_capacity(idx);

    MemoizedFunc *mf = &g_memos[idx];
    mf->func = SvREFCNT_inc_simple_NN(func);
    mf->cache = newHV();
    mf->hits = 0;
    mf->misses = 0;

    /* Create wrapper CV */
    CV *wrapper = newXS(NULL, xs_memo_call, __FILE__);
    CvXSUBANY(wrapper).any_iv = idx;

    ST(0) = newRV_noinc((SV*)wrapper);
    XSRETURN(1);
}

static XS(xs_memo_call) {
    dXSARGS;
    IV idx = CvXSUBANY(cv).any_iv;
    MemoizedFunc *mf = &g_memos[idx];

    /* Build cache key from arguments */
    SV *key = build_cache_key(aTHX_ &ST(0), items);
    STRLEN key_len;
    const char *key_pv = SvPV(key, key_len);

    /* Check cache */
    SV **cached = hv_fetch(mf->cache, key_pv, key_len, 0);
    if (cached && SvOK(*cached)) {
        mf->hits++;
        SvREFCNT_dec_NN(key);
        if (SvROK(*cached) && SvTYPE(SvRV(*cached)) == SVt_PVAV) {
            AV *av = (AV*)SvRV(*cached);
            IV len = av_len(av) + 1;
            IV i;
            EXTEND(SP, len);
            for (i = 0; i < len; i++) {
                SV **elem = av_fetch(av, i, 0);
                ST(i) = elem ? *elem : &PL_sv_undef;
            }
            XSRETURN(len);
        } else {
            ST(0) = *cached;
            XSRETURN(1);
        }
    }

    mf->misses++;

    ENTER;
    SAVETMPS;
    PUSHMARK(SP);

    IV i;
    EXTEND(SP, items);
    for (i = 0; i < items; i++) {
        PUSHs(ST(i));
    }
    PUTBACK;

    IV count = call_sv(mf->func, G_ARRAY);

    SPAGAIN;

    if (count == 1) {
        SV *result = SvREFCNT_inc(POPs);
        hv_store(mf->cache, key_pv, key_len, result, 0);
        PUTBACK;
        FREETMPS;
        LEAVE;
        SvREFCNT_dec_NN(key);
        ST(0) = result;
        XSRETURN(1);
    } else if (count > 0) {
        AV *av = newAV();
        av_extend(av, count - 1);
        for (i = count - 1; i >= 0; i--) {
            av_store(av, i, SvREFCNT_inc(POPs));
        }
        SV *result = newRV_noinc((SV*)av);
        hv_store(mf->cache, key_pv, key_len, result, 0);
        PUTBACK;
        FREETMPS;
        LEAVE;
        SvREFCNT_dec_NN(key);
        for (i = 0; i < count; i++) {
            SV **elem = av_fetch(av, i, 0);
            ST(i) = elem ? *elem : &PL_sv_undef;
        }
        XSRETURN(count);
    } else {
        hv_store(mf->cache, key_pv, key_len, &PL_sv_undef, 0);
        PUTBACK;
        FREETMPS;
        LEAVE;
        SvREFCNT_dec_NN(key);
        XSRETURN_EMPTY;
    }
}

/* ============================================
   Pipe/Compose implementation
   ============================================ */

static XS(xs_pipe) {
    dXSARGS;
    if (items < 2) croak("Usage: util::pipeline($value, \\&fn1, \\&fn2, ...)");

    SV *value = ST(0);
    IV i;

    for (i = 1; i < items; i++) {
        SV *func = ST(i);
        if (!SvROK(func) || SvTYPE(SvRV(func)) != SVt_PVCV) {
            croak("util::pipeline: argument %d is not a coderef", (int)i);
        }

        ENTER;
        SAVETMPS;
        PUSHMARK(SP);
        XPUSHs(value);
        PUTBACK;

        call_sv(func, G_SCALAR);

        SPAGAIN;
        value = POPs;
        SvREFCNT_inc(value);
        PUTBACK;
        FREETMPS;
        LEAVE;
    }

    ST(0) = value;
    XSRETURN(1);
}

static XS(xs_compose) {
    dXSARGS;
    if (items < 1) croak("Usage: util::compose(\\&fn1, \\&fn2, ...)");

    AV *funcs = newAV();
    av_extend(funcs, items - 1);
    IV i;
    for (i = 0; i < items; i++) {
        SV *func = ST(i);
        if (!SvROK(func) || SvTYPE(SvRV(func)) != SVt_PVCV) {
            croak("util::compose: argument %d is not a coderef", (int)(i+1));
        }
        av_store(funcs, i, SvREFCNT_inc_simple_NN(func));
    }

    CV *wrapper = newXS(NULL, xs_compose_call, __FILE__);
    CvXSUBANY(wrapper).any_ptr = (void*)funcs;

    ST(0) = newRV_noinc((SV*)wrapper);
    XSRETURN(1);
}

static XS(xs_compose_call) {
    dXSARGS;
    AV *funcs = (AV*)CvXSUBANY(cv).any_ptr;
    IV func_count = av_len(funcs) + 1;

    SV *value = NULL;

    IV i;
    for (i = func_count - 1; i >= 0; i--) {
        SV **func_ptr = av_fetch(funcs, i, 0);
        if (!func_ptr) continue;

        ENTER;
        SAVETMPS;
        PUSHMARK(SP);

        if (i == func_count - 1) {
            IV j;
            EXTEND(SP, items);
            for (j = 0; j < items; j++) {
                PUSHs(ST(j));
            }
        } else {
            XPUSHs(value);
        }
        PUTBACK;

        call_sv(*func_ptr, G_SCALAR);

        SPAGAIN;
        value = POPs;
        SvREFCNT_inc(value);
        PUTBACK;
        FREETMPS;
        LEAVE;
    }

    ST(0) = value ? value : &PL_sv_undef;
    XSRETURN(1);
}

/* ============================================
   Lazy evaluation implementation
   ============================================ */

static XS(xs_lazy) {
    dXSARGS;
    if (items != 1) croak("Usage: util::lazy(sub { ... })");

    SV *thunk = ST(0);
    if (!SvROK(thunk) || SvTYPE(SvRV(thunk)) != SVt_PVCV) {
        croak("util::lazy requires a coderef");
    }

    IV idx = g_lazy_count++;
    ensure_lazy_capacity(idx);

    LazyValue *lv = &g_lazies[idx];
    lv->thunk = SvREFCNT_inc_simple_NN(thunk);
    lv->value = NULL;
    lv->forced = FALSE;

    SV *obj = newSViv(idx);
    SV *ref = newRV_noinc(obj);
    sv_bless(ref, gv_stashpv("util::Lazy", GV_ADD));

    ST(0) = ref;
    XSRETURN(1);
}

static XS(xs_force) {
    dXSARGS;
    if (items != 1) croak("Usage: util::force($lazy)");

    SV *lazy = ST(0);

    if (!SvROK(lazy) || !sv_derived_from(lazy, "util::Lazy")) {
        ST(0) = lazy;
        XSRETURN(1);
    }

    IV idx = SvIV(SvRV(lazy));
    if (idx < 0 || idx >= g_lazy_count) {
        croak("util::force: invalid lazy value");
    }

    LazyValue *lv = &g_lazies[idx];

    if (lv->forced) {
        ST(0) = lv->value;
        XSRETURN(1);
    }

    ENTER;
    SAVETMPS;
    PUSHMARK(SP);
    PUTBACK;

    call_sv(lv->thunk, G_SCALAR);

    SPAGAIN;
    lv->value = SvREFCNT_inc(POPs);
    lv->forced = TRUE;
    PUTBACK;
    FREETMPS;
    LEAVE;

    SvREFCNT_dec(lv->thunk);
    lv->thunk = NULL;

    ST(0) = lv->value;
    XSRETURN(1);
}

/* ============================================
   Safe navigation (dig) implementation
   ============================================ */

static XS(xs_dig) {
    dXSARGS;
    if (items < 2) croak("Usage: util::dig($hash, @keys)");

    SV *current = ST(0);
    IV i;

    for (i = 1; i < items; i++) {
        if (!SvROK(current) || SvTYPE(SvRV(current)) != SVt_PVHV) {
            XSRETURN_UNDEF;
        }

        HV *hv = (HV*)SvRV(current);
        SV *key = ST(i);
        STRLEN key_len;
        const char *key_pv = SvPV(key, key_len);

        SV **val = hv_fetch(hv, key_pv, key_len, 0);
        if (!val || !SvOK(*val)) {
            XSRETURN_UNDEF;
        }

        current = *val;
    }

    ST(0) = current;
    XSRETURN(1);
}

/* ============================================
   Tap implementation
   ============================================ */

static XS(xs_tap) {
    dXSARGS;
    if (items != 2) croak("Usage: util::tap(\\&block, $value)");

    SV *func = ST(0);
    SV *value = ST(1);

    if (!SvROK(func) || SvTYPE(SvRV(func)) != SVt_PVCV) {
        croak("util::tap: first argument must be a coderef");
    }

    ENTER;
    SAVETMPS;
    SAVE_DEFSV;
    DEFSV_set(value);

    PUSHMARK(SP);
    XPUSHs(value);
    PUTBACK;

    call_sv(func, G_DISCARD | G_VOID);

    SPAGAIN;
    FREETMPS;
    LEAVE;

    ST(0) = value;
    XSRETURN(1);
}

/* ============================================
   Clamp XS fallback
   ============================================ */

static XS(xs_clamp) {
    dXSARGS;
    if (items != 3) croak("Usage: util::clamp($value, $min, $max)");

    NV value = SvNV(ST(0));
    NV min = SvNV(ST(1));
    NV max = SvNV(ST(2));

    if (value < min) {
        ST(0) = ST(1);
    } else if (value > max) {
        ST(0) = ST(2);
    }

    XSRETURN(1);
}

/* ============================================
   Identity XS fallback
   ============================================ */

static XS(xs_identity) {
    dXSARGS;
    if (items != 1) croak("Usage: util::identity($value)");
    XSRETURN(1);
}

/* ============================================
   Always implementation
   ============================================ */

static XS(xs_always) {
    dXSARGS;
    if (items != 1) croak("Usage: util::always($value)");

    IV idx = g_always_count++;
    ensure_always_capacity(idx);

    g_always_values[idx] = SvREFCNT_inc_simple_NN(ST(0));

    CV *wrapper = newXS(NULL, xs_always_call, __FILE__);
    CvXSUBANY(wrapper).any_iv = idx;

    ST(0) = newRV_noinc((SV*)wrapper);
    XSRETURN(1);
}

static XS(xs_always_call) {
    dXSARGS;
    PERL_UNUSED_VAR(items);
    IV idx = CvXSUBANY(cv).any_iv;

    ST(0) = g_always_values[idx];
    XSRETURN(1);
}

/* ============================================
   Functional combinators
   ============================================ */

/* negate(\&pred) - returns a function that returns the opposite */
static XS(xs_negate) {
    dXSARGS;
    if (items != 1) croak("Usage: util::negate(\\&predicate)");

    SV *pred = ST(0);
    if (!SvROK(pred) || SvTYPE(SvRV(pred)) != SVt_PVCV) {
        croak("util::negate: argument must be a coderef");
    }

    CV *wrapper = newXS(NULL, xs_negate_call, __FILE__);
    CvXSUBANY(wrapper).any_ptr = SvREFCNT_inc_simple_NN(pred);

    ST(0) = newRV_noinc((SV*)wrapper);
    XSRETURN(1);
}

static XS(xs_negate_call) {
    dXSARGS;
    SV *pred = (SV*)CvXSUBANY(cv).any_ptr;

    ENTER;
    SAVETMPS;
    PUSHMARK(SP);

    IV i;
    EXTEND(SP, items);
    for (i = 0; i < items; i++) {
        PUSHs(ST(i));
    }
    PUTBACK;

    call_sv(pred, G_SCALAR);

    SPAGAIN;
    SV *result = POPs;
    bool val = SvTRUE(result);
    PUTBACK;
    FREETMPS;
    LEAVE;

    ST(0) = val ? &PL_sv_no : &PL_sv_yes;
    XSRETURN(1);
}

/* once(\&f) - execute once, cache forever */
static XS(xs_once) {
    dXSARGS;
    if (items != 1) croak("Usage: util::once(\\&func)");

    SV *func = ST(0);
    if (!SvROK(func) || SvTYPE(SvRV(func)) != SVt_PVCV) {
        croak("util::once: argument must be a coderef");
    }

    IV idx = g_once_count++;
    ensure_once_capacity(idx);

    OnceFunc *of = &g_onces[idx];
    of->func = SvREFCNT_inc_simple_NN(func);
    of->result = NULL;
    of->called = FALSE;

    CV *wrapper = newXS(NULL, xs_once_call, __FILE__);
    CvXSUBANY(wrapper).any_iv = idx;

    ST(0) = newRV_noinc((SV*)wrapper);
    XSRETURN(1);
}

static XS(xs_once_call) {
    dXSARGS;
    PERL_UNUSED_VAR(items);
    IV idx = CvXSUBANY(cv).any_iv;
    OnceFunc *of = &g_onces[idx];

    if (of->called) {
        ST(0) = of->result ? of->result : &PL_sv_undef;
        XSRETURN(1);
    }

    ENTER;
    SAVETMPS;
    PUSHMARK(SP);
    PUTBACK;

    call_sv(of->func, G_SCALAR);

    SPAGAIN;
    of->result = SvREFCNT_inc(POPs);
    of->called = TRUE;
    PUTBACK;
    FREETMPS;
    LEAVE;

    /* Free the original function, no longer needed */
    SvREFCNT_dec(of->func);
    of->func = NULL;

    ST(0) = of->result;
    XSRETURN(1);
}

/* partial(\&f, @bound) - bind first N args */
static XS(xs_partial) {
    dXSARGS;
    if (items < 1) croak("Usage: util::partial(\\&func, @bound_args)");

    SV *func = ST(0);
    if (!SvROK(func) || SvTYPE(SvRV(func)) != SVt_PVCV) {
        croak("util::partial: first argument must be a coderef");
    }

    IV idx = g_partial_count++;
    ensure_partial_capacity(idx);

    PartialFunc *pf = &g_partials[idx];
    pf->func = SvREFCNT_inc_simple_NN(func);
    pf->bound_args = newAV();

    /* Store bound arguments */
    IV i;
    for (i = 1; i < items; i++) {
        av_push(pf->bound_args, SvREFCNT_inc_simple_NN(ST(i)));
    }

    CV *wrapper = newXS(NULL, xs_partial_call, __FILE__);
    CvXSUBANY(wrapper).any_iv = idx;

    ST(0) = newRV_noinc((SV*)wrapper);
    XSRETURN(1);
}

static XS(xs_partial_call) {
    dXSARGS;
    IV idx = CvXSUBANY(cv).any_iv;
    PartialFunc *pf = &g_partials[idx];

    IV bound_count = av_len(pf->bound_args) + 1;
    IV total = bound_count + items;

    ENTER;
    SAVETMPS;
    PUSHMARK(SP);

    EXTEND(SP, total);

    /* Push bound args first */
    IV i;
    for (i = 0; i < bound_count; i++) {
        SV **elem = av_fetch(pf->bound_args, i, 0);
        PUSHs(elem ? *elem : &PL_sv_undef);
    }

    /* Push call-time args */
    for (i = 0; i < items; i++) {
        PUSHs(ST(i));
    }
    PUTBACK;

    IV count = call_sv(pf->func, G_SCALAR);

    SPAGAIN;
    SV *result = count > 0 ? POPs : &PL_sv_undef;
    SvREFCNT_inc(result);
    PUTBACK;
    FREETMPS;
    LEAVE;

    ST(0) = result;
    XSRETURN(1);
}

/* ============================================
   Data extraction functions
   ============================================ */

/* pick($hash, @keys) - extract subset of keys */
static XS(xs_pick) {
    dXSARGS;
    if (items < 1) croak("Usage: util::pick(\\%%hash, @keys)");

    SV *href = ST(0);
    if (!SvROK(href) || SvTYPE(SvRV(href)) != SVt_PVHV) {
        croak("util::pick: first argument must be a hashref");
    }

    HV *src = (HV*)SvRV(href);
    HV *dest = newHV();

    IV i;
    for (i = 1; i < items; i++) {
        SV *key = ST(i);
        STRLEN key_len;
        const char *key_pv = SvPV(key, key_len);

        SV **val = hv_fetch(src, key_pv, key_len, 0);
        if (val && SvOK(*val)) {
            hv_store(dest, key_pv, key_len, SvREFCNT_inc(*val), 0);
        }
    }

    ST(0) = newRV_noinc((SV*)dest);
    XSRETURN(1);
}

/* pluck(\@hashes, $field) - extract field from each hash */
static XS(xs_pluck) {
    dXSARGS;
    if (items != 2) croak("Usage: util::pluck(\\@array, $field)");

    SV *aref = ST(0);
    if (!SvROK(aref) || SvTYPE(SvRV(aref)) != SVt_PVAV) {
        croak("util::pluck: first argument must be an arrayref");
    }

    SV *field = ST(1);
    STRLEN field_len;
    const char *field_pv = SvPV(field, field_len);

    AV *src = (AV*)SvRV(aref);
    IV len = av_len(src) + 1;
    AV *dest = newAV();
    av_extend(dest, len - 1);

    IV i;
    for (i = 0; i < len; i++) {
        SV **elem = av_fetch(src, i, 0);
        if (elem && SvROK(*elem) && SvTYPE(SvRV(*elem)) == SVt_PVHV) {
            HV *hv = (HV*)SvRV(*elem);
            SV **val = hv_fetch(hv, field_pv, field_len, 0);
            if (val && SvOK(*val)) {
                av_push(dest, SvREFCNT_inc(*val));
            } else {
                av_push(dest, &PL_sv_undef);
            }
        } else {
            av_push(dest, &PL_sv_undef);
        }
    }

    ST(0) = newRV_noinc((SV*)dest);
    XSRETURN(1);
}

/* ============================================
   Null coalescing functions
   ============================================ */

/* nvl($x, $default) - return $x if defined, else $default */
static XS(xs_nvl) {
    dXSARGS;
    if (items != 2) croak("Usage: util::nvl($value, $default)");

    SV *val = ST(0);
    if (SvOK(val)) {
        XSRETURN(1);  /* Return first arg */
    }
    ST(0) = ST(1);
    XSRETURN(1);
}

/* coalesce($a, $b, ...) - return first defined value */
static XS(xs_coalesce) {
    dXSARGS;
    if (items < 1) croak("Usage: util::coalesce($val, ...)");

    IV i;
    for (i = 0; i < items; i++) {
        if (SvOK(ST(i))) {
            ST(0) = ST(i);
            XSRETURN(1);
        }
    }
    /* All undefined, return undef */
    ST(0) = &PL_sv_undef;
    XSRETURN(1);
}

/* ============================================
   List functions (first, any, all, none)
   ============================================ */

/* first { block } @list - return first element where block returns true */
static XS(xs_first) {
    dXSARGS;
    if (items < 1) croak("Usage: util::first(\\&block, @list)");

    SV *block = ST(0);
    if (!SvROK(block) || SvTYPE(SvRV(block)) != SVt_PVCV) {
        croak("util::first: first argument must be a coderef");
    }

    IV i;
    for (i = 1; i < items; i++) {
        SV *elem = ST(i);

        ENTER;
        SAVETMPS;
        SAVE_DEFSV;
        DEFSV_set(elem);

        PUSHMARK(SP);
        XPUSHs(elem);
        PUTBACK;

        IV count = call_sv(block, G_SCALAR);

        SPAGAIN;
        SV *result = POPs;
        bool matched = SvTRUE(result);
        PUTBACK;
        FREETMPS;
        LEAVE;

        if (matched) {
            ST(0) = elem;
            XSRETURN(1);
        }
    }

    XSRETURN_UNDEF;
}

/* any { block } @list - return true if any element matches */
static XS(xs_any) {
    dXSARGS;
    if (items < 1) croak("Usage: util::any(\\&block, @list)");

    SV *block = ST(0);
    if (!SvROK(block) || SvTYPE(SvRV(block)) != SVt_PVCV) {
        croak("util::any: first argument must be a coderef");
    }

    IV i;
    for (i = 1; i < items; i++) {
        SV *elem = ST(i);

        ENTER;
        SAVETMPS;
        SAVE_DEFSV;
        DEFSV_set(elem);

        PUSHMARK(SP);
        XPUSHs(elem);
        PUTBACK;

        call_sv(block, G_SCALAR);

        SPAGAIN;
        SV *result = POPs;
        bool matched = SvTRUE(result);
        PUTBACK;
        FREETMPS;
        LEAVE;

        if (matched) {
            XSRETURN_YES;
        }
    }

    XSRETURN_NO;
}

/* all { block } @list - return true if all elements match */
static XS(xs_all) {
    dXSARGS;
    if (items < 1) croak("Usage: util::all(\\&block, @list)");

    SV *block = ST(0);
    if (!SvROK(block) || SvTYPE(SvRV(block)) != SVt_PVCV) {
        croak("util::all: first argument must be a coderef");
    }

    /* Empty list returns true (vacuous truth) */
    if (items == 1) {
        XSRETURN_YES;
    }

    IV i;
    for (i = 1; i < items; i++) {
        SV *elem = ST(i);

        ENTER;
        SAVETMPS;
        SAVE_DEFSV;
        DEFSV_set(elem);

        PUSHMARK(SP);
        XPUSHs(elem);
        PUTBACK;

        call_sv(block, G_SCALAR);

        SPAGAIN;
        SV *result = POPs;
        bool matched = SvTRUE(result);
        PUTBACK;
        FREETMPS;
        LEAVE;

        if (!matched) {
            XSRETURN_NO;
        }
    }

    XSRETURN_YES;
}

/* none { block } @list - return true if no elements match */
static XS(xs_none) {
    dXSARGS;
    if (items < 1) croak("Usage: util::none(\\&block, @list)");

    SV *block = ST(0);
    if (!SvROK(block) || SvTYPE(SvRV(block)) != SVt_PVCV) {
        croak("util::none: first argument must be a coderef");
    }

    IV i;
    for (i = 1; i < items; i++) {
        SV *elem = ST(i);

        ENTER;
        SAVETMPS;
        SAVE_DEFSV;
        DEFSV_set(elem);

        PUSHMARK(SP);
        XPUSHs(elem);
        PUTBACK;

        call_sv(block, G_SCALAR);

        SPAGAIN;
        SV *result = POPs;
        bool matched = SvTRUE(result);
        PUTBACK;
        FREETMPS;
        LEAVE;

        if (matched) {
            XSRETURN_NO;
        }
    }

    XSRETURN_YES;
}

/* ============================================
   Type predicate XS fallbacks
   ============================================ */

static XS(xs_is_ref) {
    dXSARGS;
    if (items != 1) croak("Usage: util::is_ref($value)");
    ST(0) = SvROK(ST(0)) ? &PL_sv_yes : &PL_sv_no;
    XSRETURN(1);
}

static XS(xs_is_array) {
    dXSARGS;
    if (items != 1) croak("Usage: util::is_array($value)");
    SV *sv = ST(0);
    ST(0) = (SvROK(sv) && SvTYPE(SvRV(sv)) == SVt_PVAV) ? &PL_sv_yes : &PL_sv_no;
    XSRETURN(1);
}

static XS(xs_is_hash) {
    dXSARGS;
    if (items != 1) croak("Usage: util::is_hash($value)");
    SV *sv = ST(0);
    ST(0) = (SvROK(sv) && SvTYPE(SvRV(sv)) == SVt_PVHV) ? &PL_sv_yes : &PL_sv_no;
    XSRETURN(1);
}

static XS(xs_is_code) {
    dXSARGS;
    if (items != 1) croak("Usage: util::is_code($value)");
    SV *sv = ST(0);
    ST(0) = (SvROK(sv) && SvTYPE(SvRV(sv)) == SVt_PVCV) ? &PL_sv_yes : &PL_sv_no;
    XSRETURN(1);
}

static XS(xs_is_defined) {
    dXSARGS;
    if (items != 1) croak("Usage: util::is_defined($value)");
    ST(0) = SvOK(ST(0)) ? &PL_sv_yes : &PL_sv_no;
    XSRETURN(1);
}

/* ============================================
   String predicate XS fallbacks
   ============================================ */

static XS(xs_is_empty) {
    dXSARGS;
    if (items != 1) croak("Usage: util::is_empty($value)");
    SV *sv = ST(0);
    if (!SvOK(sv)) {
        ST(0) = &PL_sv_yes;
    } else {
        STRLEN len;
        SvPV(sv, len);
        ST(0) = len == 0 ? &PL_sv_yes : &PL_sv_no;
    }
    XSRETURN(1);
}

static XS(xs_starts_with) {
    dXSARGS;
    if (items != 2) croak("Usage: util::starts_with($string, $prefix)");

    SV *str_sv = ST(0);
    SV *prefix_sv = ST(1);

    if (!SvOK(str_sv) || !SvOK(prefix_sv)) {
        ST(0) = &PL_sv_no;
        XSRETURN(1);
    }

    STRLEN str_len, prefix_len;
    const char *str = SvPV(str_sv, str_len);
    const char *prefix = SvPV(prefix_sv, prefix_len);

    if (prefix_len > str_len) {
        ST(0) = &PL_sv_no;
    } else if (prefix_len == 0) {
        ST(0) = &PL_sv_yes;
    } else {
        ST(0) = memcmp(str, prefix, prefix_len) == 0 ? &PL_sv_yes : &PL_sv_no;
    }
    XSRETURN(1);
}

static XS(xs_ends_with) {
    dXSARGS;
    if (items != 2) croak("Usage: util::ends_with($string, $suffix)");

    SV *str_sv = ST(0);
    SV *suffix_sv = ST(1);

    if (!SvOK(str_sv) || !SvOK(suffix_sv)) {
        ST(0) = &PL_sv_no;
        XSRETURN(1);
    }

    STRLEN str_len, suffix_len;
    const char *str = SvPV(str_sv, str_len);
    const char *suffix = SvPV(suffix_sv, suffix_len);

    if (suffix_len > str_len) {
        ST(0) = &PL_sv_no;
    } else if (suffix_len == 0) {
        ST(0) = &PL_sv_yes;
    } else {
        const char *str_end = str + str_len - suffix_len;
        ST(0) = memcmp(str_end, suffix, suffix_len) == 0 ? &PL_sv_yes : &PL_sv_no;
    }
    XSRETURN(1);
}

/* ============================================
   Import function
   ============================================ */

static char* get_caller(pTHX) {
    return HvNAME((HV*)CopSTASH(PL_curcop));
}

static XS(xs_import) {
    dXSARGS;
    char *pkg = get_caller(aTHX);
    IV i;

    for (i = 1; i < items; i++) {
        char *name = SvPV_nolen(ST(i));
        char full[512];
        CV *cv = NULL;

        snprintf(full, sizeof(full), "%s::%s", pkg, name);

        if (strcmp(name, "memo") == 0) {
            cv = newXS(full, xs_memo, __FILE__);
        } else if (strcmp(name, "pipeline") == 0) {
            cv = newXS(full, xs_pipe, __FILE__);
        } else if (strcmp(name, "compose") == 0) {
            cv = newXS(full, xs_compose, __FILE__);
        } else if (strcmp(name, "lazy") == 0) {
            cv = newXS(full, xs_lazy, __FILE__);
        } else if (strcmp(name, "force") == 0) {
            cv = newXS(full, xs_force, __FILE__);
        } else if (strcmp(name, "dig") == 0) {
            cv = newXS(full, xs_dig, __FILE__);
        } else if (strcmp(name, "clamp") == 0) {
            cv = newXS(full, xs_clamp, __FILE__);
        } else if (strcmp(name, "tap") == 0) {
            cv = newXS(full, xs_tap, __FILE__);
        } else if (strcmp(name, "identity") == 0) {
            cv = newXS(full, xs_identity, __FILE__);
            cv_set_call_checker(cv, identity_call_checker, (SV*)cv);
        } else if (strcmp(name, "always") == 0) {
            cv = newXS(full, xs_always, __FILE__);
        } else if (strcmp(name, "nvl") == 0) {
            cv = newXS(full, xs_nvl, __FILE__);
        } else if (strcmp(name, "coalesce") == 0) {
            cv = newXS(full, xs_coalesce, __FILE__);
        } else if (strcmp(name, "first") == 0) {
            cv = newXS(full, xs_first, __FILE__);
        } else if (strcmp(name, "any") == 0) {
            cv = newXS(full, xs_any, __FILE__);
        } else if (strcmp(name, "all") == 0) {
            cv = newXS(full, xs_all, __FILE__);
        } else if (strcmp(name, "none") == 0) {
            cv = newXS(full, xs_none, __FILE__);
        } else if (strcmp(name, "negate") == 0) {
            cv = newXS(full, xs_negate, __FILE__);
        } else if (strcmp(name, "once") == 0) {
            cv = newXS(full, xs_once, __FILE__);
        } else if (strcmp(name, "partial") == 0) {
            cv = newXS(full, xs_partial, __FILE__);
        } else if (strcmp(name, "pick") == 0) {
            cv = newXS(full, xs_pick, __FILE__);
        } else if (strcmp(name, "pluck") == 0) {
            cv = newXS(full, xs_pluck, __FILE__);
        } else if (strcmp(name, "is_ref") == 0) {
            cv = newXS(full, xs_is_ref, __FILE__);
            cv_set_call_checker(cv, is_ref_call_checker, (SV*)cv);
        } else if (strcmp(name, "is_array") == 0) {
            cv = newXS(full, xs_is_array, __FILE__);
            cv_set_call_checker(cv, is_array_call_checker, (SV*)cv);
        } else if (strcmp(name, "is_hash") == 0) {
            cv = newXS(full, xs_is_hash, __FILE__);
            cv_set_call_checker(cv, is_hash_call_checker, (SV*)cv);
        } else if (strcmp(name, "is_code") == 0) {
            cv = newXS(full, xs_is_code, __FILE__);
            cv_set_call_checker(cv, is_code_call_checker, (SV*)cv);
        } else if (strcmp(name, "is_defined") == 0) {
            cv = newXS(full, xs_is_defined, __FILE__);
            cv_set_call_checker(cv, is_defined_call_checker, (SV*)cv);
        } else if (strcmp(name, "is_empty") == 0) {
            cv = newXS(full, xs_is_empty, __FILE__);
            cv_set_call_checker(cv, is_empty_call_checker, (SV*)cv);
        } else if (strcmp(name, "starts_with") == 0) {
            cv = newXS(full, xs_starts_with, __FILE__);
            cv_set_call_checker(cv, starts_with_call_checker, (SV*)cv);
        } else if (strcmp(name, "ends_with") == 0) {
            cv = newXS(full, xs_ends_with, __FILE__);
            cv_set_call_checker(cv, ends_with_call_checker, (SV*)cv);
        } else {
            croak("util: unknown export '%s'", name);
        }
        PERL_UNUSED_VAR(cv);
    }

    XSRETURN_EMPTY;
}

/* ============================================
   Boot
   ============================================ */

XS_EXTERNAL(boot_util) {
    dXSBOOTARGSXSAPIVERCHK;
    PERL_UNUSED_VAR(items);

    /* Register custom ops */
    XopENTRY_set(&identity_xop, xop_name, "identity");
    XopENTRY_set(&identity_xop, xop_desc, "identity passthrough");
    Perl_custom_op_register(aTHX_ pp_identity, &identity_xop);

    XopENTRY_set(&always_xop, xop_name, "always");
    XopENTRY_set(&always_xop, xop_desc, "always return stored value");
    Perl_custom_op_register(aTHX_ pp_always, &always_xop);

    XopENTRY_set(&clamp_xop, xop_name, "clamp");
    XopENTRY_set(&clamp_xop, xop_desc, "clamp value between min and max");
    Perl_custom_op_register(aTHX_ pp_clamp, &clamp_xop);

    /* Register type predicate custom ops */
    XopENTRY_set(&is_ref_xop, xop_name, "is_ref");
    XopENTRY_set(&is_ref_xop, xop_desc, "check if value is a reference");
    Perl_custom_op_register(aTHX_ pp_is_ref, &is_ref_xop);

    XopENTRY_set(&is_array_xop, xop_name, "is_array");
    XopENTRY_set(&is_array_xop, xop_desc, "check if value is an arrayref");
    Perl_custom_op_register(aTHX_ pp_is_array, &is_array_xop);

    XopENTRY_set(&is_hash_xop, xop_name, "is_hash");
    XopENTRY_set(&is_hash_xop, xop_desc, "check if value is a hashref");
    Perl_custom_op_register(aTHX_ pp_is_hash, &is_hash_xop);

    XopENTRY_set(&is_code_xop, xop_name, "is_code");
    XopENTRY_set(&is_code_xop, xop_desc, "check if value is a coderef");
    Perl_custom_op_register(aTHX_ pp_is_code, &is_code_xop);

    XopENTRY_set(&is_defined_xop, xop_name, "is_defined");
    XopENTRY_set(&is_defined_xop, xop_desc, "check if value is defined");
    Perl_custom_op_register(aTHX_ pp_is_defined, &is_defined_xop);

    /* Register string predicate custom ops */
    XopENTRY_set(&is_empty_xop, xop_name, "is_empty");
    XopENTRY_set(&is_empty_xop, xop_desc, "check if string is empty");
    Perl_custom_op_register(aTHX_ pp_is_empty, &is_empty_xop);

    XopENTRY_set(&starts_with_xop, xop_name, "starts_with");
    XopENTRY_set(&starts_with_xop, xop_desc, "check if string starts with prefix");
    Perl_custom_op_register(aTHX_ pp_starts_with, &starts_with_xop);

    XopENTRY_set(&ends_with_xop, xop_name, "ends_with");
    XopENTRY_set(&ends_with_xop, xop_desc, "check if string ends with suffix");
    Perl_custom_op_register(aTHX_ pp_ends_with, &ends_with_xop);

    /* Initialize memo storage */
    g_memo_size = 16;
    Newxz(g_memos, g_memo_size, MemoizedFunc);

    /* Initialize lazy storage */
    g_lazy_size = 16;
    Newxz(g_lazies, g_lazy_size, LazyValue);

    /* Initialize always storage */
    g_always_size = 16;
    Newxz(g_always_values, g_always_size, SV*);

    /* Initialize once storage */
    g_once_size = 16;
    Newxz(g_onces, g_once_size, OnceFunc);

    /* Initialize partial storage */
    g_partial_size = 16;
    Newxz(g_partials, g_partial_size, PartialFunc);

    /* Export functions */
    newXS("util::import", xs_import, __FILE__);
    newXS("util::memo", xs_memo, __FILE__);
    newXS("util::pipeline", xs_pipe, __FILE__);
    newXS("util::compose", xs_compose, __FILE__);
    newXS("util::lazy", xs_lazy, __FILE__);
    newXS("util::force", xs_force, __FILE__);
    newXS("util::dig", xs_dig, __FILE__);
    
    {
        CV *cv = newXS("util::clamp", xs_clamp, __FILE__);
        cv_set_call_checker(cv, clamp_call_checker, (SV*)cv);
    }
    
    newXS("util::tap", xs_tap, __FILE__);

    {
        CV *cv = newXS("util::identity", xs_identity, __FILE__);
        cv_set_call_checker(cv, identity_call_checker, (SV*)cv);
    }

    newXS("util::always", xs_always, __FILE__);
    newXS("util::nvl", xs_nvl, __FILE__);
    newXS("util::coalesce", xs_coalesce, __FILE__);

    /* List functions */
    newXS("util::first", xs_first, __FILE__);
    newXS("util::any", xs_any, __FILE__);
    newXS("util::all", xs_all, __FILE__);
    newXS("util::none", xs_none, __FILE__);

    /* Functional combinators */
    newXS("util::negate", xs_negate, __FILE__);
    newXS("util::once", xs_once, __FILE__);
    newXS("util::partial", xs_partial, __FILE__);

    /* Data extraction */
    newXS("util::pick", xs_pick, __FILE__);
    newXS("util::pluck", xs_pluck, __FILE__);

    /* Type predicates with call checkers */
    {
        CV *cv = newXS("util::is_ref", xs_is_ref, __FILE__);
        cv_set_call_checker(cv, is_ref_call_checker, (SV*)cv);
    }
    {
        CV *cv = newXS("util::is_array", xs_is_array, __FILE__);
        cv_set_call_checker(cv, is_array_call_checker, (SV*)cv);
    }
    {
        CV *cv = newXS("util::is_hash", xs_is_hash, __FILE__);
        cv_set_call_checker(cv, is_hash_call_checker, (SV*)cv);
    }
    {
        CV *cv = newXS("util::is_code", xs_is_code, __FILE__);
        cv_set_call_checker(cv, is_code_call_checker, (SV*)cv);
    }
    {
        CV *cv = newXS("util::is_defined", xs_is_defined, __FILE__);
        cv_set_call_checker(cv, is_defined_call_checker, (SV*)cv);
    }

    /* String predicates with call checkers */
    {
        CV *cv = newXS("util::is_empty", xs_is_empty, __FILE__);
        cv_set_call_checker(cv, is_empty_call_checker, (SV*)cv);
    }
    {
        CV *cv = newXS("util::starts_with", xs_starts_with, __FILE__);
        cv_set_call_checker(cv, starts_with_call_checker, (SV*)cv);
    }
    {
        CV *cv = newXS("util::ends_with", xs_ends_with, __FILE__);
        cv_set_call_checker(cv, ends_with_call_checker, (SV*)cv);
    }

    Perl_xs_boot_epilog(aTHX_ ax);
}
