Skip to documentation
SLOP

tiny.pluck.evaluator

Reference tiny.pluck evaluator

Defined in tiny.pluck.

API (63)

Actions

Public operations.

Types and contracts

Public types and contracts.

No direct callersNo direct callstiny.pluckevaluator
Static calls · unresolved targets: unknown · external targets: unknown.

Source

Source: lib/pluck/src/dist.zig:18

zig
pub const CombinedIntDist = struct {    int_dist: IntDist,    overall_guard: Bdd,};

Source: lib/pluck/src/dist.zig:13

zig
pub const IntDistWithGuard = struct {    int_dist: IntDist,    guard: Bdd,};

Source: lib/pluck/src/monad.zig:23

zig
pub const CompileError = error{    OutOfMemory,    PluckError,    InvalidExpression,    NotImplemented,    InvalidThunkUnion,};

Source: lib/pluck/src/state/config.zig:55

zig
pub const FallbackMode = enum {    @"error",    lpsmc,};

Source: lib/pluck/src/state/config.zig:60

zig
pub const InferenceMode = enum {    exact,    lpsmc,};

Source: lib/pluck/src/state/config.zig:7

zig
pub const LazyKCConfig = struct {    max_depth: ?u32 = null,    sample_after_max_depth: bool = false,    use_strict_order: bool = true,    use_reverse_order: bool = false,    definition_order: ?*const definition_order.DefinitionOrder = null,    use_thunk_unions: bool = true,    disable_validity_tracking: bool = false,    disable_path_conditions: bool = false,    time_limit: ?f64 = null,    ite_limit: ?u64 = null,    factor_max_branches: usize = 64,    weight_dd_max_nodes: usize = 0,    fallback_mode: FallbackMode = .@"error",    inference_mode: InferenceMode = .exact,    sample_constraint: ?Bdd = null,    full_dist: bool = false,    dual: bool = false,    vector_size: u32 = 0,    state_vars: StateVars = StateVars.init(),    stacktrace: bool = true,    parallel_wmc: bool = false,    parallel_wmc_threshold: usize = 16,    parallel_wmc_threads: ?usize = null,};

Source: lib/pluck/src/state/machine.zig:37

zig
pub const LazyKCState = struct {    allocator: Allocator,    manager: *Manager,    wmc_params: WmcParams,    weight_dd: WeightDD,    weight_dd_root: Weight,    weight_dd_one: Weight,    deferred_weights: std.ArrayListUnmanaged(DeferredWeight),    cfg: LazyKCConfig,    stats: LazyKCStats,    callstack: std.ArrayList(i32),    var_of_callstack: std.HashMapUnmanaged(StateCallstackKey, Bdd, StateCallstackHashContext, 80),    sorted_callstacks: std.ArrayList(StateCallstackKey),    depth: u32,    definitions: *const Definitions,    current_def_name: ?Symbol,    stacktrace_buf: std.ArrayList(*PExpr),    query: ?*PExpr,    next_thunk_id: u32,    start_time: i128,    registry: ?*ThunkRegistry,    sampled_flips: std.HashMapUnmanaged(StateCallstackKey, bool, StateCallstackHashContext, 80),    prng: std.Random.DefaultPrng,    def_thunks: std.StringHashMapUnmanaged(*RuntimeValue),    pub const DeferredWeight = struct {        guards: []GuardedWeight,    };    pub const CallstackKey: type = callstack.CallstackKey;    pub const CallstackHashContext: type = callstack.CallstackHashContext;};

Source: lib/pluck/src/state/stats.zig:17

zig
pub const LazyKCStats = struct {    pub const MaxBddSamples: usize = 32;    time_ns: u64 = 0,    num_forward_calls: u64 = 0,    limit_reason: ?LimitReason = null,    program_error: bool = false,    num_recursive_calls: u64 = 0,    ite_cache_hits: u64 = 0,    ite_cache_misses: u64 = 0,    unique_table_grows: u64 = 0,    ite_cache_grows: u64 = 0,    thunk_reuse_hits: u64 = 0,    thunk_reuse_misses: u64 = 0,    thunk_evaluations: u64 = 0,    thunk_cache_hits: u64 = 0,    variable_count: u64 = 0,    node_count: u64 = 0,    wmc_time_ns: u64 = 0,    refinement_time_ns: u64 = 0,    refinement_count: u64 = 0,    bdd_samples_forward_calls: [MaxBddSamples]u64 = @as([MaxBddSamples]u64, @splat(0)),    bdd_samples_vars: [MaxBddSamples]u64 = @as([MaxBddSamples]u64, @splat(0)),    bdd_samples_nodes: [MaxBddSamples]u64 = @as([MaxBddSamples]u64, @splat(0)),    bdd_samples_len: u8 = 0,    max_factor_guard_branches: usize = 0,};

Source: lib/pluck/src/state/stats.zig:1

zig
pub const LimitReason = enum {    max_depth,    time_limit,    ite_limit,    factor_weight_too_complex,    pub fn message(self: LimitReason) []const u8 {        return switch (self) {            .max_depth => "max_depth limit exceeded (consider increasing --max-depth or using bounded distributions)",            .time_limit => "time limit exceeded",            .ite_limit => "BDD work quota exceeded (model may be too complex)",            .factor_weight_too_complex => "factor weight too complex for exact KC (retry with --fallback-mode=lpsmc)",        };    }};

Source: lib/pluck/src/dist.zig:23

zig
pub fn combineIntDists(    allocator: Allocator,    int_dist_results: []const IntDistWithGuard,    manager: *Manager,) !CombinedIntDist {    if (int_dist_results.len == 0) {        return .{ .int_dist = IntDist.init(&.{}), .overall_guard = Bdd.FALSE };    }    const width = int_dist_results[0].int_dist.bits.len;    const result_bits = try allocator.alloc(Bdd, width);    for (result_bits) |*bit| {        bit.* = Bdd.FALSE;    }    var overall_guard = Bdd.FALSE;    for (int_dist_results) |entry| {        std.debug.assert(entry.int_dist.bits.len == width);        overall_guard = try manager.bddOr(overall_guard, entry.guard);        for (0..width) |i| {            const new_bit = try manager.bddAnd(entry.int_dist.bits[i], entry.guard);            result_bits[i] = try manager.bddOr(result_bits[i], new_bit);        }    }    return .{        .int_dist = IntDist.init(result_bits),        .overall_guard = overall_guard,    };}
Called byCallsevaluatorprocessIntDistWorldstest sourcelib.pluck.src.evaluatortest: combineIntDists - combines two ...runtime.IntDistinitevaluatorcombineIntDists
Static calls · unresolved targets: 0 · external targets: 3.

Source: lib/pluck/src/dist.zig:79

zig
pub fn enumerateIntDist(    allocator: Allocator,    int_dist: IntDist,    overall_guard: Bdd,    manager: *Manager,) ![]World {    const num_bits = int_dist.bits.len;    std.debug.assert(num_bits <= 20);    const num_values: usize = @as(usize, 1) << @intCast(num_bits);    var results: std.ArrayList(World) = .empty;    defer results.deinit(allocator);    for (0..num_values) |i| {        const value: u64 = @intCast(i);        const value_guard = try intDistAtInt(int_dist, value, manager);        const combined_guard = try manager.bddAnd(value_guard, overall_guard);        if (combined_guard.isFalse()) {            continue;        }        const int_val = try RuntimeValue.initNative(allocator, .{ .int = @intCast(value) });        try results.append(allocator, World{ .value = int_val, .guard = combined_guard });    }    return results.toOwnedSlice(allocator);}
Called byCallsevaluatorprocessIntDistWorldstest sourcelib.pluck.src.evaluatortest: enumerateIntDist - deterministi...test sourcelib.pluck.src.evaluatortest: enumerateIntDist - non-determin...evaluatorintDistAtIntruntime.RuntimeValueinitNativetiny.reticulumnode.fixture.WorlddeinitevaluatorenumerateIntDist
Static calls · unresolved targets: 2 · external targets: 2.

Source: lib/pluck/src/dist.zig:58

zig
pub fn intDistAtInt(int_dist: IntDist, value: u64, manager: *Manager) Allocator.Error!Bdd {    var result = Bdd.TRUE;    for (0..int_dist.bits.len) |i| {        const bit_val = (value >> @intCast(i)) & 1 == 1;        const bit_bdd = int_dist.bits[i];        if (bit_val) {            result = try manager.bddAnd(result, bit_bdd);        } else {            result = try manager.bddAnd(result, manager.bddNot(bit_bdd));        }        if (result.isFalse()) {            return Bdd.FALSE;        }    }    return result;}
Called byCallsNo direct callsevaluatorenumerateIntDistevaluatorintDistAtInt
Static calls · unresolved targets: 0 · external targets: 3.

Source: lib/pluck/src/dist.zig:109

zig
pub fn processIntDistWorlds(    allocator: Allocator,    worlds: []World,    manager: *Manager,) !?[]World {    if (worlds.len == 0) {        return null;    }    const first_int_dist = worlds[0].value.maybeIntDist() orelse return null;    const width = first_int_dist.bits.len;    if (width > 20) {        return null;    }    for (worlds) |world| {        const int_dist = world.value.maybeIntDist() orelse return null;        if (int_dist.bits.len != width) {            return null;        }    }    const int_dist_pairs = try allocator.alloc(IntDistWithGuard, worlds.len);    defer allocator.free(int_dist_pairs);    for (worlds, 0..) |world, i| {        int_dist_pairs[i] = .{            .int_dist = world.value.maybeIntDist().?,            .guard = world.guard,        };    }    const combined = try combineIntDists(allocator, int_dist_pairs, manager);    errdefer allocator.free(combined.int_dist.bits);    const result = try enumerateIntDist(allocator, combined.int_dist, combined.overall_guard, manager);    allocator.free(combined.int_dist.bits);    for (worlds) |world| {        world.value.deinit(allocator);    }    return result;}
Called byCallsevaluatorcompileevaluatorcombineIntDistsevaluatorenumerateIntDistevaluatorprocessIntDistWorlds
Static calls · unresolved targets: 0 · external targets: 4.
Called byCallstest sourcelib.pluck.src.evaluatortest: IntDist enumeration - determini...test sourcelib.pluck.src.evaluatortest: IntDist enumeration - weighted ...test sourcelib.pluck.src.evaluatortest: factor WeightDD composes multip...test sourcelib.pluck.src.evaluatortest: factor WeightDD respects branch...test sourcelib.pluck.src.evaluatortest: factor defers WeightDD refineme...+4 moreevaluatorprocessIntDistWorldsprivate sourcelib.pluck.src.evaluatorcomputeWmcDeferredSequentialevaluatorinferFullDistributionevaluatortracedCompileInnerevaluatorfreeWorldsSlice+2 moreevaluatorcompile
Static calls · unresolved targets: 0 · external targets: 7.
Called byCallstest sourcelib.pluck.src.evaluatortest: flip produces path-condition-in...test sourcelib.pluck.src.evaluatortest: get args extracts arguments fro...test sourcelib.pluck.src.evaluatortest: get args extracts empty argumen...test sourcelib.pluck.src.evaluatortest: get args with S(O) returns sing...test sourcelib.pluck.src.evaluatortest: get constructor extracts constr...+19 moreprivate sourcelib.pluck.src.evaluatorcompileAbsprivate sourcelib.pluck.src.evaluatorcompileAppprivate sourcelib.pluck.src.evaluatorcompileCaseOfprivate sourcelib.pluck.src.evaluatorcompileConstNativeprivate sourcelib.pluck.src.evaluatorcompileConstruct+15 moreevaluatorcompileInner
Static calls · unresolved targets: 0 · external targets: 0.
Called byCallsNo direct callsevaluatorsubproblemMonteCarloImplprivate sourcelib.pluck.src.toplevel.queryexecuteLpsmcQueryevaluatorcreateEvaluatorOps
Static calls · unresolved targets: 0 · external targets: 0.
Called byCallsprivate sourcelib.pluck.src.evaluatorcollectFactorWeightWorldsprivate sourcelib.pluck.src.evaluatorcompileDefinedprivate sourcelib.pluck.src.evaluatorcompileVarprivate sourcelib.pluck.src.evaluatorevaluateThunkOpevaluatorforceValue+8 moreprivate sourcelib.pluck.src.evaluatorevaluateLazyKCThunkprivate sourcelib.pluck.src.evaluatorevaluateThunkUnionevaluatorpureMonadevaluatorevaluateThunk
Static calls · unresolved targets: 0 · external targets: 0.
Called byCallsprivate sourcelib.pluck.src.toplevel.queryparseLpsmcKprivate sourcelib.pluck.src.toplevel.queryparsePosteriorSampleCountevaluatorforceValueDeterministicevaluatorextractNatForcingThunks
Static calls · unresolved targets: 0 · external targets: 0.
Called byCallsprivate sourcelib.pluck.src.evaluator.MkIntWeightedContinua...contprivate sourcelib.pluck.src.evaluatorextractListForcingThunksevaluatorevaluateThunkevaluatorfreeWorldsSliceevaluatorforceValue
Static calls · unresolved targets: 0 · external targets: 0.
Called byCallsevaluatorextractNatForcingThunksprivate sourcelib.pluck.src.toplevel.queryparseLpsmcInnerQueryprivate sourcelib.pluck.src.toplevel.queryparseLpsmcKprivate sourcelib.pluck.src.toplevel.queryparsePosteriorSampleCountevaluatorevaluateThunkevaluatorfreeWorldsSliceevaluatorforceValueDeterministic
Static calls · unresolved targets: 0 · external targets: 0.
Called byCallsevaluatorcompileevaluatorprocessMarginalQueryevaluatorprocessPosteriorQueryevaluatorsubproblemMonteCarloImplprivate sourcelib.pluck.src.profiling.internal.factorrunExactFactorBenchParsedprivate sourcelib.pluck.src.profiling.internal.factorrunExactParsedevaluatorevaluateThunkevaluatorfreeWorldsSliceruntimefindFirstThunkIntoruntimegetValueAtPathruntimereplaceAtPathtiny.reticulumnode.fixture.WorlddeinitevaluatorinferFullDistribution
Static calls · unresolved targets: 4 · external targets: 3.
Called byCallsprivate sourcelib.pluck.src.evaluatorcompileAppprivate sourcelib.pluck.src.evaluatorcompileConstructprivate sourcelib.pluck.src.evaluatorcompileDefinedtest sourcelib.pluck.src.evaluatortest: joinMonad collapses constructor...test sourcelib.pluck.src.evaluatortest: joinMonad collapses list constr...+3 moreruntime.LazyKCThunkinittiny.sysenvgetevaluatormakeThunk
Static calls · unresolved targets: 0 · external targets: 1.
Called byCallstoplevel.queryprocessMarginalQueryOnceevaluatorevaluateThunkevaluatorinferFullDistributionevaluatorfreeWorldsSliceevaluatorprocessMarginalQuery
Static calls · unresolved targets: 0 · external targets: 0.
Called byCallsprivate sourcelib.pluck.src.toplevel.queryposteriorWorldsForSamplestoplevel.queryprocessPosteriorQueryOnceevaluatorevaluateThunkevaluatorinferFullDistributionevaluatorfreeWorldsSlicetiny.reticulumnode.fixture.WorlddeinitevaluatorprocessPosteriorQuery
Static calls · unresolved targets: 2 · external targets: 3.
Called byCallsNo direct callersevaluatorcreateEvaluatorOpsevaluatorinferFullDistributionevaluatorsubproblemMonteCarloImpl
Static calls · unresolved targets: 0 · external targets: 1.
Called byCallsprivate sourcelib.pluck.src.evaluator.CompileAppContinuationcontprivate sourcelib.pluck.src.evaluator.CompileCaseContinuationcontprivate sourcelib.pluck.src.evaluator.FloatBinopFirstContin...contprivate sourcelib.pluck.src.evaluator.IntDistEqFirstContinu...contprivate sourcelib.pluck.src.evaluator.NativeEqFirstContinua...cont+16 moreevaluatorcompileInnerevaluatorfalsePathConditionWorldsevaluatorinferenceErrorWorldsevaluatortracedCompileInner
Static calls · unresolved targets: 1 · external targets: 6.

Source: lib/pluck/src/monad.zig:95

zig
pub fn bindMonad(    allocator: Allocator,    pre_worlds: WorldsResult,    path_condition: Bdd,    state: *LazyKCState,    comptime ContType: type,    ctx: anytype,) CompileError!WorldsResult {    defer freeWorldsSlice(allocator, pre_worlds.worlds);    var nested_worlds: std.ArrayList(NestedWorld) = .empty;    defer nested_worlds.deinit(allocator);    for (pre_worlds.worlds) |pre_world| {        if (state.stats.limit_reason != null) {            return inferenceErrorWorlds(state);        }        const inner_pc = if (state.cfg.disable_path_conditions)            Bdd.TRUE        else            try state.manager.bddAnd(path_condition, pre_world.guard);        if (inner_pc.isFalse()) {            try nested_worlds.append(allocator, NestedWorld{                .result = falsePathConditionWorlds(state),                .guard = pre_world.guard,            });            continue;        }        const post_result = try ContType.cont(allocator, pre_world.value, inner_pc, state, ctx);        try nested_worlds.append(allocator, NestedWorld{            .result = post_result,            .guard = pre_world.guard,        });    }    return joinMonad(allocator, nested_worlds.items, pre_worlds.validity_guard, state);}
Called byCallsprivate sourcelib.pluck.src.evaluator.FloatBinopFirstContin...contprivate sourcelib.pluck.src.evaluator.IntDistEqFirstContinu...contprivate sourcelib.pluck.src.evaluator.NativeEqFirstContinua...contprivate sourcelib.pluck.src.evaluatorcompileAppprivate sourcelib.pluck.src.evaluatorcompileCaseOf+10 moreevaluatorfalsePathConditionWorldsevaluatorfreeWorldsSliceevaluatorinferenceErrorWorldsevaluatorjoinMonadevaluatorbindMonad
Static calls · unresolved targets: 3 · external targets: 2.

Source: lib/pluck/src/monad.zig:84

zig
pub fn conditionWorlds(allocator: Allocator, worlds: []World, condition: Bdd, manager: *Manager) ![]World {    const result = try allocator.alloc(World, worlds.len);    for (worlds, 0..) |world, i| {        result[i] = World{            .value = world.value,            .guard = manager.bddAnd(world.guard, condition),        };    }    return result;}

Source: lib/pluck/src/monad.zig:45

zig
pub fn falsePathConditionWorlds(_: *LazyKCState) WorldsResult {    return WorldsResult{        .worlds = &[_]World{},        .validity_guard = Bdd.FALSE,    };}
Called byCallsNo direct callsprivate sourcelib.pluck.src.evaluatorevaluateLazyKCThunkprivate sourcelib.pluck.src.evaluatorevaluateThunkUnionevaluatortracedCompileInnerevaluatorbindMonadevaluatorfalsePathConditionWorlds
Static calls · unresolved targets: 0 · external targets: 0.

Source: lib/pluck/src/monad.zig:52

zig
pub fn freeWorldsSlice(allocator: Allocator, worlds: []World) void {    if (worlds.len > 0) {        allocator.free(worlds);    }}
Called byCallsNo direct callsprivate sourcelib.pluck.src.evaluatorcollectFactorWeightWorldsevaluatorcompileprivate sourcelib.pluck.src.evaluatorcompileFactorprivate sourcelib.pluck.src.evaluatorcompilePBoolprivate sourcelib.pluck.src.evaluatorevaluateLazyKCThunk+15 moreevaluatorfreeWorldsSlice
Static calls · unresolved targets: 0 · external targets: 1.

Source: lib/pluck/src/monad.zig:67

zig
pub fn ifThenElseMonad(    allocator: Allocator,    val_if_true: *RuntimeValue,    val_if_false: *RuntimeValue,    condition: Bdd,    state: *LazyKCState,) !WorldsResult {    _ = state;    const worlds = try allocator.alloc(World, 2);    worlds[0] = World{ .value = val_if_true, .guard = condition };    worlds[1] = World{ .value = val_if_false, .guard = condition.neg() };    return WorldsResult{        .worlds = worlds,        .validity_guard = Bdd.TRUE,    };}
Called byCallsNo direct callsprivate sourcelib.pluck.src.evaluator.IntDistEqSecondContin...contprivate sourcelib.pluck.src.evaluatorevaluateLazyKCThunktest sourcelib.pluck.src.evaluatortest: if then else monadevaluatorifThenElseMonad
Static calls · unresolved targets: 0 · external targets: 2.

Source: lib/pluck/src/monad.zig:38

zig
pub fn inferenceErrorWorlds(_: *LazyKCState) WorldsResult {    return WorldsResult{        .worlds = &[_]World{},        .validity_guard = Bdd.TRUE,    };}
Called byCallsNo direct callsprivate sourcelib.pluck.src.evaluatorcompileFactorprivate sourcelib.pluck.src.evaluatorcompilePBoolevaluatortracedCompileInnerevaluatorbindMonadevaluatorinferenceErrorWorlds
Static calls · unresolved targets: 0 · external targets: 0.

Source: lib/pluck/src/monad.zig:136

zig
pub fn joinMonad(    allocator: Allocator,    nested_worlds: []const NestedWorld,    pre_validity_guard: Bdd,    state: *LazyKCState,) CompileError!WorldsResult {    var validity_guard = pre_validity_guard;    if (!state.cfg.disable_validity_tracking) {        for (nested_worlds) |nested| {            const branch_validity_guard = try state.manager.bddImplies(nested.guard, nested.result.validity_guard);            validity_guard = try state.manager.bddAnd(validity_guard, branch_validity_guard);        }    }    var join_results: std.ArrayList(World) = .empty;    defer join_results.deinit(allocator);    var index_of_result = std.HashMap(*RuntimeValue, usize, RuntimeValueContext, 80).init(allocator);    defer index_of_result.deinit();    var constructor_worlds = std.StringHashMap(std.ArrayList(World)).init(allocator);    defer {        if (state.cfg.use_thunk_unions) {            var it = constructor_worlds.iterator();            while (it.next()) |entry| {                entry.value_ptr.deinit(allocator);            }        }        constructor_worlds.deinit();    }    for (nested_worlds) |nested| {        defer freeWorldsSlice(allocator, nested.result.worlds);        for (nested.result.worlds) |post_world| {            const pre_and_post = try state.manager.bddAnd(post_world.guard, nested.guard);            if (state.cfg.use_thunk_unions and post_world.value.data == .constructed) {                const constructor = post_world.value.data.constructed.constructor;                const entry = try constructor_worlds.getOrPut(constructor);                if (!entry.found_existing) {                    entry.value_ptr.* = std.ArrayList(World).empty;                }                try entry.value_ptr.append(allocator, World{                    .value = post_world.value,                    .guard = pre_and_post,                });                continue;            }            if (index_of_result.get(post_world.value)) |result_idx| {                const old_guard = join_results.items[result_idx].guard;                const new_guard = try state.manager.bddOr(old_guard, pre_and_post);                join_results.items[result_idx].guard = new_guard;            } else {                const idx = join_results.items.len;                try index_of_result.put(post_world.value, idx);                try join_results.append(allocator, World{                    .value = post_world.value,                    .guard = pre_and_post,                });            }        }    }    if (state.cfg.use_thunk_unions) {        var ctor_iter = constructor_worlds.iterator();        while (ctor_iter.next()) |entry| {            const constructor = entry.key_ptr.*;            const worlds = entry.value_ptr.items;            if (worlds.len == 0) {                continue;            }            var value_to_guard = std.HashMap(*RuntimeValue, Bdd, RuntimeValueContext, 80).init(allocator);            defer value_to_guard.deinit();            for (worlds) |world| {                if (value_to_guard.get(world.value)) |old_guard| {                    const new_guard = try state.manager.bddOr(old_guard, world.guard);                    try value_to_guard.put(world.value, new_guard);                } else {                    try value_to_guard.put(world.value, world.guard);                }            }            if (value_to_guard.count() <= 1) {                var single_iter = value_to_guard.iterator();                while (single_iter.next()) |val_entry| {                    try join_results.append(allocator, World{                        .value = val_entry.key_ptr.*,                        .guard = val_entry.value_ptr.*,                    });                }                continue;            }            var first_iter = value_to_guard.iterator();            const first_entry = first_iter.next() orelse continue;            const first_val = first_entry.key_ptr.*;            if (first_val.data != .constructed) {                var fallback_iter = value_to_guard.iterator();                while (fallback_iter.next()) |val_entry| {                    try join_results.append(allocator, World{                        .value = val_entry.key_ptr.*,                        .guard = val_entry.value_ptr.*,                    });                }                continue;            }            const arg_count = first_val.data.constructed.args.len;            var arg_inputs = try allocator.alloc(std.ArrayList(LazyKCThunkUnion.ThunkInput), arg_count);            defer {                for (arg_inputs) |*list| {                    list.deinit(allocator);                }                allocator.free(arg_inputs);            }            for (arg_inputs) |*list| {                list.* = .empty;            }            var can_union = true;            var overall_guard = Bdd.FALSE;            var value_iter = value_to_guard.iterator();            while (value_iter.next()) |val_entry| {                const val = val_entry.key_ptr.*;                const guard = val_entry.value_ptr.*;                overall_guard = try state.manager.bddOr(overall_guard, guard);                if (val.data != .constructed) {                    can_union = false;                    break;                }                const c = val.data.constructed;                if (c.args.len != arg_count) {                    can_union = false;                    break;                }                for (c.args, 0..) |arg, i| {                    const can_union_arg = switch (arg.data) {                        .lazy_kc_thunk, .lazy_kc_thunk_union => true,                        else => false,                    };                    if (!can_union_arg) {                        can_union = false;                        break;                    }                    try arg_inputs[i].append(allocator, .{                        .value = arg,                        .outer_guard = guard,                    });                }                if (!can_union) {                    break;                }            }            if (!can_union) {                var fallback_iter = value_to_guard.iterator();                while (fallback_iter.next()) |val_entry| {                    try join_results.append(allocator, World{                        .value = val_entry.key_ptr.*,                        .guard = val_entry.value_ptr.*,                    });                }                continue;            }            const union_args = try allocator.alloc(*RuntimeValue, arg_count);            var created: usize = 0;            errdefer {                for (union_args[0..created]) |arg| {                    arg.deinit(allocator);                }                allocator.free(union_args);            }            for (arg_inputs, 0..) |*list, i| {                const thunk_union = try LazyKCThunkUnion.init(allocator, state.manager, list.items);                const union_val = try RuntimeValue.initLazyKCThunkUnion(allocator, thunk_union);                union_args[i] = union_val;                created += 1;            }            const combined_val = try RuntimeValue.initConstructed(allocator, constructor, union_args);            try join_results.append(allocator, World{                .value = combined_val,                .guard = overall_guard,            });        }    }    return WorldsResult{        .worlds = try join_results.toOwnedSlice(allocator),        .validity_guard = validity_guard,    };}
Called byCallstest sourcelib.pluck.src.evaluatortest: joinMonad collapses constructor...test sourcelib.pluck.src.evaluatortest: joinMonad collapses list constr...test sourcelib.pluck.src.evaluatortest: joinMonad collapses nested cons...test sourcelib.pluck.src.evaluatortest: joinMonad merges identical IntD...test sourcelib.pluck.src.evaluatortest: joinMonad merges structurally i...evaluatorbindMonadevaluatorfreeWorldsSliceruntime.LazyKCThunkUnioninitruntime.RuntimeValueinitConstructedruntime.RuntimeValueinitLazyKCThunkUniontiny.reticulumnode.fixture.WorlddeinitevaluatorjoinMonad
Static calls · unresolved targets: 7 · external targets: 21.

Source: lib/pluck/src/monad.zig:31

zig
pub fn programErrorWorlds(_: *LazyKCState) WorldsResult {    return WorldsResult{        .worlds = &[_]World{},        .validity_guard = Bdd.TRUE,    };}
Called byCallsNo direct callsprivate sourcelib.pluck.src.evaluator.CompileAppContinuationcontprivate sourcelib.pluck.src.evaluator.CompileCaseContinuationcontprivate sourcelib.pluck.src.evaluator.CompileFlipContinuationcontprivate sourcelib.pluck.src.evaluator.FloatBinopSecondConti...contprivate sourcelib.pluck.src.evaluator.GetArgsContinuationcont+13 moreevaluatorprogramErrorWorlds
Static calls · unresolved targets: 0 · external targets: 0.

Source: lib/pluck/src/monad.zig:58

zig
pub fn pureMonad(allocator: Allocator, val: *RuntimeValue, _: *LazyKCState) !WorldsResult {    const worlds = try allocator.alloc(World, 1);    worlds[0] = World{ .value = val, .guard = Bdd.TRUE };    return WorldsResult{        .worlds = worlds,        .validity_guard = Bdd.TRUE,    };}
Called byCallsNo direct callsprivate sourcelib.pluck.src.evaluator.BindIdentityContinuationcontprivate sourcelib.pluck.src.evaluator.CompileFlipContinuationcontprivate sourcelib.pluck.src.evaluator.FloatBinopSecondConti...contprivate sourcelib.pluck.src.evaluator.GetArgsContinuationcontprivate sourcelib.pluck.src.evaluator.GetConstructorContinu...cont+13 moreevaluatorpureMonad
Static calls · unresolved targets: 0 · external targets: 1.

Source: lib/pluck/src/state/callstack.zig:25

zig
pub fn compareCallstacks(a: []const i32, b: []const i32) std.math.Order {    const min_len = @min(a.len, b.len);    for (0..min_len) |i| {        if (a[i] < b[i]) return .lt;        if (a[i] > b[i]) return .gt;    }    if (a.len < b.len) return .lt;    if (a.len > b.len) return .gt;    return .eq;}
Called byCallsNo direct callstest sourcelib.pluck.src.evaluatortest: callstack orderingstatefindInsertPositionstatecompareCallstacks
Static calls · unresolved targets: 0 · external targets: 0.

Source: lib/pluck/src/evaluator.zig

zig
const std = @import("std");const time = @import("time.zig");const Allocator = std.mem.Allocator;const pexpr = @import("pexpr.zig");const PExpr = pexpr.PExpr;const Head = pexpr.Head;const Symbol = pexpr.Symbol;const Definitions = pexpr.Definitions;const runtime = @import("runtime.zig");const Env = runtime.Env;const RuntimeValue = runtime.RuntimeValue;const Closure = runtime.Closure;const LazyKCThunk = runtime.LazyKCThunk;const LazyKCThunkUnion = runtime.LazyKCThunkUnion;const StateVars = runtime.StateVars;const GuardedWorld = runtime.GuardedWorld;const GuardedWorlds = runtime.GuardedWorlds;const bdd = @import("bdd.zig");const Bdd = bdd.Bdd;const Manager = bdd.Manager;const WmcParams = bdd.WmcParams;const VarLabel = bdd.VarLabel;const weight_dd = @import("weight.zig");const GuardedWeight = weight_dd.GuardedWeight;const wmc_module = @import("wmc.zig");const thunk_registry = @import("registry.zig");const lpsmc_module = @import("lpsmc.zig");const int_dist_module = @import("dist.zig");const state_module = @import("state/root.zig");const monad_ops = @import("monad.zig");pub const Callstack = []const i32;pub const World = GuardedWorld;pub const WorldsResult = GuardedWorlds;pub const CompileError = monad_ops.CompileError;pub const RuntimeValueContext = runtime.RuntimeValueContext;pub const NestedWorld = runtime.NestedWorld;pub const WeightedResult = wmc_module.WeightedResult;const DeferredWmcCaches = wmc_module.DeferredCaches;pub const computeWmcParallel = wmc_module.computeWmcParallel;pub const computeWmcSequential = wmc_module.computeWmcSequential;pub const ThunkId = thunk_registry.ThunkId;pub const ThunkIdContext = thunk_registry.ThunkIdContext;pub const ThunkRegistry = thunk_registry.ThunkRegistry;pub const ThunkIdSet = thunk_registry.ThunkIdSet;pub const ThunkDependencies = thunk_registry.ThunkDependencies;pub const VarLabelSet = bdd.VarLabelSet;pub const PathChoice = lpsmc_module.PathChoice;pub const SubproblemCache = lpsmc_module.SubproblemCache;pub const LPSMCVarianceStats = lpsmc_module.LPSMCVarianceStats;pub const AdaptiveKPolicy = lpsmc_module.AdaptiveKPolicy;pub const LpsmcRunStats = lpsmc_module.LpsmcRunStats;pub const EvaluatorOps = lpsmc_module.EvaluatorOps;pub const IncrementalLPSMC = lpsmc_module.IncrementalLPSMC;pub const IntDistWithGuard = int_dist_module.IntDistWithGuard;pub const CombinedIntDist = int_dist_module.CombinedIntDist;pub const combineIntDists = int_dist_module.combineIntDists;pub const intDistAtInt = int_dist_module.intDistAtInt;pub const enumerateIntDist = int_dist_module.enumerateIntDist;pub const processIntDistWorlds = int_dist_module.processIntDistWorlds;pub const LazyKCConfig = state_module.LazyKCConfig;pub const LimitReason = state_module.LimitReason;pub const LazyKCStats = state_module.LazyKCStats;pub const LazyKCState = state_module.LazyKCState;pub const compareCallstacks = state_module.compareCallstacks;pub const FallbackMode = state_module.FallbackMode;pub const InferenceMode = state_module.InferenceMode;pub const programErrorWorlds = monad_ops.programErrorWorlds;pub const inferenceErrorWorlds = monad_ops.inferenceErrorWorlds;pub const falsePathConditionWorlds = monad_ops.falsePathConditionWorlds;pub const freeWorldsSlice = monad_ops.freeWorldsSlice;pub const pureMonad = monad_ops.pureMonad;pub const ifThenElseMonad = monad_ops.ifThenElseMonad;pub const conditionWorlds = monad_ops.conditionWorlds;pub const bindMonad = monad_ops.bindMonad;pub const joinMonad = monad_ops.joinMonad;const DeferredWmcError = error{    OutOfMemory,    NodeLimitExceeded,};fn wmcForState(state: *LazyKCState, guard: Bdd) DeferredWmcError!f64 {    return wmcWithDeferred(state, guard, state.allocator);}fn wmcForStateWithAllocator(state: *LazyKCState, guard: Bdd, allocator: Allocator) DeferredWmcError!f64 {    return wmcWithDeferred(state, guard, allocator);}fn wmcWithDeferred(state: *LazyKCState, guard: Bdd, allocator: Allocator) DeferredWmcError!f64 {    var caches = DeferredWmcCaches.init(allocator);    defer caches.deinit();    return wmcWithDeferredCached(state, guard, &caches);}fn wmcWithDeferredCached(state: *LazyKCState, guard: Bdd, caches: *DeferredWmcCaches) DeferredWmcError!f64 {    if (state.deferred_weights.items.len == 0) {        return weight_dd.wmcWeightedWithCache(            &state.weight_dd,            guard,            state.weight_dd_root,            &state.wmc_params,            &caches.weighted,        );    }    return wmcWithDeferredInner(state, guard, 0, &caches.deferred, &caches.weighted);}fn wmcWithDeferredInner(    state: *LazyKCState,    guard: Bdd,    index: usize,    deferred_cache: *std.AutoHashMap(u64, f64),    weighted_cache: *std.AutoHashMap(u64, f64),) DeferredWmcError!f64 {    if (guard.isFalse()) return 0.0;    if (index >= state.deferred_weights.items.len) {        return weight_dd.wmcWeightedWithCache(            &state.weight_dd,            guard,            state.weight_dd_root,            &state.wmc_params,            weighted_cache,        );    }    const cache_key: u64 = (@as(u64, @intCast(index)) << 32) | @as(u64, guard.toRaw());    if (deferred_cache.get(cache_key)) |cached| {        return cached;    }    const deferred = state.deferred_weights.items[index];    const limit = state.cfg.weight_dd_max_nodes;    if (limit != 0 and deferred.guards.len > limit) {        return error.NodeLimitExceeded;    }    var total: f64 = 0.0;    for (deferred.guards) |entry| {        if (entry.weight == 0.0) continue;        if (entry.guard.isFalse()) continue;        const combined = state.manager.bddAnd(guard, entry.guard) catch return error.OutOfMemory;        if (combined.isFalse()) continue;        const sub = try wmcWithDeferredInner(state, combined, index + 1, deferred_cache, weighted_cache);        total += entry.weight * sub;    }    deferred_cache.put(cache_key, total) catch {};    return total;}fn evaluateThunkOp(    allocator: Allocator,    val: *RuntimeValue,    path_condition: Bdd,    state_ptr: *anyopaque,) anyerror!lpsmc_module.WorldsResult {    const state: *LazyKCState = @ptrCast(@alignCast(state_ptr));    return evaluateThunk(allocator, val, path_condition, state);}fn freeWorldsSliceOp(allocator: Allocator, worlds: []World) void {    freeWorldsSlice(allocator, worlds);}fn setWeightOp(wmc_params: *WmcParams, variable: VarLabel, low: f64, high: f64) Allocator.Error!void {    return wmc_params.setWeight(variable, low, high);}pub fn createEvaluatorOps(state: *LazyKCState) EvaluatorOps {    return EvaluatorOps{        .evaluateThunk = evaluateThunkOp,        .freeWorldsSlice = freeWorldsSliceOp,        .setWeight = setWeightOp,        .state = state,        .wmc_params = &state.wmc_params,    };}pub fn subproblemMonteCarloImpl(    allocator: Allocator,    suspendible_thunk: *RuntimeValue,    evidence_thunk: ?*RuntimeValue,    k: usize,    k_policy: AdaptiveKPolicy,    state: *LazyKCState,    manager: *Manager,    external_rng: ?std.Random,) ![]World {    const ops = createEvaluatorOps(state);    const raw_worlds = try lpsmc_module.subproblemMonteCarloImpl(        allocator,        suspendible_thunk,        evidence_thunk,        k,        k_policy,        ops,        manager,        external_rng,    );    return inferFullDistribution(allocator, raw_worlds, state);}pub fn tracedCompileInner(    expr: *PExpr,    env: Env,    path_condition: Bdd,    state: *LazyKCState,    strict_order_index: i32,) CompileError!WorldsResult {    if (path_condition.isFalse()) {        return falsePathConditionWorlds(state);    }    if (state_module.checkLimits(state)) {        return inferenceErrorWorlds(state);    }    state.depth += 1;    defer state.depth -= 1;    try state_module.pushCallstack(state, strict_order_index);    defer state_module.popCallstack(state);    if (state.cfg.stacktrace) {        try state.stacktrace_buf.append(state.allocator, expr);    }    defer {        if (state.cfg.stacktrace and state.stacktrace_buf.items.len > 0) {            _ = state.stacktrace_buf.pop();        }    }    const result = try compileInner(expr, env, path_condition, state);    state.stats.num_forward_calls += 1;    state_module.maybeSampleBdd(state);    if (state_module.checkLimits(state)) {        return inferenceErrorWorlds(state);    }    return result;}pub fn compileInner(    expr: *PExpr,    env: Env,    path_condition: Bdd,    state: *LazyKCState,) CompileError!WorldsResult {    const allocator = state.allocator;    return switch (expr.head) {        .app => compileApp(expr, env, path_condition, state),        .abs => compileAbs(allocator, expr, env, path_condition, state),        .var_ref => |v| compileVar(allocator, v.name, env, path_condition, state),        .defined => |d| compileDefined(d.name, env, path_condition, state),        .construct => |c| compileConstruct(allocator, c.constructor, expr.args, env, path_condition, state),        .case_of => compileCaseOf(expr, env, path_condition, state),        .y_combinator => compileY(allocator, expr, env, path_condition, state),        .flip => compileFlip(allocator, expr, env, path_condition, state),        .factor => compileFactor(allocator, expr, env, path_condition, state),        .const_native => |n| compileConstNative(allocator, n, state),        .native_eq => compileNativeEq(allocator, expr, env, path_condition, state),        .get_args => compileGetArgs(allocator, expr, env, path_condition, state),        .get_constructor => compileGetConstructor(allocator, expr, env, path_condition, state),        .f_div => compileFloatBinop(allocator, .div, expr, env, path_condition, state),        .f_mul => compileFloatBinop(allocator, .mul, expr, env, path_condition, state),        .f_add => compileFloatBinop(allocator, .add, expr, env, path_condition, state),        .f_sub => compileFloatBinop(allocator, .sub, expr, env, path_condition, state),        .print_op => tracedCompileInner(expr.args[0], env, path_condition, state, 0),        .error_op => error.PluckError,        .pbool => compilePBool(allocator, expr, env, path_condition, state),        .mk_int => compileMkInt(allocator, expr, state),        .mk_int_weighted => compileMkIntWeighted(allocator, expr, env, path_condition, state),        .int_dist_eq => compileIntDistEq(allocator, expr, env, path_condition, state),        .get_config => compileGetConfig(allocator, state),        .type_def => error.InvalidExpression,    };}const CompileAppContinuation = struct {    pub fn cont(        alloc: Allocator,        f: *RuntimeValue,        inner_pc: Bdd,        s: *LazyKCState,        ctx: anytype,    ) !WorldsResult {        const thunk = ctx.thunk;        const orig_env = ctx.env;        _ = orig_env;        switch (f.data) {            .closure => |closure| {                const thunk_val = try RuntimeValue.initLazyKCThunk(alloc, thunk);                const new_env = try closure.env.extend(alloc, closure.name, thunk_val);                return switch (closure.expr) {                    .pexpr => |body| tracedCompileInner(body, new_env, inner_pc, s, 2),                    .thunk => error.NotImplemented,                };            },            else => return programErrorWorlds(s),        }    }};fn compileApp(    expr: *PExpr,    env: Env,    path_condition: Bdd,    state: *LazyKCState,) CompileError!WorldsResult {    const allocator = state.allocator;    const arg_thunk = try makeThunk(allocator, expr.args[1], env, 1, state);    const func_result = try tracedCompileInner(expr.args[0], env, path_condition, state, 0);    return bindMonad(        allocator,        func_result,        path_condition,        state,        CompileAppContinuation,        .{ .thunk = arg_thunk, .env = env },    );}fn compileAbs(    allocator: Allocator,    expr: *PExpr,    env: Env,    path_condition: Bdd,    state: *LazyKCState,) !WorldsResult {    _ = path_condition;    const var_name = expr.head.abs.var_name;    const closure = try Closure.init(allocator, expr.args[0], env, var_name);    const val = try RuntimeValue.initClosure(allocator, closure);    return pureMonad(allocator, val, state);}fn compileVar(    allocator: Allocator,    name: Symbol,    env: Env,    path_condition: Bdd,    state: *LazyKCState,) !WorldsResult {    const v = env.get(name) orelse return programErrorWorlds(state);    if (v.isThunk()) {        return evaluateThunk(allocator, v, path_condition, state);    }    return pureMonad(allocator, v, state);}fn compileDefined(    name: Symbol,    env: Env,    path_condition: Bdd,    state: *LazyKCState,) !WorldsResult {    const allocator = state.allocator;    if (env.get(name)) |v| {        if (v.isThunk()) {            return evaluateThunk(allocator, v, path_condition, state);        }        return pureMonad(allocator, v, state);    }    const def_expr = state.definitions.lookup(name) orelse return programErrorWorlds(state);    const saved_def = state.current_def_name;    state.current_def_name = name;    defer state.current_def_name = saved_def;    if (state.def_thunks.get(name)) |thunk_val| {        return evaluateThunk(allocator, thunk_val, path_condition, state);    }    var strict_index: i32 = 0;    if (state.cfg.definition_order) |order| {        if (order.getIndex(name)) |idx| {            strict_index = idx;        }    }    const thunk = try makeThunk(allocator, def_expr, Env.empty, strict_index, state);    const thunk_val = try RuntimeValue.initLazyKCThunk(allocator, thunk);    try state.def_thunks.put(allocator, name, thunk_val);    return evaluateThunk(allocator, thunk_val, path_condition, state);}fn compileConstruct(    allocator: Allocator,    constructor: Symbol,    args: []const *PExpr,    env: Env,    path_condition: Bdd,    state: *LazyKCState,) !WorldsResult {    _ = path_condition;    const thunked_args = try allocator.alloc(*RuntimeValue, args.len);    for (args, 0..) |arg, i| {        const thunk = try makeThunk(allocator, arg, env, @intCast(i + 1), state);        thunked_args[i] = try RuntimeValue.initLazyKCThunk(allocator, thunk);    }    const val = try RuntimeValue.initConstructed(allocator, constructor, thunked_args);    return pureMonad(allocator, val, state);}const CaseGuardMatchKind = enum {    constructor,    wildcard,    capture,};fn isWildcardCaseGuard(guard: pexpr.CaseOfGuard) bool {    return guard.args.len == 0 and std.mem.eql(u8, guard.constructor, "_");}fn isCaptureCaseGuard(guard: pexpr.CaseOfGuard) bool {    return guard.args.len == 0 and guard.constructor.len > 0 and !std.ascii.isUpper(guard.constructor[0]) and !std.mem.eql(u8, guard.constructor, "_");}const CompileCaseContinuation = struct {    pub fn cont(        alloc: Allocator,        scrutinee: *RuntimeValue,        inner_pc: Bdd,        s: *LazyKCState,        ctx: anytype,    ) !WorldsResult {        const e = ctx.expr;        const branches_inner = ctx.branches;        const orig_env = ctx.env;        const constructed = if (scrutinee.data == .constructed)            scrutinee.data.constructed        else            null;        var branch_idx: ?usize = null;        var match_kind: CaseGuardMatchKind = .constructor;        for (branches_inner, 0..) |guard, i| {            if (constructed) |c| {                if (std.mem.eql(u8, guard.constructor, c.constructor)) {                    branch_idx = i;                    match_kind = .constructor;                    break;                }            }            if (isWildcardCaseGuard(guard)) {                branch_idx = i;                match_kind = .wildcard;                break;            }            if (isCaptureCaseGuard(guard)) {                branch_idx = i;                match_kind = .capture;                break;            }        }        if (branch_idx == null) {            return programErrorWorlds(s);        }        const idx = branch_idx.?;        const guard = branches_inner[idx];        const case_expr = e.args[idx + 1];        var new_env = orig_env;        switch (match_kind) {            .constructor => {                const c = constructed orelse return programErrorWorlds(s);                if (guard.args.len != c.args.len) {                    return programErrorWorlds(s);                }                for (guard.args, c.args) |name, arg| {                    new_env = try new_env.extend(alloc, name, arg);                }            },            .wildcard => {},            .capture => {                new_env = try new_env.extend(alloc, guard.constructor, scrutinee);            },        }        return tracedCompileInner(case_expr, new_env, inner_pc, s, @intCast(idx + 1));    }};fn compileCaseOf(    expr: *PExpr,    env: Env,    path_condition: Bdd,    state: *LazyKCState,) !WorldsResult {    const allocator = state.allocator;    const branches = expr.head.case_of.branches;    const scrutinee_result = try tracedCompileInner(expr.args[0], env, path_condition, state, 0);    return bindMonad(        allocator,        scrutinee_result,        path_condition,        state,        CompileCaseContinuation,        .{ .expr = expr, .branches = branches, .env = env },    );}fn compileY(    allocator: Allocator,    expr: *PExpr,    env: Env,    path_condition: Bdd,    state: *LazyKCState,) !WorldsResult {    _ = path_condition;    const rec_lambda = expr.args[0];    if (rec_lambda.head != .abs) return programErrorWorlds(state);    const arg_lambda = rec_lambda.args[0];    if (arg_lambda.head != .abs) return programErrorWorlds(state);    const rec_name = rec_lambda.head.abs.var_name;    const arg_name = arg_lambda.head.abs.var_name;    const body = arg_lambda.args[0];    const closure = try Closure.makeSelfLoop(allocator, body, env, rec_name, arg_name);    const val = try RuntimeValue.initClosure(allocator, closure);    return pureMonad(allocator, val, state);}const FlipOutcome = union(enum) {    addr: Bdd,    sampled: bool,};fn sampleFlipForCallstack(    allocator: Allocator,    callstack: []const i32,    p: f64,    state: *LazyKCState,) CompileError!bool {    const lookup_key = LazyKCState.CallstackKey{        .callstack = callstack,        .prob = p,    };    if (state.sampled_flips.get(lookup_key)) |sampled_true| {        return sampled_true;    }    const random = state.prng.random();    const sampled_true = random.float(f64) < p;    const callstack_copy = try allocator.dupe(i32, callstack);    const store_key = LazyKCState.CallstackKey{        .callstack = callstack_copy,        .prob = p,    };    state.sampled_flips.put(allocator, store_key, sampled_true) catch {        allocator.free(callstack_copy);        return CompileError.OutOfMemory;    };    return sampled_true;}fn resolveFlipForCallstack(    allocator: Allocator,    callstack: []const i32,    p: f64,    state: *LazyKCState,) CompileError!FlipOutcome {    if (state.cfg.sample_after_max_depth) {        if (state.cfg.max_depth) |max| {            if (state.depth > max) {                const lookup_key = LazyKCState.CallstackKey{                    .callstack = callstack,                    .prob = p,                };                if (!state.var_of_callstack.contains(lookup_key)) {                    return .{ .sampled = try sampleFlipForCallstack(allocator, callstack, p, state) };                }            }        }    }    const addr = try state_module.currentAddressForCallstack(state, callstack, p);    return .{ .addr = addr };}const CompileFlipContinuation = struct {    pub fn cont(        alloc: Allocator,        p_val: *RuntimeValue,        inner_pc: Bdd,        s: *LazyKCState,        ctx: anytype,    ) CompileError!WorldsResult {        _ = ctx;        _ = inner_pc;        const p = switch (p_val.data) {            .native => |n| switch (n) {                .float => |f| f,                else => return programErrorWorlds(s),            },            else => return programErrorWorlds(s),        };        if (p < 0.0 or p > 1.0) {            return programErrorWorlds(s);        }        if (@abs(p) < 1e-10) {            const false_val = try RuntimeValue.initFalse(alloc);            return pureMonad(alloc, false_val, s);        }        if (@abs(p - 1.0) < 1e-10) {            const true_val = try RuntimeValue.initTrue(alloc);            return pureMonad(alloc, true_val, s);        }        try state_module.pushCallstack(s, 1);        defer state_module.popCallstack(s);        if (s.cfg.sample_constraint) |constraint| {            const addr = try state_module.currentAddress(s, p);            const implies_true = try s.manager.bddImplies(constraint, addr);            if (implies_true.isTrue()) {                const true_val = try RuntimeValue.initTrue(alloc);                return pureMonad(alloc, true_val, s);            }            const implies_false = try s.manager.bddImplies(constraint, addr.neg());            if (implies_false.isTrue()) {                const false_val = try RuntimeValue.initFalse(alloc);                return pureMonad(alloc, false_val, s);            }            const sampled_true = try sampleFlipForCallstack(alloc, s.callstack.items, p, s);            if (sampled_true) {                const true_val = try RuntimeValue.initTrue(alloc);                return pureMonad(alloc, true_val, s);            } else {                const false_val = try RuntimeValue.initFalse(alloc);                return pureMonad(alloc, false_val, s);            }        }        if (s.cfg.sample_after_max_depth) {            if (s.cfg.max_depth) |max| {                if (s.depth > max) {                    const lookup_key = LazyKCState.CallstackKey{                        .callstack = s.callstack.items,                        .prob = p,                    };                    if (!s.var_of_callstack.contains(lookup_key)) {                        const sampled_true = try sampleFlipForCallstack(                            alloc,                            s.callstack.items,                            p,                            s,                        );                        if (sampled_true) {                            const true_val = try RuntimeValue.initTrue(alloc);                            return pureMonad(alloc, true_val, s);                        } else {                            const false_val = try RuntimeValue.initFalse(alloc);                            return pureMonad(alloc, false_val, s);                        }                    }                }            }        }        const addr = try state_module.currentAddress(s, p);        const true_val = try RuntimeValue.initTrue(alloc);        const false_val = try RuntimeValue.initFalse(alloc);        const worlds = try alloc.alloc(World, 2);        worlds[0] = World{ .value = true_val, .guard = addr };        worlds[1] = World{ .value = false_val, .guard = addr.neg() };        return WorldsResult{            .worlds = worlds,            .validity_guard = Bdd.TRUE,        };    }};fn compileFlip(    allocator: Allocator,    expr: *PExpr,    env: Env,    path_condition: Bdd,    state: *LazyKCState,) !WorldsResult {    const prob_result = try tracedCompileInner(expr.args[0], env, path_condition, state, 0);    return bindMonad(allocator, prob_result, path_condition, state, CompileFlipContinuation, {});}fn collectFactorWeightWorlds(    allocator: Allocator,    out: *std.ArrayList(World),    value: *RuntimeValue,    guard: Bdd,    path_condition: Bdd,    state: *LazyKCState,) CompileError!void {    if (state.stats.limit_reason != null) return;    if (guard.isFalse()) return;    switch (value.data) {        .lazy_kc_thunk, .lazy_kc_thunk_union => {            const inner_pc = if (state.cfg.disable_path_conditions)                Bdd.TRUE            else                try state.manager.bddAnd(path_condition, guard);            const result = try evaluateThunk(allocator, value, inner_pc, state);            defer freeWorldsSlice(allocator, result.worlds);            for (result.worlds) |world| {                const combined_guard = try state.manager.bddAnd(guard, world.guard);                try collectFactorWeightWorlds(                    allocator,                    out,                    world.value,                    combined_guard,                    path_condition,                    state,                );                if (state.stats.limit_reason != null) return;            }        },        else => {            try out.append(allocator, World{ .value = value, .guard = guard });        },    }}const WeightSymbolicResult = union(enum) {    ok: []GuardedWeight,    fail,    invalid,};fn compileWeightSymbolic(    allocator: Allocator,    worlds: []const World,) CompileError!WeightSymbolicResult {    var guards: std.ArrayList(GuardedWeight) = .empty;    defer guards.deinit(allocator);    for (worlds) |world| {        if (world.guard.isFalse()) continue;        if (world.value.isThunk()) return .fail;        const weight = valueToFloat(world.value) orelse return .invalid;        if (!std.math.isFinite(weight) or weight < 0.0) return .invalid;        if (weight == 0.0) continue;        try guards.append(allocator, .{            .guard = world.guard,            .weight = weight,        });    }    const owned = try guards.toOwnedSlice(allocator);    return .{ .ok = owned };}fn emptyFactorResult(validity_guard: Bdd) WorldsResult {    return WorldsResult{        .worlds = &[_]World{},        .validity_guard = validity_guard,    };}fn unitFactorResult(allocator: Allocator, guard: Bdd, validity_guard: Bdd) CompileError!WorldsResult {    const unit_val = try RuntimeValue.initConstructed(allocator, "Unit", &[_]*RuntimeValue{});    const worlds = try allocator.alloc(World, 1);    worlds[0] = World{ .value = unit_val, .guard = guard };    return WorldsResult{        .worlds = worlds,        .validity_guard = validity_guard,    };}fn finishFactorFromGuardList(    allocator: Allocator,    guards: []const GuardedWeight,    validity_guard: Bdd,    state: *LazyKCState,) CompileError!WorldsResult {    var output: std.ArrayList(World) = .empty;    defer output.deinit(allocator);    const unit_val = try RuntimeValue.initConstructed(allocator, "Unit", &[_]*RuntimeValue{});    for (guards) |entry| {        if (entry.guard.isFalse()) continue;        if (entry.weight == 0.0) continue;        if (@abs(entry.weight - 1.0) < 1e-12) {            try output.append(allocator, World{ .value = unit_val, .guard = entry.guard });            continue;        }        const factor_var = try state.manager.newVar(true);        try state.wmc_params.setWeight(state.manager.topVar(factor_var), 1.0, entry.weight);        const combined_guard = try state.manager.bddAnd(entry.guard, factor_var);        try output.append(allocator, World{ .value = unit_val, .guard = combined_guard });    }    if (output.items.len == 0) {        return emptyFactorResult(validity_guard);    }    const worlds = try output.toOwnedSlice(allocator);    return WorldsResult{        .worlds = worlds,        .validity_guard = validity_guard,    };}fn deferFactorGuards(    allocator: Allocator,    guards: []const GuardedWeight,    path_condition: Bdd,    validity_guard: Bdd,    state: *LazyKCState,) CompileError!WorldsResult {    var restricted: std.ArrayList(GuardedWeight) = .empty;    defer restricted.deinit(allocator);    try restricted.ensureTotalCapacity(allocator, guards.len);    for (guards) |entry| {        if (entry.weight == 0.0) continue;        if (entry.guard.isFalse()) continue;        const combined_guard = if (state.cfg.disable_path_conditions)            entry.guard        else            state.manager.bddAnd(entry.guard, path_condition) catch return error.OutOfMemory;        if (combined_guard.isFalse()) continue;        try restricted.append(allocator, .{            .guard = combined_guard,            .weight = entry.weight,        });    }    const owned = try restricted.toOwnedSlice(allocator);    if (owned.len == 0) {        allocator.free(owned);        return emptyFactorResult(validity_guard);    }    try state.deferred_weights.append(allocator, .{ .guards = owned });    return unitFactorResult(allocator, Bdd.TRUE, validity_guard);}fn finishFactorWeightDdError(    err: weight_dd.ApplyError,    allocator: Allocator,    guards: []const GuardedWeight,    path_condition: Bdd,    validity_guard: Bdd,    state: *LazyKCState,) CompileError!WorldsResult {    return switch (err) {        error.OutOfMemory => error.OutOfMemory,        error.NodeLimitExceeded => deferFactorGuards(allocator, guards, path_condition, validity_guard, state),        error.NaNWeight, error.NonFiniteWeight => programErrorWorlds(state),    };}fn finishFactorWithWeightDD(    allocator: Allocator,    guards: []const GuardedWeight,    path_condition: Bdd,    validity_guard: Bdd,    state: *LazyKCState,) CompileError!WorldsResult {    const node_limit = if (state.cfg.inference_mode == .lpsmc) 0 else state.cfg.weight_dd_max_nodes;    const current_pc = if (state.cfg.disable_path_conditions) Bdd.TRUE else path_condition;    const refine_start = time.nanoTimestamp();    defer {        const elapsed = time.nanoTimestamp() - refine_start;        state.stats.refinement_time_ns += @intCast(@max(0, elapsed));        state.stats.refinement_count += 1;    }    const weight_root = state.weight_dd.refineWeight(guards, node_limit) catch |err| {        return finishFactorWeightDdError(err, allocator, guards, current_pc, validity_guard, state);    };    if (node_limit != 0) {        _ = state.weight_dd.nodeCountLimited(weight_root, node_limit) catch |err| {            return finishFactorWeightDdError(err, allocator, guards, current_pc, validity_guard, state);        };    }    const gated_weight = if (node_limit != 0)        state.weight_dd.iteLimited(current_pc, weight_root, state.weight_dd_one, node_limit) catch |err| {            return finishFactorWeightDdError(err, allocator, guards, current_pc, validity_guard, state);        }    else        state.weight_dd.ite(current_pc, weight_root, state.weight_dd_one) catch |err| {            return finishFactorWeightDdError(err, allocator, guards, current_pc, validity_guard, state);        };    const new_root = if (node_limit != 0)        state.weight_dd.mulLimited(state.weight_dd_root, gated_weight, node_limit) catch |err| {            return finishFactorWeightDdError(err, allocator, guards, current_pc, validity_guard, state);        }    else        state.weight_dd.mul(state.weight_dd_root, gated_weight) catch |err| {            return finishFactorWeightDdError(err, allocator, guards, current_pc, validity_guard, state);        };    state.weight_dd_root = new_root;    return unitFactorResult(allocator, Bdd.TRUE, validity_guard);}fn finishFactorFromGuards(    allocator: Allocator,    guards: []const GuardedWeight,    path_condition: Bdd,    validity_guard: Bdd,    state: *LazyKCState,) CompileError!WorldsResult {    if (guards.len == 0) {        return emptyFactorResult(validity_guard);    }    const use_weight_dd = state.cfg.factor_max_branches != 0 and        guards.len > state.cfg.factor_max_branches;    if (guards.len > state.stats.max_factor_guard_branches) {        state.stats.max_factor_guard_branches = guards.len;    }    if (!use_weight_dd) {        return finishFactorFromGuardList(allocator, guards, validity_guard, state);    }    return finishFactorWithWeightDD(allocator, guards, path_condition, validity_guard, state);}fn compileFactor(    allocator: Allocator,    expr: *PExpr,    env: Env,    path_condition: Bdd,    state: *LazyKCState,) CompileError!WorldsResult {    const weight_result = try tracedCompileInner(expr.args[0], env, path_condition, state, 0);    defer freeWorldsSlice(allocator, weight_result.worlds);    const validity_guard = weight_result.validity_guard;    if (state.stats.limit_reason != null) {        return inferenceErrorWorlds(state);    }    if (weight_result.worlds.len == 0) {        return WorldsResult{            .worlds = &[_]World{},            .validity_guard = validity_guard,        };    }    const symbolic = try compileWeightSymbolic(allocator, weight_result.worlds);    switch (symbolic) {        .ok => |guards| {            defer allocator.free(guards);            return finishFactorFromGuards(allocator, guards, path_condition, validity_guard, state);        },        .invalid => return programErrorWorlds(state),        .fail => {},    }    const fallback_limit = state.cfg.factor_max_branches;    if (fallback_limit != 0 and weight_result.worlds.len > fallback_limit) {        state.stats.limit_reason = .factor_weight_too_complex;        return inferenceErrorWorlds(state);    }    var weight_worlds: std.ArrayList(World) = .empty;    defer weight_worlds.deinit(allocator);    for (weight_result.worlds) |world| {        try collectFactorWeightWorlds(            allocator,            &weight_worlds,            world.value,            world.guard,            path_condition,            state,        );        if (state.stats.limit_reason != null) {            return inferenceErrorWorlds(state);        }    }    if (fallback_limit != 0 and weight_worlds.items.len > fallback_limit) {        state.stats.limit_reason = .factor_weight_too_complex;        return inferenceErrorWorlds(state);    }    const fallback = try compileWeightSymbolic(allocator, weight_worlds.items);    switch (fallback) {        .ok => |guards| {            defer allocator.free(guards);            return finishFactorFromGuards(allocator, guards, path_condition, validity_guard, state);        },        .invalid => return programErrorWorlds(state),        .fail => {            state.stats.limit_reason = .factor_weight_too_complex;            return inferenceErrorWorlds(state);        },    }}fn compileConstNative(    allocator: Allocator,    native: pexpr.NativeValue,    state: *LazyKCState,) !WorldsResult {    const data: runtime.NativeValueData = switch (native) {        .int => |i| .{ .int = i },        .float => |f| .{ .float = f },        .symbol => |s| .{ .symbol = s },        .bool_val => |b| .{ .bool_val = b },    };    const val = try RuntimeValue.initNative(allocator, data);    return pureMonad(allocator, val, state);}const NativeEqSecondContinuation = struct {    pub fn cont(        inner_alloc: Allocator,        arg2: *RuntimeValue,        _: Bdd,        inner_s: *LazyKCState,        inner_ctx: anytype,    ) CompileError!WorldsResult {        const a1 = inner_ctx.arg1;        const eq = if (a1.data == .native and arg2.data == .native)            a1.data.native.eql(arg2.data.native)        else            false;        const result_val = if (eq)            try RuntimeValue.initTrue(inner_alloc)        else            try RuntimeValue.initFalse(inner_alloc);        return pureMonad(inner_alloc, result_val, inner_s);    }};const NativeEqFirstContinuation = struct {    pub fn cont(        alloc: Allocator,        arg1: *RuntimeValue,        inner_pc: Bdd,        s: *LazyKCState,        ctx: anytype,    ) CompileError!WorldsResult {        const result2 = try tracedCompileInner(ctx.expr.args[1], ctx.env, inner_pc, s, 1);        return bindMonad(            alloc,            result2,            inner_pc,            s,            NativeEqSecondContinuation,            .{ .arg1 = arg1 },        );    }};fn compileNativeEq(    allocator: Allocator,    expr: *PExpr,    env: Env,    path_condition: Bdd,    state: *LazyKCState,) !WorldsResult {    const result1 = try tracedCompileInner(expr.args[0], env, path_condition, state, 0);    return bindMonad(        allocator,        result1,        path_condition,        state,        NativeEqFirstContinuation,        .{ .expr = expr, .env = env },    );}const FloatBinopType = enum { div, mul, add, sub };fn valueToFloat(val: *RuntimeValue) ?f64 {    return switch (val.data) {        .native => |n| switch (n) {            .float => |f| f,            .int => |i| @as(f64, @floatFromInt(i)),            else => null,        },        .constructed => {            if (val.maybeNat()) |nat_val| {                return @as(f64, @floatFromInt(nat_val));            }            return null;        },        else => null,    };}const FloatBinopSecondContinuation = struct {    pub fn cont(        inner_alloc: Allocator,        arg2: *RuntimeValue,        _: Bdd,        inner_s: *LazyKCState,        inner_ctx: anytype,    ) CompileError!WorldsResult {        const a1 = inner_ctx.arg1;        const operation = inner_ctx.op;        const v1 = valueToFloat(a1) orelse return programErrorWorlds(inner_s);        const v2 = valueToFloat(arg2) orelse return programErrorWorlds(inner_s);        const result_f = switch (operation) {            .div => v1 / v2,            .mul => v1 * v2,            .add => v1 + v2,            .sub => v1 - v2,        };        const val = try RuntimeValue.initNative(inner_alloc, .{ .float = result_f });        return pureMonad(inner_alloc, val, inner_s);    }};const FloatBinopFirstContinuation = struct {    pub fn cont(        alloc: Allocator,        arg1: *RuntimeValue,        inner_pc: Bdd,        s: *LazyKCState,        ctx: anytype,    ) CompileError!WorldsResult {        const result2 = try tracedCompileInner(ctx.expr.args[1], ctx.env, inner_pc, s, 1);        return bindMonad(            alloc,            result2,            inner_pc,            s,            FloatBinopSecondContinuation,            .{ .arg1 = arg1, .op = ctx.op },        );    }};fn compileFloatBinop(    allocator: Allocator,    op: FloatBinopType,    expr: *PExpr,    env: Env,    path_condition: Bdd,    state: *LazyKCState,) !WorldsResult {    const result1 = try tracedCompileInner(expr.args[0], env, path_condition, state, 0);    return bindMonad(        allocator,        result1,        path_condition,        state,        FloatBinopFirstContinuation,        .{ .expr = expr, .env = env, .op = op },    );}const GetArgsContinuation = struct {    pub fn cont(        alloc: Allocator,        val: *RuntimeValue,        _: Bdd,        s: *LazyKCState,        ctx: anytype,    ) !WorldsResult {        _ = ctx;        if (val.data != .constructed) {            return programErrorWorlds(s);        }        const c = val.data.constructed;        var list = try RuntimeValue.initConstructed(alloc, "Nil", &[_]*RuntimeValue{});        var i = c.args.len;        while (i > 0) {            i -= 1;            const args_slice = try alloc.alloc(*RuntimeValue, 2);            args_slice[0] = c.args[i];            args_slice[1] = list;            list = try RuntimeValue.initConstructed(alloc, "Cons", args_slice);        }        return pureMonad(alloc, list, s);    }};fn compileGetArgs(    allocator: Allocator,    expr: *PExpr,    env: Env,    path_condition: Bdd,    state: *LazyKCState,) !WorldsResult {    const result = try tracedCompileInner(expr.args[0], env, path_condition, state, 0);    return bindMonad(allocator, result, path_condition, state, GetArgsContinuation, {});}const GetConstructorContinuation = struct {    pub fn cont(        alloc: Allocator,        val: *RuntimeValue,        _: Bdd,        s: *LazyKCState,        ctx: anytype,    ) !WorldsResult {        _ = ctx;        if (val.data != .constructed) {            return programErrorWorlds(s);        }        const c = val.data.constructed;        const sym_val = try RuntimeValue.initNative(alloc, .{ .symbol = c.constructor });        return pureMonad(alloc, sym_val, s);    }};fn compileGetConstructor(    allocator: Allocator,    expr: *PExpr,    env: Env,    path_condition: Bdd,    state: *LazyKCState,) !WorldsResult {    const result = try tracedCompileInner(expr.args[0], env, path_condition, state, 0);    return bindMonad(allocator, result, path_condition, state, GetConstructorContinuation, {});}fn logAddExp(a: f64, b: f64) f64 {    if (a == -std.math.inf(f64)) return b;    if (b == -std.math.inf(f64)) return a;    if (a > b) {        return a + @log(1.0 + @exp(b - a));    } else {        return b + @log(1.0 + @exp(a - b));    }}const PBoolContinuation = struct {    pub fn cont(        alloc: Allocator,        cond_val: *RuntimeValue,        _: Bdd,        s: *LazyKCState,        ctx: anytype,    ) CompileError!WorldsResult {        const p_t = ctx;        if (cond_val.data != .constructed) {            return programErrorWorlds(s);        }        const c = cond_val.data.constructed;        const prob_val = try RuntimeValue.initNative(alloc, .{ .float = p_t });        const args = try alloc.alloc(*RuntimeValue, 2);        args[0] = prob_val;        args[1] = cond_val;        if (std.mem.eql(u8, c.constructor, "True") or std.mem.eql(u8, c.constructor, "False")) {            const pbool_val = try RuntimeValue.initConstructed(alloc, "PBool", args);            return pureMonad(alloc, pbool_val, s);        }        return programErrorWorlds(s);    }};fn compilePBool(    allocator: Allocator,    expr: *PExpr,    env: Env,    path_condition: Bdd,    state: *LazyKCState,) !WorldsResult {    const cond_result = try tracedCompileInner(expr.args[0], env, path_condition, state, 0);    var log_p_true: f64 = -std.math.inf(f64);    var log_p_false: f64 = -std.math.inf(f64);    for (cond_result.worlds) |world| {        if (world.value.data != .constructed) continue;        const c = world.value.data.constructed;        const prob = wmcForState(state, world.guard) catch |err| switch (err) {            error.OutOfMemory => return error.OutOfMemory,            error.NodeLimitExceeded => {                state.stats.limit_reason = .factor_weight_too_complex;                freeWorldsSlice(allocator, cond_result.worlds);                return inferenceErrorWorlds(state);            },        };        const log_prob = if (prob > 0) @log(prob) else -std.math.inf(f64);        if (std.mem.eql(u8, c.constructor, "True")) {            log_p_true = logAddExp(log_p_true, log_prob);        } else if (std.mem.eql(u8, c.constructor, "False")) {            log_p_false = logAddExp(log_p_false, log_prob);        }    }    const log_total = logAddExp(log_p_true, log_p_false);    const prob_true: f64 = if (log_total > -std.math.inf(f64))        @exp(log_p_true - log_total)    else        0.0;    return bindMonad(allocator, cond_result, path_condition, state, PBoolContinuation, prob_true);}fn compileMkInt(    allocator: Allocator,    expr: *PExpr,    state: *LazyKCState,) !WorldsResult {    const bitwidth_expr = expr.args[0];    const val_expr = expr.args[1];    const bitwidth: u6 = switch (bitwidth_expr.head) {        .const_native => |n| switch (n) {            .int => |i| @intCast(i),            else => return programErrorWorlds(state),        },        else => return programErrorWorlds(state),    };    const value: u64 = switch (val_expr.head) {        .const_native => |n| switch (n) {            .int => |i| @bitCast(i),            else => return programErrorWorlds(state),        },        else => return programErrorWorlds(state),    };    const bits = try allocator.alloc(Bdd, bitwidth);    for (0..bitwidth) |i| {        const bit_set = (value >> @intCast(i)) & 1 == 1;        bits[i] = if (bit_set) Bdd.TRUE else Bdd.FALSE;    }    const int_dist = runtime.IntDist.init(bits);    const val = try RuntimeValue.initNative(allocator, .{ .int_dist = int_dist });    return pureMonad(allocator, val, state);}const MkIntWeightedContinuation = struct {    pub fn cont(        alloc: Allocator,        list_val: *RuntimeValue,        inner_pc: Bdd,        s: *LazyKCState,        ctx: anytype,    ) CompileError!WorldsResult {        _ = inner_pc;        const bw = ctx.bitwidth;        const pairs = try extractListForcingThunks(alloc, list_val, s) orelse            return programErrorWorlds(s);        defer alloc.free(pairs);        if (pairs.len == 0) {            return programErrorWorlds(s);        }        var values = try alloc.alloc(u64, pairs.len);        defer alloc.free(values);        var probs = try alloc.alloc(f64, pairs.len);        defer alloc.free(probs);        for (pairs, 0..) |pair, i| {            const forced_pair = try forceValue(alloc, pair, s);            const p = forced_pair.maybePair() orelse return programErrorWorlds(s);            const forced_fst = try forceValue(alloc, p.fst, s);            values[i] = switch (forced_fst.data) {                .native => |n| switch (n) {                    .int => |iv| @bitCast(iv),                    else => return programErrorWorlds(s),                },                else => return programErrorWorlds(s),            };            const forced_snd = try forceValue(alloc, p.snd, s);            probs[i] = switch (forced_snd.data) {                .native => |n| switch (n) {                    .float => |f| f,                    else => return programErrorWorlds(s),                },                else => return programErrorWorlds(s),            };        }        return createWeightedIntDist(alloc, bw, values, probs, s);    }};fn compileMkIntWeighted(    allocator: Allocator,    expr: *PExpr,    env: Env,    path_condition: Bdd,    state: *LazyKCState,) !WorldsResult {    const bitwidth_expr = expr.args[0];    const bitwidth: u6 = switch (bitwidth_expr.head) {        .const_native => |n| switch (n) {            .int => |i| @intCast(i),            else => return programErrorWorlds(state),        },        else => return programErrorWorlds(state),    };    const list_result = try tracedCompileInner(expr.args[1], env, path_condition, state, 0);    return bindMonad(        allocator,        list_result,        path_condition,        state,        MkIntWeightedContinuation,        .{ .bitwidth = bitwidth },    );}pub fn forceValue(    allocator: Allocator,    val: *RuntimeValue,    state: *LazyKCState,) !*RuntimeValue {    switch (val.data) {        .lazy_kc_thunk, .lazy_kc_thunk_union => {            const result = try evaluateThunk(allocator, val, Bdd.TRUE, state);            defer freeWorldsSlice(allocator, result.worlds);            if (result.worlds.len == 0) return error.PluckError;            return result.worlds[0].value;        },        else => return val,    }}pub fn forceValueDeterministic(    allocator: Allocator,    val: *RuntimeValue,    state: *LazyKCState,) !*RuntimeValue {    switch (val.data) {        .lazy_kc_thunk, .lazy_kc_thunk_union => {            const result = try evaluateThunk(allocator, val, Bdd.TRUE, state);            defer freeWorldsSlice(allocator, result.worlds);            if (result.worlds.len != 1) return error.PluckError;            return result.worlds[0].value;        },        else => return val,    }}fn extractListForcingThunks(    allocator: Allocator,    val: *RuntimeValue,    state: *LazyKCState,) !?[]*RuntimeValue {    var items: std.ArrayList(*RuntimeValue) = .empty;    errdefer items.deinit(allocator);    var current = val;    while (true) {        current = try forceValue(allocator, current, state);        switch (current.data) {            .constructed => |c| {                if (std.mem.eql(u8, c.constructor, "Nil") and c.args.len == 0) {                    const slice = try items.toOwnedSlice(allocator);                    return slice;                } else if (std.mem.eql(u8, c.constructor, "Cons") and c.args.len == 2) {                    const head = try forceValue(allocator, c.args[0], state);                    try items.append(allocator, head);                    current = c.args[1];                } else {                    items.deinit(allocator);                    return null;                }            },            else => {                items.deinit(allocator);                return null;            },        }    }}pub fn extractNatForcingThunks(    allocator: Allocator,    val: *RuntimeValue,    max_value: i64,    state: *LazyKCState,) !?i64 {    var current = val;    var count: i64 = 0;    while (true) {        current = try forceValueDeterministic(allocator, current, state);        switch (current.data) {            .constructed => |c| {                if (std.mem.eql(u8, c.constructor, "O") and c.args.len == 0) {                    return count;                } else if (std.mem.eql(u8, c.constructor, "S") and c.args.len == 1) {                    count += 1;                    if (count > max_value) {                        return null;                    }                    current = c.args[0];                } else {                    return null;                }            },            else => return null,        }    }}fn createWeightedIntDist(    allocator: Allocator,    bitwidth: u6,    values: []const u64,    probs: []const f64,    state: *LazyKCState,) !WorldsResult {    const n = values.len;    var total_prob: f64 = 0;    for (probs) |p| {        if (p < 0 or p != p or std.math.isInf(p)) {            return programErrorWorlds(state);        }        total_prob += p;    }    if (total_prob <= 0 or @abs(total_prob - 1.0) > 1e-6) {        if (total_prob <= 0) {            return programErrorWorlds(state);        }    }    const norm_probs = try allocator.alloc(f64, n);    defer allocator.free(norm_probs);    for (probs, 0..) |p, i| {        norm_probs[i] = p / total_prob;    }    if (n == 1) {        const bits = try allocator.alloc(Bdd, bitwidth);        for (0..bitwidth) |i| {            const bit_set = (values[0] >> @intCast(i)) & 1 == 1;            bits[i] = if (bit_set) Bdd.TRUE else Bdd.FALSE;        }        const int_dist = runtime.IntDist.init(bits);        const val = try RuntimeValue.initNative(allocator, .{ .int_dist = int_dist });        return pureMonad(allocator, val, state);    }    const selector_guards = try allocator.alloc(Bdd, n);    defer allocator.free(selector_guards);    var remaining_prob: f64 = 1.0;    var not_selected_yet = Bdd.TRUE;    for (0..n) |i| {        try state_module.pushCallstack(state, @intCast(i + 100));        defer state_module.popCallstack(state);        if (i == n - 1) {            selector_guards[i] = not_selected_yet;        } else {            const cond_prob: f64 = if (remaining_prob > 1e-10)                norm_probs[i] / remaining_prob            else                0.5;            const flip_var = try state_module.currentAddress(state, cond_prob);            selector_guards[i] = try state.manager.bddAnd(not_selected_yet, flip_var);            not_selected_yet = try state.manager.bddAnd(not_selected_yet, flip_var.neg());            remaining_prob -= norm_probs[i];        }    }    const bits = try allocator.alloc(Bdd, bitwidth);    for (0..bitwidth) |bit_pos| {        var bit_bdd = Bdd.FALSE;        for (values, 0..) |value, val_idx| {            const bit_set = (value >> @intCast(bit_pos)) & 1 == 1;            if (bit_set) {                bit_bdd = try state.manager.bddOr(bit_bdd, selector_guards[val_idx]);            }        }        bits[bit_pos] = bit_bdd;    }    const int_dist = runtime.IntDist.init(bits);    const val = try RuntimeValue.initNative(allocator, .{ .int_dist = int_dist });    return pureMonad(allocator, val, state);}const IntDistEqSecondContinuation = struct {    pub fn cont(        inner_alloc: Allocator,        second_val: *RuntimeValue,        _: Bdd,        inner_state: *LazyKCState,        inner_ctx: anytype,    ) CompileError!WorldsResult {        const second_int_dist = switch (second_val.data) {            .native => |n| switch (n) {                .int_dist => |d| d,                else => return programErrorWorlds(inner_state),            },            else => return programErrorWorlds(inner_state),        };        const eq_bdd = try inner_ctx.first_dist.eql(second_int_dist, inner_state.manager);        const true_val = try RuntimeValue.initTrue(inner_alloc);        const false_val = try RuntimeValue.initFalse(inner_alloc);        return ifThenElseMonad(inner_alloc, true_val, false_val, eq_bdd, inner_state);    }};const IntDistEqFirstContinuation = struct {    pub fn cont(        alloc: Allocator,        first_val: *RuntimeValue,        inner_pc: Bdd,        s: *LazyKCState,        ctx: anytype,    ) CompileError!WorldsResult {        const first_int_dist = switch (first_val.data) {            .native => |n| switch (n) {                .int_dist => |d| d,                else => return programErrorWorlds(s),            },            else => return programErrorWorlds(s),        };        const second_result = try tracedCompileInner(ctx.expr.args[1], ctx.env, inner_pc, s, 1);        return bindMonad(            alloc,            second_result,            inner_pc,            s,            IntDistEqSecondContinuation,            .{ .first_dist = first_int_dist },        );    }};fn compileIntDistEq(    allocator: Allocator,    expr: *PExpr,    env: Env,    path_condition: Bdd,    state: *LazyKCState,) !WorldsResult {    const first_result = try tracedCompileInner(expr.args[0], env, path_condition, state, 0);    return bindMonad(        allocator,        first_result,        path_condition,        state,        IntDistEqFirstContinuation,        .{ .expr = expr, .env = env },    );}fn makePair(allocator: Allocator, key: *RuntimeValue, value: *RuntimeValue) !*RuntimeValue {    const args = try allocator.alloc(*RuntimeValue, 2);    args[0] = key;    args[1] = value;    return RuntimeValue.initConstructed(allocator, "Pair", args);}fn makeOptionalInt(allocator: Allocator, maybe_val: ?u64) !*RuntimeValue {    if (maybe_val) |val| {        const inner = try RuntimeValue.initNative(allocator, .{ .int = @intCast(val) });        const args = try allocator.alloc(*RuntimeValue, 1);        args[0] = inner;        return RuntimeValue.initConstructed(allocator, "Some", args);    } else {        return RuntimeValue.initConstructed(allocator, "None", &[_]*RuntimeValue{});    }}fn makeOptionalFloat(allocator: Allocator, maybe_val: ?f64) !*RuntimeValue {    if (maybe_val) |val| {        const inner = try RuntimeValue.initNative(allocator, .{ .float = val });        const args = try allocator.alloc(*RuntimeValue, 1);        args[0] = inner;        return RuntimeValue.initConstructed(allocator, "Some", args);    } else {        return RuntimeValue.initConstructed(allocator, "None", &[_]*RuntimeValue{});    }}const ConfigPrepender = struct {    fn prepend(        alloc: Allocator,        l: *RuntimeValue,        key_str: []const u8,        val: *RuntimeValue,    ) !*RuntimeValue {        const key = try RuntimeValue.initNative(alloc, .{ .symbol = key_str });        const pair = try makePair(alloc, key, val);        const cons_args = try alloc.alloc(*RuntimeValue, 2);        cons_args[0] = pair;        cons_args[1] = l;        return RuntimeValue.initConstructed(alloc, "Cons", cons_args);    }};fn compileGetConfig(    allocator: Allocator,    state: *LazyKCState,) !WorldsResult {    const cfg = state.cfg;    var list = try RuntimeValue.initConstructed(allocator, "Nil", &[_]*RuntimeValue{});    const parallel_wmc_val = if (cfg.parallel_wmc)        try RuntimeValue.initTrue(allocator)    else        try RuntimeValue.initFalse(allocator);    list = try ConfigPrepender.prepend(allocator, list, "parallel_wmc", parallel_wmc_val);    const use_strict_order_val = if (cfg.use_strict_order)        try RuntimeValue.initTrue(allocator)    else        try RuntimeValue.initFalse(allocator);    list = try ConfigPrepender.prepend(allocator, list, "use_strict_order", use_strict_order_val);    const use_reverse_order_val = if (cfg.use_reverse_order)        try RuntimeValue.initTrue(allocator)    else        try RuntimeValue.initFalse(allocator);    list = try ConfigPrepender.prepend(allocator, list, "use_reverse_order", use_reverse_order_val);    const ite_limit_val = try makeOptionalInt(allocator, cfg.ite_limit);    list = try ConfigPrepender.prepend(allocator, list, "ite_limit", ite_limit_val);    const time_limit_val = try makeOptionalFloat(allocator, cfg.time_limit);    list = try ConfigPrepender.prepend(allocator, list, "time_limit", time_limit_val);    const sample_after_max_depth_val = if (cfg.sample_after_max_depth)        try RuntimeValue.initTrue(allocator)    else        try RuntimeValue.initFalse(allocator);    list = try ConfigPrepender.prepend(        allocator,        list,        "sample_after_max_depth",        sample_after_max_depth_val,    );    const max_depth_u64: ?u64 = if (cfg.max_depth) |d| @as(u64, d) else null;    const max_depth_val = try makeOptionalInt(allocator, max_depth_u64);    list = try ConfigPrepender.prepend(allocator, list, "max_depth", max_depth_val);    return pureMonad(allocator, list, state);}pub fn makeThunk(    allocator: Allocator,    expr: *PExpr,    env: Env,    strict_order_index: i32,    state: *LazyKCState,) !*LazyKCThunk {    if (expr.head == .var_ref) {        if (env.get(expr.head.var_ref.name)) |v| {            if (v.data == .lazy_kc_thunk) {                state.stats.thunk_reuse_hits += 1;                return v.data.lazy_kc_thunk;            }        }    }    state.stats.thunk_reuse_misses += 1;    const thunk = try LazyKCThunk.init(        allocator,        expr,        env,        strict_order_index,        state.callstack.items,    );    if (state.registry) |registry| {        try registry.registerWithContext(thunk, expr, state.callstack.items, state.current_def_name);    }    return thunk;}pub fn evaluateThunk(    allocator: Allocator,    val: *RuntimeValue,    path_condition: Bdd,    state: *LazyKCState,) !WorldsResult {    switch (val.data) {        .lazy_kc_thunk => |thunk| return evaluateLazyKCThunk(allocator, thunk, path_condition, state),        .lazy_kc_thunk_union => |union_thunk| return evaluateThunkUnion(allocator, union_thunk, path_condition, state),        else => return pureMonad(allocator, val, state),    }}const ThunkCacheContinuation = struct {    pub fn cont(        alloc: Allocator,        hit_cache: *RuntimeValue,        inner_pc: Bdd,        s: *LazyKCState,        ctx: anytype,    ) CompileError!WorldsResult {        const t = ctx.thunk;        const cached_result = ctx.cached;        const is_true = switch (hit_cache.data) {            .constructed => |c| std.mem.eql(u8, c.constructor, "True"),            else => false,        };        if (is_true) {            const worlds_copy = try alloc.alloc(World, cached_result.worlds.len);            @memcpy(worlds_copy, cached_result.worlds);            return WorldsResult{                .worlds = worlds_copy,                .validity_guard = cached_result.validity_guard,            };        } else {            return evaluateThunkNoCache(alloc, t, inner_pc, s);        }    }};fn evaluateLazyKCThunk(    allocator: Allocator,    thunk: *LazyKCThunk,    path_condition: Bdd,    state: *LazyKCState,) !WorldsResult {    state.stats.thunk_evaluations += 1;    if (path_condition.isFalse()) {        return falsePathConditionWorlds(state);    }    if (state.cfg.sample_constraint != null) {        return evaluateThunkNoCache(allocator, thunk, path_condition, state);    }    for (thunk.cache.items) |cached| {        const cache_valid_for_path = try state.manager.bddImplies(path_condition, cached.validity_guard);        if (cache_valid_for_path.isTrue()) {            state.stats.thunk_cache_hits += 1;            const worlds_copy = try allocator.alloc(World, cached.worlds.len);            @memcpy(worlds_copy, cached.worlds);            return WorldsResult{                .worlds = worlds_copy,                .validity_guard = cached.validity_guard,            };        }    }    if (thunk.cache.items.len == 0) {        const result = try evaluateThunkNoCache(allocator, thunk, path_condition, state);        const cache_copy = try allocator.alloc(World, result.worlds.len);        @memcpy(cache_copy, result.worlds);        try thunk.cache.append(allocator, GuardedWorlds{            .worlds = cache_copy,            .validity_guard = result.validity_guard,        });        return result;    }    const cached = thunk.cache.items[0];    const cached_validity_guard = cached.validity_guard;    const true_val = try RuntimeValue.initTrue(allocator);    defer true_val.deinit(allocator);    const false_val = try RuntimeValue.initFalse(allocator);    defer false_val.deinit(allocator);    const hit_cache_worlds = try ifThenElseMonad(allocator, true_val, false_val, cached_validity_guard, state);    const extended_pc = try state.manager.bddOr(path_condition, cached_validity_guard);    const result = try bindMonad(        allocator,        hit_cache_worlds,        extended_pc,        state,        ThunkCacheContinuation,        .{ .thunk = thunk, .cached = cached },    );    freeWorldsSlice(allocator, thunk.cache.items[0].worlds);    const cache_copy = try allocator.alloc(World, result.worlds.len);    @memcpy(cache_copy, result.worlds);    thunk.cache.items[0] = GuardedWorlds{        .worlds = cache_copy,        .validity_guard = result.validity_guard,    };    return result;}fn evaluateThunkNoCache(    allocator: Allocator,    thunk: *LazyKCThunk,    path_condition: Bdd,    state: *LazyKCState,) CompileError!WorldsResult {    const old_callstack = try state.allocator.dupe(i32, state.callstack.items);    defer state.allocator.free(old_callstack);    state.callstack.clearRetainingCapacity();    try state.callstack.appendSlice(state.allocator, thunk.callstack);    defer {        state.callstack.clearRetainingCapacity();        state.callstack.appendSlice(state.allocator, old_callstack) catch {};    }    return switch (thunk.expr) {        .pexpr => |e| tracedCompileInner(e, thunk.env, path_condition, state, thunk.strict_order_index),        .thunk => |inner| evaluateLazyKCThunk(allocator, inner, path_condition, state),    };}fn evaluateThunkUnion(    allocator: Allocator,    union_thunk: *LazyKCThunkUnion,    path_condition: Bdd,    state: *LazyKCState,) !WorldsResult {    if (path_condition.isFalse()) {        return falsePathConditionWorlds(state);    }    var all_worlds: std.ArrayList(World) = .empty;    defer all_worlds.deinit(allocator);    var overall_validity_guard = Bdd.TRUE;    for (union_thunk.thunks) |tg| {        const inner_pc = try state.manager.bddAnd(path_condition, tg.guard);        if (inner_pc.isFalse()) {            continue;        }        const result = try evaluateLazyKCThunk(allocator, tg.thunk, inner_pc, state);        defer freeWorldsSlice(allocator, result.worlds);        for (result.worlds) |world| {            const combined_guard = try state.manager.bddAnd(world.guard, tg.guard);            try all_worlds.append(allocator, World{ .value = world.value, .guard = combined_guard });        }        if (!state.cfg.disable_validity_tracking) {            const branch_validity_guard = try state.manager.bddImplies(tg.guard, result.validity_guard);            overall_validity_guard = try state.manager.bddAnd(overall_validity_guard, branch_validity_guard);        }    }    const worlds = try all_worlds.toOwnedSlice(allocator);    return WorldsResult{        .worlds = worlds,        .validity_guard = overall_validity_guard,    };}pub const CompileResult = struct {    weighted_results: []WeightedResult,    stats: LazyKCStats,    raw_worlds: ?[]World,};fn computeWmcDeferredSequential(    allocator: Allocator,    worlds: []const World,    state: *LazyKCState,) DeferredWmcError![]WeightedResult {    var weighted_results = try allocator.alloc(WeightedResult, worlds.len);    errdefer allocator.free(weighted_results);    var caches = DeferredWmcCaches.init(allocator);    defer caches.deinit();    for (worlds, 0..) |world, i| {        const prob = try wmcWithDeferredCached(state, world.guard, &caches);        weighted_results[i] = WeightedResult{            .value = world.value,            .probability = prob,        };    }    return weighted_results;}pub fn compile(    allocator: Allocator,    cache_allocator: Allocator,    expr: *PExpr,    definitions: *const Definitions,    manager: *Manager,    cfg: LazyKCConfig,) !CompileResult {    const start_time = time.nanoTimestamp();    var state = try state_module.initChecked(allocator, manager, definitions, cfg);    defer state_module.deinit(&state);    state.query = expr;    state_module.startTimeLimit(&state);    defer state_module.stopTimeLimit(&state);    const worlds_result = tracedCompileInner(expr, Env.empty, Bdd.TRUE, &state, 0) catch |err| switch (err) {        error.PluckError => {            state.stats.program_error = true;            state.stats.time_ns = @intCast(@max(0, time.nanoTimestamp() - start_time));            return CompileResult{                .weighted_results = &[_]WeightedResult{},                .stats = state.stats,                .raw_worlds = null,            };        },        else => return err,    };    if (state.stats.limit_reason != null) {        freeWorldsSlice(allocator, worlds_result.worlds);        state.stats.time_ns = @intCast(@max(0, time.nanoTimestamp() - start_time));        return CompileResult{            .weighted_results = &[_]WeightedResult{},            .stats = state.stats,            .raw_worlds = null,        };    }    var worlds = worlds_result.worlds;    if (cfg.full_dist) {        const new_worlds = try inferFullDistribution(allocator, worlds, &state);        freeWorldsSlice(allocator, worlds);        worlds = new_worlds;    }    if (try processIntDistWorlds(allocator, worlds, manager)) |int_worlds| {        freeWorldsSlice(allocator, worlds);        worlds = int_worlds;    }    const has_deferred = state.deferred_weights.items.len > 0;    const use_parallel = !has_deferred and cfg.parallel_wmc and worlds.len >= cfg.parallel_wmc_threshold;    const wmc_start = time.nanoTimestamp();    defer {        const elapsed = time.nanoTimestamp() - wmc_start;        state.stats.wmc_time_ns = @intCast(@max(0, elapsed));    }    const weighted_results = if (has_deferred)        computeWmcDeferredSequential(allocator, worlds, &state) catch |err| switch (err) {            error.OutOfMemory => return error.OutOfMemory,            error.NodeLimitExceeded => {                state.stats.limit_reason = .factor_weight_too_complex;                freeWorldsSlice(allocator, worlds);                state.stats.time_ns = @intCast(@max(0, time.nanoTimestamp() - start_time));                return CompileResult{                    .weighted_results = &[_]WeightedResult{},                    .stats = state.stats,                    .raw_worlds = null,                };            },        }    else if (use_parallel)        try computeWmcParallel(            allocator,            cache_allocator,            worlds,            &state.wmc_params,            &state.weight_dd,            state.weight_dd_root,            cfg.parallel_wmc_threads,        )    else        try computeWmcSequential(            allocator,            worlds,            &state.wmc_params,            &state.weight_dd,            state.weight_dd_root,        );    state_module.recordFinalBddSample(&state);    state_module.recordManagerStats(&state);    state.stats.time_ns = @intCast(@max(0, time.nanoTimestamp() - start_time));    return CompileResult{        .weighted_results = weighted_results,        .stats = state.stats,        .raw_worlds = worlds,    };}pub fn processPosteriorQuery(    allocator: Allocator,    query_thunk: *RuntimeValue,    evidence_thunk: *RuntimeValue,    state: *LazyKCState,) ![]World {    const evidence_result = try evaluateThunk(allocator, evidence_thunk, Bdd.TRUE, state);    defer freeWorldsSlice(allocator, evidence_result.worlds);    var query_worlds: std.ArrayList(World) = .empty;    errdefer {        for (query_worlds.items) |w| {            _ = w;        }        query_worlds.deinit(allocator);    }    for (evidence_result.worlds) |evidence_world| {        if (evidence_world.value.data == .constructed) {            const c = evidence_world.value.data.constructed;            if (std.mem.eql(u8, c.constructor, "True") and c.args.len == 0) {                const query_result = try evaluateThunk(                    allocator,                    query_thunk,                    evidence_world.guard,                    state,                );                defer freeWorldsSlice(allocator, query_result.worlds);                for (query_result.worlds) |query_world| {                    const combined_guard = try state.manager.bddAnd(evidence_world.guard, query_world.guard);                    if (!combined_guard.isFalse()) {                        try query_worlds.append(allocator, World{                            .value = query_world.value,                            .guard = combined_guard,                        });                    }                }            }        }    }    const initial_worlds = try query_worlds.toOwnedSlice(allocator);    defer allocator.free(initial_worlds);    return inferFullDistribution(allocator, initial_worlds, state);}pub fn processMarginalQuery(    allocator: Allocator,    query_thunk: *RuntimeValue,    state: *LazyKCState,) ![]World {    const query_result = try evaluateThunk(allocator, query_thunk, Bdd.TRUE, state);    defer freeWorldsSlice(allocator, query_result.worlds);    return inferFullDistribution(allocator, query_result.worlds, state);}pub fn inferFullDistribution(    allocator: Allocator,    initial_worlds: []World,    state: *LazyKCState,) ![]World {    var queue: std.ArrayList(World) = .empty;    defer queue.deinit(allocator);    var resolved: std.ArrayList(World) = .empty;    defer resolved.deinit(allocator);    var thunk_path: std.ArrayList(usize) = .empty;    defer thunk_path.deinit(allocator);    try queue.appendSlice(allocator, initial_worlds);    while (queue.items.len > 0) {        if (state.manager.limits.checkTimeLimit()) {            state.stats.limit_reason = .time_limit;            break;        }        const current = queue.pop().?;        if (!try runtime.findFirstThunkInto(allocator, current.value, &thunk_path)) {            try resolved.append(allocator, current);            continue;        }        const path = thunk_path.items;        const thunk_val = runtime.getValueAtPath(current.value, path) orelse {            try resolved.append(allocator, current);            continue;        };        const sub_result = try evaluateThunk(allocator, thunk_val, current.guard, state);        defer freeWorldsSlice(allocator, sub_result.worlds);        for (sub_result.worlds) |sub_world| {            const new_val = try runtime.replaceAtPath(                allocator,                current.value,                path,                sub_world.value,            );            const combined_guard = try state.manager.bddAnd(current.guard, sub_world.guard);            try queue.append(allocator, World{ .value = new_val, .guard = combined_guard });        }    }    return resolved.toOwnedSlice(allocator);}test "pure monad" {    const allocator = std.testing.allocator;    var manager = try Manager.init(allocator);    defer manager.deinit();    var defs = pexpr.Definitions.init(allocator);    defer defs.deinit();    var state = try state_module.initChecked(allocator, &manager, &defs, .{});    defer state_module.deinit(&state);    const val = try RuntimeValue.initTrue(allocator);    defer val.deinit(allocator);    const result = try pureMonad(allocator, val, &state);    defer freeWorldsSlice(allocator, result.worlds);    try std.testing.expectEqual(@as(usize, 1), result.worlds.len);    try std.testing.expect(result.worlds[0].guard.isTrue());}test "if then else monad" {    const allocator = std.testing.allocator;    var manager = try Manager.init(allocator);    defer manager.deinit();    var defs = pexpr.Definitions.init(allocator);    defer defs.deinit();    var state = try state_module.initChecked(allocator, &manager, &defs, .{});    defer state_module.deinit(&state);    const true_val = try RuntimeValue.initTrue(allocator);    defer true_val.deinit(allocator);    const false_val = try RuntimeValue.initFalse(allocator);    defer false_val.deinit(allocator);    const x = try manager.newVar(true);    const result = try ifThenElseMonad(allocator, true_val, false_val, x, &state);    defer freeWorldsSlice(allocator, result.worlds);    try std.testing.expectEqual(@as(usize, 2), result.worlds.len);}test "joinMonad merges structurally identical values - pluck-rs-ui3" {    const allocator = std.testing.allocator;    var manager = try Manager.init(allocator);    defer manager.deinit();    var defs = pexpr.Definitions.init(allocator);    defer defs.deinit();    var state = try state_module.initChecked(allocator, &manager, &defs, .{});    defer state_module.deinit(&state);    const true_val1 = try RuntimeValue.initTrue(allocator);    defer true_val1.deinit(allocator);    const true_val2 = try RuntimeValue.initTrue(allocator);    defer true_val2.deinit(allocator);    try std.testing.expect(true_val1.eql(true_val2));    try std.testing.expect(true_val1 != true_val2);    const x = try manager.newVar(true);    const worlds1 = try allocator.alloc(World, 1);    worlds1[0] = World{ .value = true_val1, .guard = Bdd.TRUE };    const worlds2 = try allocator.alloc(World, 1);    worlds2[0] = World{ .value = true_val2, .guard = Bdd.TRUE };    const nested_worlds = [_]NestedWorld{        NestedWorld{            .result = WorldsResult{                .worlds = worlds1,                .validity_guard = Bdd.TRUE,            },            .guard = x,        },        NestedWorld{            .result = WorldsResult{                .worlds = worlds2,                .validity_guard = Bdd.TRUE,            },            .guard = x.neg(),        },    };    const result = try joinMonad(allocator, &nested_worlds, Bdd.TRUE, &state);    defer allocator.free(result.worlds);    try std.testing.expectEqual(@as(usize, 1), result.worlds.len);    try std.testing.expectEqual(Bdd.TRUE, result.worlds[0].guard);}test "joinMonad merges identical IntDist values - pluck-rs-ui3" {    const allocator = std.testing.allocator;    var manager = try Manager.init(allocator);    defer manager.deinit();    var defs = pexpr.Definitions.init(allocator);    defer defs.deinit();    var state = try state_module.initChecked(allocator, &manager, &defs, .{});    defer state_module.deinit(&state);    const bits1 = try allocator.alloc(Bdd, 8);    for (bits1) |*bit| bit.* = Bdd.FALSE;    bits1[1] = Bdd.TRUE;    bits1[3] = Bdd.TRUE;    bits1[5] = Bdd.TRUE;    const bits2 = try allocator.alloc(Bdd, 8);    for (bits2) |*bit| bit.* = Bdd.FALSE;    bits2[1] = Bdd.TRUE;    bits2[3] = Bdd.TRUE;    bits2[5] = Bdd.TRUE;    const intdist1 = try RuntimeValue.initNative(allocator, .{ .int_dist = runtime.IntDist.init(bits1) });    defer intdist1.deinit(allocator);    const intdist2 = try RuntimeValue.initNative(allocator, .{ .int_dist = runtime.IntDist.init(bits2) });    defer intdist2.deinit(allocator);    try std.testing.expect(intdist1.eql(intdist2));    try std.testing.expect(intdist1 != intdist2);    const x = try manager.newVar(true);    const worlds1 = try allocator.alloc(World, 1);    worlds1[0] = World{ .value = intdist1, .guard = Bdd.TRUE };    const worlds2 = try allocator.alloc(World, 1);    worlds2[0] = World{ .value = intdist2, .guard = Bdd.TRUE };    const nested_worlds = [_]NestedWorld{        NestedWorld{            .result = WorldsResult{ .worlds = worlds1, .validity_guard = Bdd.TRUE },            .guard = x,        },        NestedWorld{            .result = WorldsResult{ .worlds = worlds2, .validity_guard = Bdd.TRUE },            .guard = x.neg(),        },    };    const result = try joinMonad(allocator, &nested_worlds, Bdd.TRUE, &state);    defer allocator.free(result.worlds);    try std.testing.expectEqual(@as(usize, 1), result.worlds.len);    try std.testing.expectEqual(Bdd.TRUE, result.worlds[0].guard);}test "joinMonad collapses constructor worlds with thunk unions" {    var arena = std.heap.ArenaAllocator.init(std.testing.allocator);    defer arena.deinit();    const allocator = arena.allocator();    var manager = try Manager.init(allocator);    defer manager.deinit();    var defs = pexpr.Definitions.init(allocator);    defer defs.deinit();    var state = try state_module.initChecked(allocator, &manager, &defs, .{ .use_thunk_unions = true });    defer state_module.deinit(&state);    const expr1 = try pexpr.PExpr.init(allocator, .{ .const_native = .{ .int = 1 } });    const expr2 = try pexpr.PExpr.init(allocator, .{ .const_native = .{ .int = 2 } });    const thunk1 = try makeThunk(allocator, expr1, Env.empty, 0, &state);    const thunk2 = try makeThunk(allocator, expr2, Env.empty, 1, &state);    const thunk_val1 = try RuntimeValue.initLazyKCThunk(allocator, thunk1);    const thunk_val2 = try RuntimeValue.initLazyKCThunk(allocator, thunk2);    const args1 = try allocator.alloc(*RuntimeValue, 1);    args1[0] = thunk_val1;    const val1 = try RuntimeValue.initConstructed(allocator, "Box", args1);    const args2 = try allocator.alloc(*RuntimeValue, 1);    args2[0] = thunk_val2;    const val2 = try RuntimeValue.initConstructed(allocator, "Box", args2);    try std.testing.expect(!val1.eql(val2));    const x = try manager.newVar(true);    const worlds1 = try allocator.alloc(World, 1);    worlds1[0] = World{ .value = val1, .guard = Bdd.TRUE };    const worlds2 = try allocator.alloc(World, 1);    worlds2[0] = World{ .value = val2, .guard = Bdd.TRUE };    const nested_worlds = [_]NestedWorld{        NestedWorld{            .result = WorldsResult{ .worlds = worlds1, .validity_guard = Bdd.TRUE },            .guard = x,        },        NestedWorld{            .result = WorldsResult{ .worlds = worlds2, .validity_guard = Bdd.TRUE },            .guard = x.neg(),        },    };    const result = try joinMonad(allocator, &nested_worlds, Bdd.TRUE, &state);    defer allocator.free(result.worlds);    try std.testing.expectEqual(@as(usize, 1), result.worlds.len);    try std.testing.expectEqual(Bdd.TRUE, result.worlds[0].guard);    const combined = result.worlds[0].value;    try std.testing.expect(combined.data == .constructed);    const c = combined.data.constructed;    try std.testing.expect(std.mem.eql(u8, c.constructor, "Box"));    try std.testing.expectEqual(@as(usize, 1), c.args.len);    try std.testing.expect(c.args[0].data == .lazy_kc_thunk_union);    try std.testing.expectEqual(@as(usize, 2), c.args[0].data.lazy_kc_thunk_union.thunks.len);}fn contains_thunk(union_thunk: *LazyKCThunkUnion, thunk: *LazyKCThunk) bool {    for (union_thunk.thunks) |tg| {        if (tg.thunk == thunk) return true;    }    return false;}test "joinMonad collapses list constructors with thunk unions" {    var arena = std.heap.ArenaAllocator.init(std.testing.allocator);    defer arena.deinit();    const allocator = arena.allocator();    var manager = try Manager.init(allocator);    defer manager.deinit();    var defs = pexpr.Definitions.init(allocator);    defer defs.deinit();    var state = try state_module.initChecked(allocator, &manager, &defs, .{ .use_thunk_unions = true });    defer state_module.deinit(&state);    const head_expr1 = try pexpr.PExpr.init(allocator, .{ .const_native = .{ .int = 1 } });    const head_expr2 = try pexpr.PExpr.init(allocator, .{ .const_native = .{ .int = 2 } });    const tail_head1 = try pexpr.PExpr.init(allocator, .{ .const_native = .{ .int = 3 } });    const tail_nil1 = try pexpr.PExpr.init(allocator, .{ .construct = .{ .constructor = "Nil" } });    const tail_expr1 = try pexpr.PExpr.initWithArgs(        allocator,        .{ .construct = .{ .constructor = "Cons" } },        &[_]*pexpr.PExpr{ tail_head1, tail_nil1 },    );    const tail_head2 = try pexpr.PExpr.init(allocator, .{ .const_native = .{ .int = 4 } });    const tail_nil2 = try pexpr.PExpr.init(allocator, .{ .construct = .{ .constructor = "Nil" } });    const tail_expr2 = try pexpr.PExpr.initWithArgs(        allocator,        .{ .construct = .{ .constructor = "Cons" } },        &[_]*pexpr.PExpr{ tail_head2, tail_nil2 },    );    const head_thunk1 = try makeThunk(allocator, head_expr1, Env.empty, 0, &state);    const head_thunk2 = try makeThunk(allocator, head_expr2, Env.empty, 1, &state);    const tail_thunk1 = try makeThunk(allocator, tail_expr1, Env.empty, 2, &state);    const tail_thunk2 = try makeThunk(allocator, tail_expr2, Env.empty, 3, &state);    const head_val1 = try RuntimeValue.initLazyKCThunk(allocator, head_thunk1);    const head_val2 = try RuntimeValue.initLazyKCThunk(allocator, head_thunk2);    const tail_val1 = try RuntimeValue.initLazyKCThunk(allocator, tail_thunk1);    const tail_val2 = try RuntimeValue.initLazyKCThunk(allocator, tail_thunk2);    const args1 = try allocator.alloc(*RuntimeValue, 2);    args1[0] = head_val1;    args1[1] = tail_val1;    const list1 = try RuntimeValue.initConstructed(allocator, "Cons", args1);    const args2 = try allocator.alloc(*RuntimeValue, 2);    args2[0] = head_val2;    args2[1] = tail_val2;    const list2 = try RuntimeValue.initConstructed(allocator, "Cons", args2);    try std.testing.expect(!list1.eql(list2));    const x = try manager.newVar(true);    const worlds1 = try allocator.alloc(World, 1);    worlds1[0] = World{ .value = list1, .guard = Bdd.TRUE };    const worlds2 = try allocator.alloc(World, 1);    worlds2[0] = World{ .value = list2, .guard = Bdd.TRUE };    const nested_worlds = [_]NestedWorld{        NestedWorld{            .result = WorldsResult{ .worlds = worlds1, .validity_guard = Bdd.TRUE },            .guard = x,        },        NestedWorld{            .result = WorldsResult{ .worlds = worlds2, .validity_guard = Bdd.TRUE },            .guard = x.neg(),        },    };    const result = try joinMonad(allocator, &nested_worlds, Bdd.TRUE, &state);    defer allocator.free(result.worlds);    try std.testing.expectEqual(@as(usize, 1), result.worlds.len);    try std.testing.expectEqual(Bdd.TRUE, result.worlds[0].guard);    const combined = result.worlds[0].value;    try std.testing.expect(combined.data == .constructed);    const c = combined.data.constructed;    try std.testing.expect(std.mem.eql(u8, c.constructor, "Cons"));    try std.testing.expectEqual(@as(usize, 2), c.args.len);    try std.testing.expect(c.args[0].data == .lazy_kc_thunk_union);    try std.testing.expect(c.args[1].data == .lazy_kc_thunk_union);    const head_union = c.args[0].data.lazy_kc_thunk_union;    const tail_union = c.args[1].data.lazy_kc_thunk_union;    try std.testing.expectEqual(@as(usize, 2), head_union.thunks.len);    try std.testing.expectEqual(@as(usize, 2), tail_union.thunks.len);    try std.testing.expect(contains_thunk(head_union, head_thunk1));    try std.testing.expect(contains_thunk(head_union, head_thunk2));    try std.testing.expect(contains_thunk(tail_union, tail_thunk1));    try std.testing.expect(contains_thunk(tail_union, tail_thunk2));    var formatted_buf = std.Io.Writer.Allocating.init(allocator);    defer formatted_buf.deinit();    try combined.format("", .{}, &formatted_buf.writer);    try std.testing.expect(std.mem.indexOf(u8, formatted_buf.written(), "LazyKCThunkUnion") != null);}test "joinMonad collapses nested constructors with thunk unions" {    var arena = std.heap.ArenaAllocator.init(std.testing.allocator);    defer arena.deinit();    const allocator = arena.allocator();    var manager = try Manager.init(allocator);    defer manager.deinit();    var defs = pexpr.Definitions.init(allocator);    defer defs.deinit();    var state = try state_module.initChecked(allocator, &manager, &defs, .{ .use_thunk_unions = true });    defer state_module.deinit(&state);    const list_head1 = try pexpr.PExpr.init(allocator, .{ .const_native = .{ .int = 10 } });    const list_nil1 = try pexpr.PExpr.init(allocator, .{ .construct = .{ .constructor = "Nil" } });    const list_expr1 = try pexpr.PExpr.initWithArgs(        allocator,        .{ .construct = .{ .constructor = "Cons" } },        &[_]*pexpr.PExpr{ list_head1, list_nil1 },    );    const list_head2 = try pexpr.PExpr.init(allocator, .{ .const_native = .{ .int = 20 } });    const list_nil2 = try pexpr.PExpr.init(allocator, .{ .construct = .{ .constructor = "Nil" } });    const list_expr2 = try pexpr.PExpr.initWithArgs(        allocator,        .{ .construct = .{ .constructor = "Cons" } },        &[_]*pexpr.PExpr{ list_head2, list_nil2 },    );    const pair_left1 = try pexpr.PExpr.init(allocator, .{ .const_native = .{ .int = 1 } });    const pair_right1 = try pexpr.PExpr.init(allocator, .{ .const_native = .{ .int = 2 } });    const pair_expr1 = try pexpr.PExpr.initWithArgs(        allocator,        .{ .construct = .{ .constructor = "Pair" } },        &[_]*pexpr.PExpr{ pair_left1, pair_right1 },    );    const pair_left2 = try pexpr.PExpr.init(allocator, .{ .const_native = .{ .int = 3 } });    const pair_right2 = try pexpr.PExpr.init(allocator, .{ .const_native = .{ .int = 4 } });    const pair_expr2 = try pexpr.PExpr.initWithArgs(        allocator,        .{ .construct = .{ .constructor = "Pair" } },        &[_]*pexpr.PExpr{ pair_left2, pair_right2 },    );    const left_thunk1 = try makeThunk(allocator, list_expr1, Env.empty, 0, &state);    const left_thunk2 = try makeThunk(allocator, list_expr2, Env.empty, 1, &state);    const right_thunk1 = try makeThunk(allocator, pair_expr1, Env.empty, 2, &state);    const right_thunk2 = try makeThunk(allocator, pair_expr2, Env.empty, 3, &state);    const left_val1 = try RuntimeValue.initLazyKCThunk(allocator, left_thunk1);    const left_val2 = try RuntimeValue.initLazyKCThunk(allocator, left_thunk2);    const right_val1 = try RuntimeValue.initLazyKCThunk(allocator, right_thunk1);    const right_val2 = try RuntimeValue.initLazyKCThunk(allocator, right_thunk2);    const node_args1 = try allocator.alloc(*RuntimeValue, 2);    node_args1[0] = left_val1;    node_args1[1] = right_val1;    const node1 = try RuntimeValue.initConstructed(allocator, "Node", node_args1);    const node_args2 = try allocator.alloc(*RuntimeValue, 2);    node_args2[0] = left_val2;    node_args2[1] = right_val2;    const node2 = try RuntimeValue.initConstructed(allocator, "Node", node_args2);    try std.testing.expect(!node1.eql(node2));    const x = try manager.newVar(true);    const worlds1 = try allocator.alloc(World, 1);    worlds1[0] = World{ .value = node1, .guard = Bdd.TRUE };    const worlds2 = try allocator.alloc(World, 1);    worlds2[0] = World{ .value = node2, .guard = Bdd.TRUE };    const nested_worlds = [_]NestedWorld{        NestedWorld{            .result = WorldsResult{ .worlds = worlds1, .validity_guard = Bdd.TRUE },            .guard = x,        },        NestedWorld{            .result = WorldsResult{ .worlds = worlds2, .validity_guard = Bdd.TRUE },            .guard = x.neg(),        },    };    const result = try joinMonad(allocator, &nested_worlds, Bdd.TRUE, &state);    defer allocator.free(result.worlds);    try std.testing.expectEqual(@as(usize, 1), result.worlds.len);    try std.testing.expectEqual(Bdd.TRUE, result.worlds[0].guard);    const combined = result.worlds[0].value;    try std.testing.expect(combined.data == .constructed);    const c = combined.data.constructed;    try std.testing.expect(std.mem.eql(u8, c.constructor, "Node"));    try std.testing.expectEqual(@as(usize, 2), c.args.len);    try std.testing.expect(c.args[0].data == .lazy_kc_thunk_union);    try std.testing.expect(c.args[1].data == .lazy_kc_thunk_union);    try std.testing.expectEqual(@as(usize, 2), c.args[0].data.lazy_kc_thunk_union.thunks.len);    try std.testing.expectEqual(@as(usize, 2), c.args[1].data.lazy_kc_thunk_union.thunks.len);}test "callstack ordering" {    const a: []const i32 = &[_]i32{ 1, 2, 3 };    const b: []const i32 = &[_]i32{ 1, 2, 4 };    const c: []const i32 = &[_]i32{ 1, 2 };    try std.testing.expectEqual(std.math.Order.lt, compareCallstacks(a, b));    try std.testing.expectEqual(std.math.Order.gt, compareCallstacks(b, a));    try std.testing.expectEqual(std.math.Order.gt, compareCallstacks(a, c));    try std.testing.expectEqual(std.math.Order.eq, compareCallstacks(a, a));}fn make_callstack(allocator: Allocator, value: i32) ![]i32 {    const slice = try allocator.alloc(i32, 1);    slice[0] = value;    return slice;}test "findInsertPosition respects use_reverse_order flag" {    var arena = std.heap.ArenaAllocator.init(std.testing.allocator);    defer arena.deinit();    const allocator = arena.allocator();    var manager = try Manager.init(allocator);    defer manager.deinit();    var defs = pexpr.Definitions.init(allocator);    defer defs.deinit();    {        var state = try state_module.initChecked(allocator, &manager, &defs, .{ .use_reverse_order = false });        defer state_module.deinit(&state);        const cs1 = try make_callstack(allocator, 1);        const cs3 = try make_callstack(allocator, 3);        const cs5 = try make_callstack(allocator, 5);        try state.sorted_callstacks.append(allocator, .{ .callstack = cs1, .prob = 0.5 });        try state.sorted_callstacks.append(allocator, .{ .callstack = cs3, .prob = 0.5 });        try state.sorted_callstacks.append(allocator, .{ .callstack = cs5, .prob = 0.5 });        const cs2 = try make_callstack(allocator, 2);        const pos2 = state_module.findInsertPosition(&state, .{ .callstack = cs2, .prob = 0.5 });        try std.testing.expectEqual(@as(usize, 1), pos2);        const cs0 = try make_callstack(allocator, 0);        const pos0 = state_module.findInsertPosition(&state, .{ .callstack = cs0, .prob = 0.5 });        try std.testing.expectEqual(@as(usize, 0), pos0);        const cs6 = try make_callstack(allocator, 6);        const pos6 = state_module.findInsertPosition(&state, .{ .callstack = cs6, .prob = 0.5 });        try std.testing.expectEqual(@as(usize, 3), pos6);    }    {        var state = try state_module.initChecked(allocator, &manager, &defs, .{ .use_reverse_order = true });        defer state_module.deinit(&state);        const cs5 = try make_callstack(allocator, 5);        const cs3 = try make_callstack(allocator, 3);        const cs1 = try make_callstack(allocator, 1);        try state.sorted_callstacks.append(allocator, .{ .callstack = cs5, .prob = 0.5 });        try state.sorted_callstacks.append(allocator, .{ .callstack = cs3, .prob = 0.5 });        try state.sorted_callstacks.append(allocator, .{ .callstack = cs1, .prob = 0.5 });        const cs4 = try make_callstack(allocator, 4);        const pos4 = state_module.findInsertPosition(&state, .{ .callstack = cs4, .prob = 0.5 });        try std.testing.expectEqual(@as(usize, 1), pos4);        const cs6 = try make_callstack(allocator, 6);        const pos6 = state_module.findInsertPosition(&state, .{ .callstack = cs6, .prob = 0.5 });        try std.testing.expectEqual(@as(usize, 0), pos6);        const cs0 = try make_callstack(allocator, 0);        const pos0 = state_module.findInsertPosition(&state, .{ .callstack = cs0, .prob = 0.5 });        try std.testing.expectEqual(@as(usize, 3), pos0);    }}test "flip produces path-condition-independent guards" {    var arena = std.heap.ArenaAllocator.init(std.testing.allocator);    defer arena.deinit();    const allocator = arena.allocator();    var manager = try Manager.init(allocator);    defer manager.deinit();    var defs = pexpr.Definitions.init(allocator);    defer defs.deinit();    var state = try state_module.initChecked(allocator, &manager, &defs, .{});    defer state_module.deinit(&state);    const prob_expr = try pexpr.PExpr.init(allocator, .{ .const_native = .{ .float = 0.5 } });    const flip_expr = try pexpr.PExpr.initWithArgs(allocator, .flip, &[_]*pexpr.PExpr{prob_expr});    const pc_var = try manager.newVar(true);    const result = try compileInner(flip_expr, Env.empty, pc_var, &state);    try std.testing.expectEqual(@as(usize, 2), result.worlds.len);    const true_guard = result.worlds[0].guard;    const false_guard = result.worlds[1].guard;    try std.testing.expect(manager.eq(true_guard.neg(), false_guard));    const implies_pc = try manager.bddImplies(true_guard, pc_var);    try std.testing.expect(!implies_pc.isTrue());}const BindIdentityContinuation = struct {    pub fn cont(        alloc: Allocator,        value: *RuntimeValue,        _: Bdd,        state: *LazyKCState,        _: anytype,    ) CompileError!WorldsResult {        return pureMonad(alloc, value, state);    }};test "bindMonad frees input worlds slice" {    const allocator = std.testing.allocator;    var manager = try Manager.init(allocator);    defer manager.deinit();    var defs = pexpr.Definitions.init(allocator);    defer defs.deinit();    var state = try state_module.initChecked(allocator, &manager, &defs, .{});    defer state_module.deinit(&state);    const val1 = try RuntimeValue.initNative(allocator, .{ .int = 1 });    const val2 = try RuntimeValue.initNative(allocator, .{ .int = 2 });    defer val1.deinit(allocator);    defer val2.deinit(allocator);    const input_worlds = try allocator.alloc(World, 2);    input_worlds[0] = World{ .value = val1, .guard = Bdd.TRUE };    input_worlds[1] = World{ .value = val2, .guard = Bdd.TRUE };    const input = WorldsResult{        .worlds = input_worlds,        .validity_guard = Bdd.TRUE,    };    const result = try bindMonad(allocator, input, Bdd.TRUE, &state, BindIdentityContinuation, {});    defer freeWorldsSlice(allocator, result.worlds);    try std.testing.expectEqual(@as(usize, 2), result.worlds.len);}test "thunk cache returns equivalent guards on hit" {    var arena = std.heap.ArenaAllocator.init(std.testing.allocator);    defer arena.deinit();    const allocator = arena.allocator();    var manager = try Manager.init(allocator);    defer manager.deinit();    var defs = pexpr.Definitions.init(allocator);    defer defs.deinit();    var state = try state_module.initChecked(allocator, &manager, &defs, .{});    defer state_module.deinit(&state);    const val_expr = try pexpr.PExpr.init(allocator, .{ .const_native = .{ .int = 42 } });    const thunk = try makeThunk(allocator, val_expr, Env.empty, 0, &state);    const result1 = try evaluateLazyKCThunk(allocator, thunk, Bdd.TRUE, &state);    try std.testing.expectEqual(@as(usize, 1), thunk.cache.items.len);    const result2 = try evaluateLazyKCThunk(allocator, thunk, Bdd.TRUE, &state);    try std.testing.expectEqual(result1.worlds.len, result2.worlds.len);    for (result1.worlds, result2.worlds) |w1, w2| {        try std.testing.expect(manager.eq(w1.guard, w2.guard));    }}test "mk_int creates IntDist with correct bits" {    var arena = std.heap.ArenaAllocator.init(std.testing.allocator);    defer arena.deinit();    const allocator = arena.allocator();    var manager = try Manager.init(allocator);    defer manager.deinit();    var defs = pexpr.Definitions.init(allocator);    defer defs.deinit();    var state = try state_module.initChecked(allocator, &manager, &defs, .{});    defer state_module.deinit(&state);    const bitwidth_expr = try pexpr.PExpr.init(allocator, .{ .const_native = .{ .int = 4 } });    const value_expr = try pexpr.PExpr.init(allocator, .{ .const_native = .{ .int = 5 } });    const mk_int_expr = try pexpr.PExpr.initWithArgs(allocator, .mk_int, &[_]*pexpr.PExpr{ bitwidth_expr, value_expr });    const result = try compileInner(mk_int_expr, Env.empty, Bdd.TRUE, &state);    try std.testing.expectEqual(@as(usize, 1), result.worlds.len);    const val = result.worlds[0].value;    try std.testing.expect(val.data == .native);    try std.testing.expect(val.data.native == .int_dist);    const int_dist = val.data.native.int_dist;    try std.testing.expectEqual(@as(usize, 4), int_dist.bits.len);    try std.testing.expect(int_dist.bits[0].isTrue());    try std.testing.expect(int_dist.bits[1].isFalse());    try std.testing.expect(int_dist.bits[2].isTrue());    try std.testing.expect(int_dist.bits[3].isFalse());}test "int_dist_eq with equal values returns True" {    var arena = std.heap.ArenaAllocator.init(std.testing.allocator);    defer arena.deinit();    const allocator = arena.allocator();    var manager = try Manager.init(allocator);    defer manager.deinit();    var defs = pexpr.Definitions.init(allocator);    defer defs.deinit();    var state = try state_module.initChecked(allocator, &manager, &defs, .{});    defer state_module.deinit(&state);    const bitwidth_expr1 = try pexpr.PExpr.init(allocator, .{ .const_native = .{ .int = 4 } });    const value_expr1 = try pexpr.PExpr.init(allocator, .{ .const_native = .{ .int = 7 } });    const mk_int_expr1 = try pexpr.PExpr.initWithArgs(allocator, .mk_int, &[_]*pexpr.PExpr{ bitwidth_expr1, value_expr1 });    const bitwidth_expr2 = try pexpr.PExpr.init(allocator, .{ .const_native = .{ .int = 4 } });    const value_expr2 = try pexpr.PExpr.init(allocator, .{ .const_native = .{ .int = 7 } });    const mk_int_expr2 = try pexpr.PExpr.initWithArgs(allocator, .mk_int, &[_]*pexpr.PExpr{ bitwidth_expr2, value_expr2 });    const eq_expr = try pexpr.PExpr.initWithArgs(allocator, .int_dist_eq, &[_]*pexpr.PExpr{ mk_int_expr1, mk_int_expr2 });    const result = try compileInner(eq_expr, Env.empty, Bdd.TRUE, &state);    try std.testing.expect(result.worlds.len >= 1);    var found_true = false;    for (result.worlds) |world| {        if (world.value.data == .constructed) {            const c = world.value.data.constructed;            if (std.mem.eql(u8, c.constructor, "True") and world.guard.isTrue()) {                found_true = true;            }        }    }    try std.testing.expect(found_true);}test "int_dist_eq with different values returns False" {    var arena = std.heap.ArenaAllocator.init(std.testing.allocator);    defer arena.deinit();    const allocator = arena.allocator();    var manager = try Manager.init(allocator);    defer manager.deinit();    var defs = pexpr.Definitions.init(allocator);    defer defs.deinit();    var state = try state_module.initChecked(allocator, &manager, &defs, .{});    defer state_module.deinit(&state);    const bitwidth_expr1 = try pexpr.PExpr.init(allocator, .{ .const_native = .{ .int = 4 } });    const value_expr1 = try pexpr.PExpr.init(allocator, .{ .const_native = .{ .int = 5 } });    const mk_int_expr1 = try pexpr.PExpr.initWithArgs(allocator, .mk_int, &[_]*pexpr.PExpr{ bitwidth_expr1, value_expr1 });    const bitwidth_expr2 = try pexpr.PExpr.init(allocator, .{ .const_native = .{ .int = 4 } });    const value_expr2 = try pexpr.PExpr.init(allocator, .{ .const_native = .{ .int = 7 } });    const mk_int_expr2 = try pexpr.PExpr.initWithArgs(allocator, .mk_int, &[_]*pexpr.PExpr{ bitwidth_expr2, value_expr2 });    const eq_expr = try pexpr.PExpr.initWithArgs(allocator, .int_dist_eq, &[_]*pexpr.PExpr{ mk_int_expr1, mk_int_expr2 });    const result = try compileInner(eq_expr, Env.empty, Bdd.TRUE, &state);    try std.testing.expect(result.worlds.len >= 1);    var found_false = false;    for (result.worlds) |world| {        if (world.value.data == .constructed) {            const c = world.value.data.constructed;            if (std.mem.eql(u8, c.constructor, "False") and world.guard.isTrue()) {                found_false = true;            }        }    }    try std.testing.expect(found_false);}test "pbool with deterministic True" {    var arena = std.heap.ArenaAllocator.init(std.testing.allocator);    defer arena.deinit();    const allocator = arena.allocator();    var manager = try Manager.init(allocator);    defer manager.deinit();    var defs = pexpr.Definitions.init(allocator);    defer defs.deinit();    var state = try state_module.initChecked(allocator, &manager, &defs, .{});    defer state_module.deinit(&state);    const true_expr = try pexpr.PExpr.initWithArgs(allocator, .{ .construct = .{ .constructor = "True" } }, &[_]*pexpr.PExpr{});    const pbool_expr = try pexpr.PExpr.initWithArgs(allocator, .pbool, &[_]*pexpr.PExpr{true_expr});    const result = try compileInner(pbool_expr, Env.empty, Bdd.TRUE, &state);    try std.testing.expectEqual(@as(usize, 1), result.worlds.len);    const val = result.worlds[0].value;    try std.testing.expect(val.data == .constructed);    try std.testing.expect(std.mem.eql(u8, val.data.constructed.constructor, "PBool"));    const prob_val = val.data.constructed.args[0];    try std.testing.expect(prob_val.data == .native);    try std.testing.expect(prob_val.data.native == .float);    try std.testing.expectApproxEqAbs(@as(f64, 1.0), prob_val.data.native.float, 1e-10);}test "pbool with deterministic False" {    var arena = std.heap.ArenaAllocator.init(std.testing.allocator);    defer arena.deinit();    const allocator = arena.allocator();    var manager = try Manager.init(allocator);    defer manager.deinit();    var defs = pexpr.Definitions.init(allocator);    defer defs.deinit();    var state = try state_module.initChecked(allocator, &manager, &defs, .{});    defer state_module.deinit(&state);    const false_expr = try pexpr.PExpr.initWithArgs(allocator, .{ .construct = .{ .constructor = "False" } }, &[_]*pexpr.PExpr{});    const pbool_expr = try pexpr.PExpr.initWithArgs(allocator, .pbool, &[_]*pexpr.PExpr{false_expr});    const result = try compileInner(pbool_expr, Env.empty, Bdd.TRUE, &state);    try std.testing.expectEqual(@as(usize, 1), result.worlds.len);    const val = result.worlds[0].value;    try std.testing.expect(val.data == .constructed);    try std.testing.expect(std.mem.eql(u8, val.data.constructed.constructor, "PBool"));    const prob_val = val.data.constructed.args[0];    try std.testing.expect(prob_val.data == .native);    try std.testing.expect(prob_val.data.native == .float);    try std.testing.expectApproxEqAbs(@as(f64, 0.0), prob_val.data.native.float, 1e-10);}test "pbool with flip(0.5)" {    var arena = std.heap.ArenaAllocator.init(std.testing.allocator);    defer arena.deinit();    const allocator = arena.allocator();    var manager = try Manager.init(allocator);    defer manager.deinit();    var defs = pexpr.Definitions.init(allocator);    defer defs.deinit();    var state = try state_module.initChecked(allocator, &manager, &defs, .{});    defer state_module.deinit(&state);    const prob_expr = try pexpr.PExpr.init(allocator, .{ .const_native = .{ .float = 0.5 } });    const flip_expr = try pexpr.PExpr.initWithArgs(allocator, .flip, &[_]*pexpr.PExpr{prob_expr});    const pbool_expr = try pexpr.PExpr.initWithArgs(allocator, .pbool, &[_]*pexpr.PExpr{flip_expr});    const result = try compileInner(pbool_expr, Env.empty, Bdd.TRUE, &state);    try std.testing.expectEqual(@as(usize, 2), result.worlds.len);    for (result.worlds) |world| {        try std.testing.expect(world.value.data == .constructed);        try std.testing.expect(std.mem.eql(u8, world.value.data.constructed.constructor, "PBool"));        const prob_val = world.value.data.constructed.args[0];        try std.testing.expect(prob_val.data == .native);        try std.testing.expect(prob_val.data.native == .float);        try std.testing.expectApproxEqAbs(@as(f64, 0.5), prob_val.data.native.float, 1e-10);    }}test "mk_int_weighted with single value is deterministic" {    var arena = std.heap.ArenaAllocator.init(std.testing.allocator);    defer arena.deinit();    const allocator = arena.allocator();    var manager = try Manager.init(allocator);    defer manager.deinit();    var defs = pexpr.Definitions.init(allocator);    defer defs.deinit();    var state = try state_module.initChecked(allocator, &manager, &defs, .{});    defer state_module.deinit(&state);    const bitwidth_expr = try pexpr.PExpr.init(allocator, .{ .const_native = .{ .int = 4 } });    const pair_val = try pexpr.PExpr.init(allocator, .{ .const_native = .{ .int = 5 } });    const pair_prob = try pexpr.PExpr.init(allocator, .{ .const_native = .{ .float = 1.0 } });    const pair_expr = try pexpr.PExpr.initWithArgs(allocator, .{ .construct = .{ .constructor = "Pair" } }, &[_]*pexpr.PExpr{ pair_val, pair_prob });    const nil_expr = try pexpr.PExpr.init(allocator, .{ .construct = .{ .constructor = "Nil" } });    const list_expr = try pexpr.PExpr.initWithArgs(allocator, .{ .construct = .{ .constructor = "Cons" } }, &[_]*pexpr.PExpr{ pair_expr, nil_expr });    const mk_int_weighted_expr = try pexpr.PExpr.initWithArgs(allocator, .mk_int_weighted, &[_]*pexpr.PExpr{ bitwidth_expr, list_expr });    const result = try compileInner(mk_int_weighted_expr, Env.empty, Bdd.TRUE, &state);    try std.testing.expectEqual(@as(usize, 1), result.worlds.len);    const val = result.worlds[0].value;    try std.testing.expect(val.data == .native);    try std.testing.expect(val.data.native == .int_dist);    const int_dist = val.data.native.int_dist;    try std.testing.expectEqual(@as(usize, 4), int_dist.bits.len);    try std.testing.expect(int_dist.bits[0].isTrue());    try std.testing.expect(int_dist.bits[1].isFalse());    try std.testing.expect(int_dist.bits[2].isTrue());    try std.testing.expect(int_dist.bits[3].isFalse());}test "mk_int_weighted with two equal probability values" {    var arena = std.heap.ArenaAllocator.init(std.testing.allocator);    defer arena.deinit();    const allocator = arena.allocator();    var manager = try Manager.init(allocator);    defer manager.deinit();    var defs = pexpr.Definitions.init(allocator);    defer defs.deinit();    var state = try state_module.initChecked(allocator, &manager, &defs, .{});    defer state_module.deinit(&state);    const bitwidth_expr = try pexpr.PExpr.init(allocator, .{ .const_native = .{ .int = 4 } });    const pair1_val = try pexpr.PExpr.init(allocator, .{ .const_native = .{ .int = 1 } });    const pair1_prob = try pexpr.PExpr.init(allocator, .{ .const_native = .{ .float = 0.5 } });    const pair1_expr = try pexpr.PExpr.initWithArgs(allocator, .{ .construct = .{ .constructor = "Pair" } }, &[_]*pexpr.PExpr{ pair1_val, pair1_prob });    const pair2_val = try pexpr.PExpr.init(allocator, .{ .const_native = .{ .int = 2 } });    const pair2_prob = try pexpr.PExpr.init(allocator, .{ .const_native = .{ .float = 0.5 } });    const pair2_expr = try pexpr.PExpr.initWithArgs(allocator, .{ .construct = .{ .constructor = "Pair" } }, &[_]*pexpr.PExpr{ pair2_val, pair2_prob });    const nil_expr = try pexpr.PExpr.init(allocator, .{ .construct = .{ .constructor = "Nil" } });    const cons2 = try pexpr.PExpr.initWithArgs(allocator, .{ .construct = .{ .constructor = "Cons" } }, &[_]*pexpr.PExpr{ pair2_expr, nil_expr });    const list_expr = try pexpr.PExpr.initWithArgs(allocator, .{ .construct = .{ .constructor = "Cons" } }, &[_]*pexpr.PExpr{ pair1_expr, cons2 });    const mk_int_weighted_expr = try pexpr.PExpr.initWithArgs(allocator, .mk_int_weighted, &[_]*pexpr.PExpr{ bitwidth_expr, list_expr });    const result = try compileInner(mk_int_weighted_expr, Env.empty, Bdd.TRUE, &state);    try std.testing.expectEqual(@as(usize, 1), result.worlds.len);    const val = result.worlds[0].value;    try std.testing.expect(val.data == .native);    try std.testing.expect(val.data.native == .int_dist);    const int_dist = val.data.native.int_dist;    try std.testing.expectEqual(@as(usize, 4), int_dist.bits.len);    try std.testing.expect(int_dist.bits[2].isFalse());    try std.testing.expect(int_dist.bits[3].isFalse());    try std.testing.expect(!int_dist.bits[0].isTrue() and !int_dist.bits[0].isFalse());    try std.testing.expect(!int_dist.bits[1].isTrue() and !int_dist.bits[1].isFalse());    try std.testing.expect(manager.eq(int_dist.bits[0], int_dist.bits[1].neg()));}test "mk_int_weighted WMC gives correct probabilities" {    var arena = std.heap.ArenaAllocator.init(std.testing.allocator);    defer arena.deinit();    const allocator = arena.allocator();    var manager = try Manager.init(allocator);    defer manager.deinit();    var defs = pexpr.Definitions.init(allocator);    defer defs.deinit();    var state = try state_module.initChecked(allocator, &manager, &defs, .{});    defer state_module.deinit(&state);    const bitwidth_expr = try pexpr.PExpr.init(allocator, .{ .const_native = .{ .int = 4 } });    const pair1_val = try pexpr.PExpr.init(allocator, .{ .const_native = .{ .int = 3 } });    const pair1_prob = try pexpr.PExpr.init(allocator, .{ .const_native = .{ .float = 0.7 } });    const pair1_expr = try pexpr.PExpr.initWithArgs(allocator, .{ .construct = .{ .constructor = "Pair" } }, &[_]*pexpr.PExpr{ pair1_val, pair1_prob });    const pair2_val = try pexpr.PExpr.init(allocator, .{ .const_native = .{ .int = 5 } });    const pair2_prob = try pexpr.PExpr.init(allocator, .{ .const_native = .{ .float = 0.3 } });    const pair2_expr = try pexpr.PExpr.initWithArgs(allocator, .{ .construct = .{ .constructor = "Pair" } }, &[_]*pexpr.PExpr{ pair2_val, pair2_prob });    const nil_expr = try pexpr.PExpr.init(allocator, .{ .construct = .{ .constructor = "Nil" } });    const cons2 = try pexpr.PExpr.initWithArgs(allocator, .{ .construct = .{ .constructor = "Cons" } }, &[_]*pexpr.PExpr{ pair2_expr, nil_expr });    const list_expr = try pexpr.PExpr.initWithArgs(allocator, .{ .construct = .{ .constructor = "Cons" } }, &[_]*pexpr.PExpr{ pair1_expr, cons2 });    const mk_int_weighted_expr = try pexpr.PExpr.initWithArgs(allocator, .mk_int_weighted, &[_]*pexpr.PExpr{ bitwidth_expr, list_expr });    const result = try compileInner(mk_int_weighted_expr, Env.empty, Bdd.TRUE, &state);    try std.testing.expectEqual(@as(usize, 1), result.worlds.len);    const val = result.worlds[0].value;    try std.testing.expect(val.data == .native);    try std.testing.expect(val.data.native == .int_dist);    const int_dist = val.data.native.int_dist;    const p_bit0 = try wmcForState(&state, int_dist.bits[0]);    const p_bit1 = try wmcForState(&state, int_dist.bits[1]);    const p_bit2 = try wmcForState(&state, int_dist.bits[2]);    const p_bit3 = try wmcForState(&state, int_dist.bits[3]);    try std.testing.expectApproxEqAbs(@as(f64, 1.0), p_bit0, 1e-10);    try std.testing.expectApproxEqAbs(@as(f64, 0.7), p_bit1, 1e-10);    try std.testing.expectApproxEqAbs(@as(f64, 0.3), p_bit2, 1e-10);    try std.testing.expectApproxEqAbs(@as(f64, 0.0), p_bit3, 1e-10);}test "mk_int_weighted int_dist_eq works correctly" {    var arena = std.heap.ArenaAllocator.init(std.testing.allocator);    defer arena.deinit();    const allocator = arena.allocator();    var manager = try Manager.init(allocator);    defer manager.deinit();    var defs = pexpr.Definitions.init(allocator);    defer defs.deinit();    var state = try state_module.initChecked(allocator, &manager, &defs, .{});    defer state_module.deinit(&state);    const bw1 = try pexpr.PExpr.init(allocator, .{ .const_native = .{ .int = 4 } });    const p1_val = try pexpr.PExpr.init(allocator, .{ .const_native = .{ .int = 3 } });    const p1_prob = try pexpr.PExpr.init(allocator, .{ .const_native = .{ .float = 0.6 } });    const p1_expr = try pexpr.PExpr.initWithArgs(allocator, .{ .construct = .{ .constructor = "Pair" } }, &[_]*pexpr.PExpr{ p1_val, p1_prob });    const p2_val = try pexpr.PExpr.init(allocator, .{ .const_native = .{ .int = 5 } });    const p2_prob = try pexpr.PExpr.init(allocator, .{ .const_native = .{ .float = 0.4 } });    const p2_expr = try pexpr.PExpr.initWithArgs(allocator, .{ .construct = .{ .constructor = "Pair" } }, &[_]*pexpr.PExpr{ p2_val, p2_prob });    const nil1 = try pexpr.PExpr.init(allocator, .{ .construct = .{ .constructor = "Nil" } });    const cons2_1 = try pexpr.PExpr.initWithArgs(allocator, .{ .construct = .{ .constructor = "Cons" } }, &[_]*pexpr.PExpr{ p2_expr, nil1 });    const list1 = try pexpr.PExpr.initWithArgs(allocator, .{ .construct = .{ .constructor = "Cons" } }, &[_]*pexpr.PExpr{ p1_expr, cons2_1 });    const weighted_expr = try pexpr.PExpr.initWithArgs(allocator, .mk_int_weighted, &[_]*pexpr.PExpr{ bw1, list1 });    const bw2 = try pexpr.PExpr.init(allocator, .{ .const_native = .{ .int = 4 } });    const v2 = try pexpr.PExpr.init(allocator, .{ .const_native = .{ .int = 3 } });    const const_expr = try pexpr.PExpr.initWithArgs(allocator, .mk_int, &[_]*pexpr.PExpr{ bw2, v2 });    const eq_expr = try pexpr.PExpr.initWithArgs(allocator, .int_dist_eq, &[_]*pexpr.PExpr{ weighted_expr, const_expr });    const result = try compileInner(eq_expr, Env.empty, Bdd.TRUE, &state);    try std.testing.expectEqual(@as(usize, 2), result.worlds.len);    var p_true: f64 = 0;    var p_false: f64 = 0;    for (result.worlds) |world| {        if (world.value.data == .constructed) {            const c = world.value.data.constructed;            const prob = try wmcForState(&state, world.guard);            if (std.mem.eql(u8, c.constructor, "True")) {                p_true = prob;            } else if (std.mem.eql(u8, c.constructor, "False")) {                p_false = prob;            }        }    }    try std.testing.expectApproxEqAbs(@as(f64, 0.6), p_true, 1e-10);    try std.testing.expectApproxEqAbs(@as(f64, 0.4), p_false, 1e-10);}test "mk_int_weighted with three values" {    var arena = std.heap.ArenaAllocator.init(std.testing.allocator);    defer arena.deinit();    const allocator = arena.allocator();    var manager = try Manager.init(allocator);    defer manager.deinit();    var defs = pexpr.Definitions.init(allocator);    defer defs.deinit();    var state = try state_module.initChecked(allocator, &manager, &defs, .{});    defer state_module.deinit(&state);    const bitwidth_expr = try pexpr.PExpr.init(allocator, .{ .const_native = .{ .int = 4 } });    const pair1_val = try pexpr.PExpr.init(allocator, .{ .const_native = .{ .int = 1 } });    const pair1_prob = try pexpr.PExpr.init(allocator, .{ .const_native = .{ .float = 0.5 } });    const pair1_expr = try pexpr.PExpr.initWithArgs(allocator, .{ .construct = .{ .constructor = "Pair" } }, &[_]*pexpr.PExpr{ pair1_val, pair1_prob });    const pair2_val = try pexpr.PExpr.init(allocator, .{ .const_native = .{ .int = 2 } });    const pair2_prob = try pexpr.PExpr.init(allocator, .{ .const_native = .{ .float = 0.3 } });    const pair2_expr = try pexpr.PExpr.initWithArgs(allocator, .{ .construct = .{ .constructor = "Pair" } }, &[_]*pexpr.PExpr{ pair2_val, pair2_prob });    const pair3_val = try pexpr.PExpr.init(allocator, .{ .const_native = .{ .int = 3 } });    const pair3_prob = try pexpr.PExpr.init(allocator, .{ .const_native = .{ .float = 0.2 } });    const pair3_expr = try pexpr.PExpr.initWithArgs(allocator, .{ .construct = .{ .constructor = "Pair" } }, &[_]*pexpr.PExpr{ pair3_val, pair3_prob });    const nil_expr = try pexpr.PExpr.init(allocator, .{ .construct = .{ .constructor = "Nil" } });    const cons3 = try pexpr.PExpr.initWithArgs(allocator, .{ .construct = .{ .constructor = "Cons" } }, &[_]*pexpr.PExpr{ pair3_expr, nil_expr });    const cons2 = try pexpr.PExpr.initWithArgs(allocator, .{ .construct = .{ .constructor = "Cons" } }, &[_]*pexpr.PExpr{ pair2_expr, cons3 });    const list_expr = try pexpr.PExpr.initWithArgs(allocator, .{ .construct = .{ .constructor = "Cons" } }, &[_]*pexpr.PExpr{ pair1_expr, cons2 });    const mk_int_weighted_expr = try pexpr.PExpr.initWithArgs(allocator, .mk_int_weighted, &[_]*pexpr.PExpr{ bitwidth_expr, list_expr });    const result = try compileInner(mk_int_weighted_expr, Env.empty, Bdd.TRUE, &state);    try std.testing.expectEqual(@as(usize, 1), result.worlds.len);    const val = result.worlds[0].value;    try std.testing.expect(val.data == .native);    try std.testing.expect(val.data.native == .int_dist);    const int_dist = val.data.native.int_dist;    const p_bit0 = try wmcForState(&state, int_dist.bits[0]);    const p_bit1 = try wmcForState(&state, int_dist.bits[1]);    const p_bit2 = try wmcForState(&state, int_dist.bits[2]);    const p_bit3 = try wmcForState(&state, int_dist.bits[3]);    try std.testing.expectApproxEqAbs(@as(f64, 0.7), p_bit0, 1e-10);    try std.testing.expectApproxEqAbs(@as(f64, 0.5), p_bit1, 1e-10);    try std.testing.expectApproxEqAbs(@as(f64, 0.0), p_bit2, 1e-10);    try std.testing.expectApproxEqAbs(@as(f64, 0.0), p_bit3, 1e-10);}test "mk_int_weighted with four values verifies cascade correctness" {    var arena = std.heap.ArenaAllocator.init(std.testing.allocator);    defer arena.deinit();    const allocator = arena.allocator();    var manager = try Manager.init(allocator);    defer manager.deinit();    var defs = pexpr.Definitions.init(allocator);    defer defs.deinit();    var state = try state_module.initChecked(allocator, &manager, &defs, .{});    defer state_module.deinit(&state);    const bitwidth_expr = try pexpr.PExpr.init(allocator, .{ .const_native = .{ .int = 4 } });    const pair0_val = try pexpr.PExpr.init(allocator, .{ .const_native = .{ .int = 0 } });    const pair0_prob = try pexpr.PExpr.init(allocator, .{ .const_native = .{ .float = 0.1 } });    const pair0_expr = try pexpr.PExpr.initWithArgs(allocator, .{ .construct = .{ .constructor = "Pair" } }, &[_]*pexpr.PExpr{ pair0_val, pair0_prob });    const pair1_val = try pexpr.PExpr.init(allocator, .{ .const_native = .{ .int = 1 } });    const pair1_prob = try pexpr.PExpr.init(allocator, .{ .const_native = .{ .float = 0.2 } });    const pair1_expr = try pexpr.PExpr.initWithArgs(allocator, .{ .construct = .{ .constructor = "Pair" } }, &[_]*pexpr.PExpr{ pair1_val, pair1_prob });    const pair2_val = try pexpr.PExpr.init(allocator, .{ .const_native = .{ .int = 2 } });    const pair2_prob = try pexpr.PExpr.init(allocator, .{ .const_native = .{ .float = 0.3 } });    const pair2_expr = try pexpr.PExpr.initWithArgs(allocator, .{ .construct = .{ .constructor = "Pair" } }, &[_]*pexpr.PExpr{ pair2_val, pair2_prob });    const pair3_val = try pexpr.PExpr.init(allocator, .{ .const_native = .{ .int = 3 } });    const pair3_prob = try pexpr.PExpr.init(allocator, .{ .const_native = .{ .float = 0.4 } });    const pair3_expr = try pexpr.PExpr.initWithArgs(allocator, .{ .construct = .{ .constructor = "Pair" } }, &[_]*pexpr.PExpr{ pair3_val, pair3_prob });    const nil_expr = try pexpr.PExpr.init(allocator, .{ .construct = .{ .constructor = "Nil" } });    const cons3 = try pexpr.PExpr.initWithArgs(allocator, .{ .construct = .{ .constructor = "Cons" } }, &[_]*pexpr.PExpr{ pair3_expr, nil_expr });    const cons2 = try pexpr.PExpr.initWithArgs(allocator, .{ .construct = .{ .constructor = "Cons" } }, &[_]*pexpr.PExpr{ pair2_expr, cons3 });    const cons1 = try pexpr.PExpr.initWithArgs(allocator, .{ .construct = .{ .constructor = "Cons" } }, &[_]*pexpr.PExpr{ pair1_expr, cons2 });    const list_expr = try pexpr.PExpr.initWithArgs(allocator, .{ .construct = .{ .constructor = "Cons" } }, &[_]*pexpr.PExpr{ pair0_expr, cons1 });    const mk_int_weighted_expr = try pexpr.PExpr.initWithArgs(allocator, .mk_int_weighted, &[_]*pexpr.PExpr{ bitwidth_expr, list_expr });    const result = try compileInner(mk_int_weighted_expr, Env.empty, Bdd.TRUE, &state);    try std.testing.expectEqual(@as(usize, 1), result.worlds.len);    const val = result.worlds[0].value;    try std.testing.expect(val.data == .native);    try std.testing.expect(val.data.native == .int_dist);    const int_dist = val.data.native.int_dist;    const p_bit0 = try wmcForState(&state, int_dist.bits[0]);    const p_bit1 = try wmcForState(&state, int_dist.bits[1]);    const p_bit2 = try wmcForState(&state, int_dist.bits[2]);    const p_bit3 = try wmcForState(&state, int_dist.bits[3]);    try std.testing.expectApproxEqAbs(@as(f64, 0.6), p_bit0, 1e-10);    try std.testing.expectApproxEqAbs(@as(f64, 0.7), p_bit1, 1e-10);    try std.testing.expectApproxEqAbs(@as(f64, 0.0), p_bit2, 1e-10);    try std.testing.expectApproxEqAbs(@as(f64, 0.0), p_bit3, 1e-10);}test "mk_int_weighted validates negative probabilities" {    var arena = std.heap.ArenaAllocator.init(std.testing.allocator);    defer arena.deinit();    const allocator = arena.allocator();    var manager = try Manager.init(allocator);    defer manager.deinit();    var defs = pexpr.Definitions.init(allocator);    defer defs.deinit();    var state = try state_module.initChecked(allocator, &manager, &defs, .{});    defer state_module.deinit(&state);    const bitwidth_expr = try pexpr.PExpr.init(allocator, .{ .const_native = .{ .int = 4 } });    const pair1_val = try pexpr.PExpr.init(allocator, .{ .const_native = .{ .int = 1 } });    const pair1_prob = try pexpr.PExpr.init(allocator, .{ .const_native = .{ .float = -0.5 } });    const pair1_expr = try pexpr.PExpr.initWithArgs(allocator, .{ .construct = .{ .constructor = "Pair" } }, &[_]*pexpr.PExpr{ pair1_val, pair1_prob });    const pair2_val = try pexpr.PExpr.init(allocator, .{ .const_native = .{ .int = 2 } });    const pair2_prob = try pexpr.PExpr.init(allocator, .{ .const_native = .{ .float = 0.5 } });    const pair2_expr = try pexpr.PExpr.initWithArgs(allocator, .{ .construct = .{ .constructor = "Pair" } }, &[_]*pexpr.PExpr{ pair2_val, pair2_prob });    const nil_expr = try pexpr.PExpr.init(allocator, .{ .construct = .{ .constructor = "Nil" } });    const cons2 = try pexpr.PExpr.initWithArgs(allocator, .{ .construct = .{ .constructor = "Cons" } }, &[_]*pexpr.PExpr{ pair2_expr, nil_expr });    const list_expr = try pexpr.PExpr.initWithArgs(allocator, .{ .construct = .{ .constructor = "Cons" } }, &[_]*pexpr.PExpr{ pair1_expr, cons2 });    const mk_int_weighted_expr = try pexpr.PExpr.initWithArgs(allocator, .mk_int_weighted, &[_]*pexpr.PExpr{ bitwidth_expr, list_expr });    const result = try compileInner(mk_int_weighted_expr, Env.empty, Bdd.TRUE, &state);    try std.testing.expectEqual(@as(usize, 0), result.worlds.len);}test "mk_int_weighted validates infinite probabilities" {    var arena = std.heap.ArenaAllocator.init(std.testing.allocator);    defer arena.deinit();    const allocator = arena.allocator();    var manager = try Manager.init(allocator);    defer manager.deinit();    var defs = pexpr.Definitions.init(allocator);    defer defs.deinit();    var state = try state_module.initChecked(allocator, &manager, &defs, .{});    defer state_module.deinit(&state);    const bitwidth_expr = try pexpr.PExpr.init(allocator, .{ .const_native = .{ .int = 4 } });    const pair1_val = try pexpr.PExpr.init(allocator, .{ .const_native = .{ .int = 1 } });    const pair1_prob = try pexpr.PExpr.init(allocator, .{ .const_native = .{ .float = std.math.inf(f64) } });    const pair1_expr = try pexpr.PExpr.initWithArgs(allocator, .{ .construct = .{ .constructor = "Pair" } }, &[_]*pexpr.PExpr{ pair1_val, pair1_prob });    const pair2_val = try pexpr.PExpr.init(allocator, .{ .const_native = .{ .int = 2 } });    const pair2_prob = try pexpr.PExpr.init(allocator, .{ .const_native = .{ .float = 0.5 } });    const pair2_expr = try pexpr.PExpr.initWithArgs(allocator, .{ .construct = .{ .constructor = "Pair" } }, &[_]*pexpr.PExpr{ pair2_val, pair2_prob });    const nil_expr = try pexpr.PExpr.init(allocator, .{ .construct = .{ .constructor = "Nil" } });    const cons2 = try pexpr.PExpr.initWithArgs(allocator, .{ .construct = .{ .constructor = "Cons" } }, &[_]*pexpr.PExpr{ pair2_expr, nil_expr });    const list_expr = try pexpr.PExpr.initWithArgs(allocator, .{ .construct = .{ .constructor = "Cons" } }, &[_]*pexpr.PExpr{ pair1_expr, cons2 });    const mk_int_weighted_expr = try pexpr.PExpr.initWithArgs(allocator, .mk_int_weighted, &[_]*pexpr.PExpr{ bitwidth_expr, list_expr });    const result_inf = try compileInner(mk_int_weighted_expr, Env.empty, Bdd.TRUE, &state);    try std.testing.expectEqual(@as(usize, 0), result_inf.worlds.len);}test "parallel WMC respects threshold" {    var arena = std.heap.ArenaAllocator.init(std.testing.allocator);    defer arena.deinit();    const allocator = arena.allocator();    var manager = try Manager.init(allocator);    defer manager.deinit();    var defs = pexpr.Definitions.init(allocator);    defer defs.deinit();    const prob_expr = try pexpr.PExpr.init(allocator, .{ .const_native = .{ .float = 0.5 } });    const flip_expr = try pexpr.PExpr.initWithArgs(allocator, .flip, &[_]*pexpr.PExpr{prob_expr});    const result_seq = try compile(allocator, allocator, flip_expr, &defs, &manager, .{        .parallel_wmc = false,    });    const result_high_thresh = try compile(allocator, allocator, flip_expr, &defs, &manager, .{        .parallel_wmc = true,        .parallel_wmc_threshold = 100,    });    try std.testing.expectEqual(@as(usize, 2), result_seq.weighted_results.len);    try std.testing.expectEqual(@as(usize, 2), result_high_thresh.weighted_results.len);    var total_seq: f64 = 0;    var total_high: f64 = 0;    for (result_seq.weighted_results) |wr| {        total_seq += wr.probability;    }    for (result_high_thresh.weighted_results) |wr| {        total_high += wr.probability;    }    try std.testing.expectApproxEqAbs(@as(f64, 1.0), total_seq, 1e-10);    try std.testing.expectApproxEqAbs(@as(f64, 1.0), total_high, 1e-10);}test "thunk cache respects path condition - regression for pluck-rs-0cb" {    var arena = std.heap.ArenaAllocator.init(std.testing.allocator);    defer arena.deinit();    const allocator = arena.allocator();    var manager = try Manager.init(allocator);    defer manager.deinit();    var defs = pexpr.Definitions.init(allocator);    defer defs.deinit();    var state = try state_module.initChecked(allocator, &manager, &defs, .{});    defer state_module.deinit(&state);    const b_var = try manager.newVar(true);    const not_b_var = b_var.neg();    const expr1 = try pexpr.PExpr.init(allocator, .{ .const_native = .{ .int = 1 } });    const thunk1 = try makeThunk(allocator, expr1, Env.empty, 0, &state);    _ = try evaluateLazyKCThunk(allocator, thunk1, b_var, &state);    try std.testing.expectEqual(@as(usize, 1), thunk1.cache.items.len);    try std.testing.expect(thunk1.cache.items[0].validity_guard.isTrue());    const implies_check = try manager.bddImplies(not_b_var, thunk1.cache.items[0].validity_guard);    try std.testing.expect(implies_check.isTrue());    const expr2 = try pexpr.PExpr.init(allocator, .{ .const_native = .{ .int = 42 } });    const thunk2 = try makeThunk(allocator, expr2, Env.empty, 0, &state);    _ = try evaluateLazyKCThunk(allocator, thunk2, b_var, &state);    try std.testing.expectEqual(@as(usize, 1), thunk2.cache.items.len);    const result2_notb_first = try evaluateLazyKCThunk(allocator, thunk2, not_b_var, &state);    try std.testing.expectEqual(@as(usize, 1), thunk2.cache.items.len);    try std.testing.expect(result2_notb_first.worlds.len >= 1);    for (result2_notb_first.worlds) |world| {        try std.testing.expectEqual(@as(i64, 42), world.value.data.native.int);    }    const result2_b_after = try evaluateLazyKCThunk(allocator, thunk2, b_var, &state);    try std.testing.expect(result2_b_after.worlds.len >= 1);    for (result2_b_after.worlds) |world| {        try std.testing.expectEqual(@as(i64, 42), world.value.data.native.int);    }    const d_var = try manager.newVar(true);    const e_var = try manager.newVar(true);    const expr4 = try pexpr.PExpr.init(allocator, .{ .const_native = .{ .int = 77 } });    const thunk4 = try makeThunk(allocator, expr4, Env.empty, 0, &state);    _ = try evaluateLazyKCThunk(allocator, thunk4, d_var, &state);    try std.testing.expect(thunk4.cache.items[0].validity_guard.isTrue());    const implies_e = try manager.bddImplies(e_var, thunk4.cache.items[0].validity_guard);    try std.testing.expect(implies_e.isTrue());    const result4_e = try evaluateLazyKCThunk(allocator, thunk4, e_var, &state);    for (result4_e.worlds) |world| {        try std.testing.expectEqual(@as(i64, 77), world.value.data.native.int);    }    try std.testing.expect(thunk4.cache.items[0].validity_guard.isTrue());    const not_d = d_var.neg();    const not_e = e_var.neg();    const third_path = try state.manager.bddAnd(not_d, not_e);    const implies_third = try state.manager.bddImplies(third_path, thunk4.cache.items[0].validity_guard);    try std.testing.expect(implies_third.isTrue());    const result4_third = try evaluateLazyKCThunk(allocator, thunk4, third_path, &state);    for (result4_third.worlds) |world| {        try std.testing.expectEqual(@as(i64, 77), world.value.data.native.int);    }}test "thunk cache - repro program level regression for pluck-rs-0cb" {    var arena = std.heap.ArenaAllocator.init(std.testing.allocator);    defer arena.deinit();    const allocator = arena.allocator();    var manager = try Manager.init(allocator);    defer manager.deinit();    var types = try pexpr.TypeRegistry.initWithDefaults(allocator);    defer types.deinit();    var defs = pexpr.Definitions.init(allocator);    defer defs.deinit();    const expr = try pexpr.parseExpr(allocator, "(let [b (flip 0.5) x (if b 1 2)] (case b of True => x | False => x))", &types, &defs);    const result = try compile(allocator, allocator, expr, &defs, &manager, .{ .full_dist = true });    var prob_1: f64 = 0.0;    var prob_2: f64 = 0.0;    var found_count: usize = 0;    for (result.weighted_results) |wr| {        const val = wr.value;        if (val.data == .native) {            switch (val.data.native) {                .int => |int_val| {                    found_count += 1;                    if (int_val == 1) {                        prob_1 += wr.probability;                    } else if (int_val == 2) {                        prob_2 += wr.probability;                    }                },                else => {},            }        } else if (val.data == .constructed) {            const ctor = val.data.constructed.constructor;            if (std.mem.eql(u8, ctor, "S") or std.mem.eql(u8, ctor, "O")) {                var nat_val: i64 = 0;                var current = val;                while (current.data == .constructed and                    std.mem.eql(u8, current.data.constructed.constructor, "S"))                {                    nat_val += 1;                    current = current.data.constructed.args[0];                }                found_count += 1;                if (nat_val == 1) {                    prob_1 += wr.probability;                } else if (nat_val == 2) {                    prob_2 += wr.probability;                }            }        }    }    try std.testing.expectEqual(@as(usize, 2), found_count);    try std.testing.expectApproxEqAbs(@as(f64, 0.5), prob_1, 1e-10);    try std.testing.expectApproxEqAbs(@as(f64, 0.5), prob_2, 1e-10);}test "factor supports guarded weights" {    var arena = std.heap.ArenaAllocator.init(std.testing.allocator);    defer arena.deinit();    const allocator = arena.allocator();    var manager = try Manager.init(allocator);    defer manager.deinit();    var types = try pexpr.TypeRegistry.initWithDefaults(allocator);    defer types.deinit();    var defs = pexpr.Definitions.init(allocator);    defer defs.deinit();    const expr_src =        "(let [b (flip 0.5)] " ++        "(case (factor (if b 0.2 0.8)) of Unit => b))";    const expr = try pexpr.parseExpr(allocator, expr_src, &types, &defs);    const result = try compile(allocator, allocator, expr, &defs, &manager, .{ .full_dist = true });    var prob_true: f64 = 0.0;    var prob_false: f64 = 0.0;    var total: f64 = 0.0;    for (result.weighted_results) |wr| {        total += wr.probability;        if (wr.value.isTrue()) prob_true += wr.probability;        if (wr.value.isFalse()) prob_false += wr.probability;    }    try std.testing.expect(total > 0.0);    try std.testing.expectApproxEqAbs(@as(f64, 0.2), prob_true / total, 1e-10);    try std.testing.expectApproxEqAbs(@as(f64, 0.8), prob_false / total, 1e-10);}test "factor defers WeightDD refinement when node limit is small" {    var arena = std.heap.ArenaAllocator.init(std.testing.allocator);    defer arena.deinit();    const allocator = arena.allocator();    var manager = try Manager.init(allocator);    defer manager.deinit();    var types = try pexpr.TypeRegistry.initWithDefaults(allocator);    defer types.deinit();    var defs = pexpr.Definitions.init(allocator);    defer defs.deinit();    const expr_src =        "(let [b (flip 0.5)] " ++        "(case (factor (if b 0.2 0.8)) of Unit => b))";    const expr = try pexpr.parseExpr(allocator, expr_src, &types, &defs);    const result = try compile(allocator, allocator, expr, &defs, &manager, .{        .full_dist = true,        .factor_max_branches = 1,        .weight_dd_max_nodes = 2,    });    try std.testing.expect(result.stats.limit_reason == null);    var prob_true: f64 = 0.0;    var prob_false: f64 = 0.0;    var total: f64 = 0.0;    for (result.weighted_results) |wr| {        total += wr.probability;        if (wr.value.isTrue()) prob_true += wr.probability;        if (wr.value.isFalse()) prob_false += wr.probability;    }    try std.testing.expect(total > 0.0);    try std.testing.expectApproxEqAbs(@as(f64, 0.2), prob_true / total, 1e-10);    try std.testing.expectApproxEqAbs(@as(f64, 0.8), prob_false / total, 1e-10);}test "factor prunes zero-weight branch" {    var arena = std.heap.ArenaAllocator.init(std.testing.allocator);    defer arena.deinit();    const allocator = arena.allocator();    var manager = try Manager.init(allocator);    defer manager.deinit();    var types = try pexpr.TypeRegistry.initWithDefaults(allocator);    defer types.deinit();    var defs = pexpr.Definitions.init(allocator);    defer defs.deinit();    const expr_src =        "(let [b (flip 0.5)] " ++        "(case (factor (if b 0.0 1.0)) of Unit => b))";    const expr = try pexpr.parseExpr(allocator, expr_src, &types, &defs);    const result = try compile(allocator, allocator, expr, &defs, &manager, .{ .full_dist = true });    var prob_true: f64 = 0.0;    var prob_false: f64 = 0.0;    var total: f64 = 0.0;    for (result.weighted_results) |wr| {        total += wr.probability;        if (wr.value.isTrue()) prob_true += wr.probability;        if (wr.value.isFalse()) prob_false += wr.probability;    }    try std.testing.expect(total > 0.0);    try std.testing.expectApproxEqAbs(@as(f64, 0.0), prob_true / total, 1e-10);    try std.testing.expectApproxEqAbs(@as(f64, 1.0), prob_false / total, 1e-10);}test "factor WeightDD composes multiple factors" {    var arena = std.heap.ArenaAllocator.init(std.testing.allocator);    defer arena.deinit();    const allocator = arena.allocator();    var manager = try Manager.init(allocator);    defer manager.deinit();    var types = try pexpr.TypeRegistry.initWithDefaults(allocator);    defer types.deinit();    var defs = pexpr.Definitions.init(allocator);    defer defs.deinit();    const expr_src =        "(let [b (flip 0.5)] " ++        "(case (factor (if b 0.2 0.8)) of Unit => " ++        "(case (factor (if b 0.5 1.5)) of Unit => b)))";    const expr = try pexpr.parseExpr(allocator, expr_src, &types, &defs);    const result = try compile(allocator, allocator, expr, &defs, &manager, .{        .full_dist = true,        .factor_max_branches = 1,    });    var prob_true: f64 = 0.0;    var prob_false: f64 = 0.0;    var total: f64 = 0.0;    for (result.weighted_results) |wr| {        total += wr.probability;        if (wr.value.isTrue()) prob_true += wr.probability;        if (wr.value.isFalse()) prob_false += wr.probability;    }    try std.testing.expect(total > 0.0);    const norm_true = prob_true / total;    const norm_false = prob_false / total;    try std.testing.expectApproxEqAbs(@as(f64, 0.07692307692307693), norm_true, 1e-10);    try std.testing.expectApproxEqAbs(@as(f64, 0.9230769230769231), norm_false, 1e-10);}test "factor WeightDD respects branch guards" {    var arena = std.heap.ArenaAllocator.init(std.testing.allocator);    defer arena.deinit();    const allocator = arena.allocator();    var manager = try Manager.init(allocator);    defer manager.deinit();    var types = try pexpr.TypeRegistry.initWithDefaults(allocator);    defer types.deinit();    var defs = pexpr.Definitions.init(allocator);    defer defs.deinit();    const expr_src =        "(let [b (flip 0.5) c (flip 0.5)] " ++        "(if c (case (factor (if b 0.2 0.8)) of Unit => b) b))";    const expr = try pexpr.parseExpr(allocator, expr_src, &types, &defs);    const result = try compile(allocator, allocator, expr, &defs, &manager, .{        .full_dist = true,        .factor_max_branches = 1,    });    var prob_true: f64 = 0.0;    var prob_false: f64 = 0.0;    var total: f64 = 0.0;    for (result.weighted_results) |wr| {        total += wr.probability;        if (wr.value.isTrue()) prob_true += wr.probability;        if (wr.value.isFalse()) prob_false += wr.probability;    }    try std.testing.expect(total > 0.0);    const norm_true = prob_true / total;    const norm_false = prob_false / total;    try std.testing.expectApproxEqAbs(@as(f64, 0.4), norm_true, 1e-10);    try std.testing.expectApproxEqAbs(@as(f64, 0.6), norm_false, 1e-10);}fn expectGuardsCoverAndDisjoint(manager: *Manager, guards: []const GuardedWeight) !void {    var coverage = Bdd.FALSE;    for (guards) |entry| {        coverage = try manager.bddOr(coverage, entry.guard);    }    try std.testing.expect(coverage.isTrue());    for (guards, 0..) |entry, i| {        var j: usize = i + 1;        while (j < guards.len) : (j += 1) {            const overlap = try manager.bddAnd(entry.guard, guards[j].guard);            try std.testing.expect(overlap.isFalse());        }    }}fn expectWeightSet(guards: []const GuardedWeight, expected: []const f64) !void {    for (expected) |target| {        var found = false;        for (guards) |entry| {            if (@abs(entry.weight - target) < 1e-12) {                found = true;                break;            }        }        try std.testing.expect(found);    }}test "symbolic weight compiler handles boolean chain" {    var arena = std.heap.ArenaAllocator.init(std.testing.allocator);    defer arena.deinit();    const allocator = arena.allocator();    var manager = try Manager.init(allocator);    defer manager.deinit();    var types = try pexpr.TypeRegistry.initWithDefaults(allocator);    defer types.deinit();    var defs = pexpr.Definitions.init(allocator);    defer defs.deinit();    const expr_src =        "(let [a (flip 0.5) b (flip 0.5)] " ++        "(if a 0.2 (if b 0.3 0.5)))";    const expr = try pexpr.parseExpr(allocator, expr_src, &types, &defs);    var state = try state_module.initChecked(allocator, &manager, &defs, .{});    defer state_module.deinit(&state);    const result = try compileInner(expr, Env.empty, Bdd.TRUE, &state);    defer freeWorldsSlice(allocator, result.worlds);    const symbolic = try compileWeightSymbolic(allocator, result.worlds);    switch (symbolic) {        .ok => |guards| {            defer allocator.free(guards);            try std.testing.expectEqual(@as(usize, 3), guards.len);            try expectWeightSet(guards, &[_]f64{ 0.2, 0.3, 0.5 });            try expectGuardsCoverAndDisjoint(&manager, guards);        },        else => try std.testing.expect(false),    }}test "symbolic weight compiler handles int_dist_eq" {    var arena = std.heap.ArenaAllocator.init(std.testing.allocator);    defer arena.deinit();    const allocator = arena.allocator();    var manager = try Manager.init(allocator);    defer manager.deinit();    var types = try pexpr.TypeRegistry.initWithDefaults(allocator);    defer types.deinit();    try types.defineType("pair", &.{        .{ .name = "Pair", .args = &.{ "nat", "nat" } },    });    var defs = pexpr.Definitions.init(allocator);    defer defs.deinit();    const expr_src =        "(let [x (mk_int_weighted @2 [(Pair @1 0.5) (Pair @3 0.5)])] " ++        "(if (int_dist_eq x (mk_int @2 @1)) 0.1 0.9))";    const expr = try pexpr.parseExpr(allocator, expr_src, &types, &defs);    var state = try state_module.initChecked(allocator, &manager, &defs, .{});    defer state_module.deinit(&state);    const result = try compileInner(expr, Env.empty, Bdd.TRUE, &state);    defer freeWorldsSlice(allocator, result.worlds);    const symbolic = try compileWeightSymbolic(allocator, result.worlds);    switch (symbolic) {        .ok => |guards| {            defer allocator.free(guards);            try std.testing.expectEqual(@as(usize, 2), guards.len);            try expectWeightSet(guards, &[_]f64{ 0.1, 0.9 });            try expectGuardsCoverAndDisjoint(&manager, guards);        },        else => try std.testing.expect(false),    }}test "symbolic weight compiler handles nested if/case" {    var arena = std.heap.ArenaAllocator.init(std.testing.allocator);    defer arena.deinit();    const allocator = arena.allocator();    var manager = try Manager.init(allocator);    defer manager.deinit();    var types = try pexpr.TypeRegistry.initWithDefaults(allocator);    defer types.deinit();    var defs = pexpr.Definitions.init(allocator);    defer defs.deinit();    const expr_src =        "(let [b (flip 0.5) c (flip 0.5)] " ++        "(case b of True => (if c 0.2 0.4) | " ++        "False => (case c of True => 0.6 | False => 0.8)))";    const expr = try pexpr.parseExpr(allocator, expr_src, &types, &defs);    var state = try state_module.initChecked(allocator, &manager, &defs, .{});    defer state_module.deinit(&state);    const result = try compileInner(expr, Env.empty, Bdd.TRUE, &state);    defer freeWorldsSlice(allocator, result.worlds);    const symbolic = try compileWeightSymbolic(allocator, result.worlds);    switch (symbolic) {        .ok => |guards| {            defer allocator.free(guards);            try std.testing.expectEqual(@as(usize, 4), guards.len);            try expectWeightSet(guards, &[_]f64{ 0.2, 0.4, 0.6, 0.8 });            try expectGuardsCoverAndDisjoint(&manager, guards);        },        else => try std.testing.expect(false),    }}test "IntDist enumeration - deterministic value" {    const allocator = std.testing.allocator;    var manager = try Manager.init(allocator);    defer manager.deinit();    var defs = pexpr.Definitions.init(allocator);    defer defs.deinit();    const bitwidth_expr = try pexpr.PExpr.init(allocator, .{ .const_native = .{ .int = 4 } });    const value_expr = try pexpr.PExpr.init(allocator, .{ .const_native = .{ .int = 5 } });    const mk_int_expr = try pexpr.PExpr.initWithArgs(allocator, .mk_int, &[_]*pexpr.PExpr{ bitwidth_expr, value_expr });    defer mk_int_expr.deinit(allocator);    const result = try compile(allocator, allocator, mk_int_expr, &defs, &manager, .{});    defer {        for (result.weighted_results) |wr| {            wr.value.deinit(allocator);        }        allocator.free(result.weighted_results);        if (result.raw_worlds) |worlds| {            allocator.free(worlds);        }    }    try std.testing.expectEqual(@as(usize, 1), result.weighted_results.len);    const wr = result.weighted_results[0];    try std.testing.expect(wr.value.data == .native);    try std.testing.expect(wr.value.data.native == .int);    try std.testing.expectEqual(@as(i64, 5), wr.value.data.native.int);    try std.testing.expectApproxEqAbs(@as(f64, 1.0), wr.probability, 1e-10);}test "IntDist enumeration - weighted values" {    var arena = std.heap.ArenaAllocator.init(std.testing.allocator);    defer arena.deinit();    const allocator = arena.allocator();    var manager = try Manager.init(allocator);    defer manager.deinit();    var defs = pexpr.Definitions.init(allocator);    defer defs.deinit();    const bitwidth_expr = try pexpr.PExpr.init(allocator, .{ .const_native = .{ .int = 3 } });    const val1 = try pexpr.PExpr.init(allocator, .{ .const_native = .{ .int = 2 } });    const prob1 = try pexpr.PExpr.init(allocator, .{ .const_native = .{ .float = 0.3 } });    const pair1 = try pexpr.PExpr.initWithArgs(allocator, .{ .construct = .{ .constructor = "Pair" } }, &[_]*pexpr.PExpr{ val1, prob1 });    const val2 = try pexpr.PExpr.init(allocator, .{ .const_native = .{ .int = 5 } });    const prob2 = try pexpr.PExpr.init(allocator, .{ .const_native = .{ .float = 0.7 } });    const pair2 = try pexpr.PExpr.initWithArgs(allocator, .{ .construct = .{ .constructor = "Pair" } }, &[_]*pexpr.PExpr{ val2, prob2 });    const nil = try pexpr.PExpr.initWithArgs(allocator, .{ .construct = .{ .constructor = "Nil" } }, &[_]*pexpr.PExpr{});    const cons2 = try pexpr.PExpr.initWithArgs(allocator, .{ .construct = .{ .constructor = "Cons" } }, &[_]*pexpr.PExpr{ pair2, nil });    const pairs_list = try pexpr.PExpr.initWithArgs(allocator, .{ .construct = .{ .constructor = "Cons" } }, &[_]*pexpr.PExpr{ pair1, cons2 });    const mk_int_weighted_expr = try pexpr.PExpr.initWithArgs(allocator, .mk_int_weighted, &[_]*pexpr.PExpr{ bitwidth_expr, pairs_list });    const result = try compile(allocator, allocator, mk_int_weighted_expr, &defs, &manager, .{});    try std.testing.expectEqual(@as(usize, 2), result.weighted_results.len);    var prob_2: f64 = 0;    var prob_5: f64 = 0;    for (result.weighted_results) |wr| {        if (wr.value.data == .native and wr.value.data.native == .int) {            if (wr.value.data.native.int == 2) {                prob_2 = wr.probability;            } else if (wr.value.data.native.int == 5) {                prob_5 = wr.probability;            }        }    }    try std.testing.expectApproxEqAbs(@as(f64, 0.3), prob_2, 1e-10);    try std.testing.expectApproxEqAbs(@as(f64, 0.7), prob_5, 1e-10);}test "combineIntDists - combines two IntDists under different guards" {    const allocator = std.testing.allocator;    var manager = try Manager.init(allocator);    defer manager.deinit();    const g = try manager.newVar(true);    const bits1 = try allocator.alloc(Bdd, 3);    defer allocator.free(bits1);    bits1[0] = Bdd.FALSE;    bits1[1] = Bdd.TRUE;    bits1[2] = Bdd.FALSE;    const bits2 = try allocator.alloc(Bdd, 3);    defer allocator.free(bits2);    bits2[0] = Bdd.TRUE;    bits2[1] = Bdd.FALSE;    bits2[2] = Bdd.TRUE;    const pairs = try allocator.alloc(IntDistWithGuard, 2);    defer allocator.free(pairs);    pairs[0] = .{ .int_dist = runtime.IntDist.init(bits1), .guard = g };    pairs[1] = .{ .int_dist = runtime.IntDist.init(bits2), .guard = g.neg() };    const combined = try combineIntDists(allocator, pairs, &manager);    defer allocator.free(combined.int_dist.bits);    try std.testing.expect(combined.overall_guard.isTrue());    try std.testing.expectEqual(combined.int_dist.bits.len, 3);    try std.testing.expectEqual(combined.int_dist.bits[0].toRaw(), g.neg().toRaw());    try std.testing.expectEqual(combined.int_dist.bits[1].toRaw(), g.toRaw());    try std.testing.expectEqual(combined.int_dist.bits[2].toRaw(), g.neg().toRaw());}test "enumerateIntDist - deterministic value" {    const allocator = std.testing.allocator;    var manager = try Manager.init(allocator);    defer manager.deinit();    const bits = try allocator.alloc(Bdd, 2);    defer allocator.free(bits);    bits[0] = Bdd.TRUE;    bits[1] = Bdd.TRUE;    const int_dist = runtime.IntDist.init(bits);    const results = try enumerateIntDist(allocator, int_dist, Bdd.TRUE, &manager);    defer {        for (results) |world| {            world.value.deinit(allocator);        }        allocator.free(results);    }    try std.testing.expectEqual(@as(usize, 1), results.len);    try std.testing.expect(results[0].value.data == .native);    try std.testing.expect(results[0].value.data.native == .int);    try std.testing.expectEqual(@as(i64, 3), results[0].value.data.native.int);    try std.testing.expect(results[0].guard.isTrue());}test "enumerateIntDist - non-deterministic" {    const allocator = std.testing.allocator;    var manager = try Manager.init(allocator);    defer manager.deinit();    const x = try manager.newVar(true);    const bits = try allocator.alloc(Bdd, 1);    defer allocator.free(bits);    bits[0] = x;    const int_dist = runtime.IntDist.init(bits);    const results = try enumerateIntDist(allocator, int_dist, Bdd.TRUE, &manager);    defer {        for (results) |world| {            world.value.deinit(allocator);        }        allocator.free(results);    }    try std.testing.expectEqual(@as(usize, 2), results.len);    var found_0 = false;    var found_1 = false;    for (results) |world| {        const val = world.value.data.native.int;        if (val == 0) {            try std.testing.expectEqual(world.guard.toRaw(), x.neg().toRaw());            found_0 = true;        } else if (val == 1) {            try std.testing.expectEqual(world.guard.toRaw(), x.toRaw());            found_1 = true;        }    }    try std.testing.expect(found_0);    try std.testing.expect(found_1);}test "get_constructor extracts constructor name from ADT value" {    var arena = std.heap.ArenaAllocator.init(std.testing.allocator);    defer arena.deinit();    const allocator = arena.allocator();    var manager = try Manager.init(allocator);    defer manager.deinit();    var defs = pexpr.Definitions.init(allocator);    defer defs.deinit();    var state = try state_module.initChecked(allocator, &manager, &defs, .{});    defer state_module.deinit(&state);    const true_expr = try pexpr.PExpr.initWithArgs(allocator, .{ .construct = .{ .constructor = "True" } }, &[_]*pexpr.PExpr{});    const get_ctor_expr = try pexpr.PExpr.initWithArgs(allocator, .get_constructor, &[_]*pexpr.PExpr{true_expr});    const result = try compileInner(get_ctor_expr, Env.empty, Bdd.TRUE, &state);    try std.testing.expectEqual(@as(usize, 1), result.worlds.len);    const val = result.worlds[0].value;    try std.testing.expect(val.data == .native);    try std.testing.expect(val.data.native == .symbol);    try std.testing.expectEqualStrings("True", val.data.native.symbol);}test "get_constructor extracts constructor name from Cons value" {    var arena = std.heap.ArenaAllocator.init(std.testing.allocator);    defer arena.deinit();    const allocator = arena.allocator();    var manager = try Manager.init(allocator);    defer manager.deinit();    var defs = pexpr.Definitions.init(allocator);    defer defs.deinit();    var state = try state_module.initChecked(allocator, &manager, &defs, .{});    defer state_module.deinit(&state);    const o_expr = try pexpr.PExpr.initWithArgs(allocator, .{ .construct = .{ .constructor = "O" } }, &[_]*pexpr.PExpr{});    const nil_expr = try pexpr.PExpr.initWithArgs(allocator, .{ .construct = .{ .constructor = "Nil" } }, &[_]*pexpr.PExpr{});    const cons_expr = try pexpr.PExpr.initWithArgs(allocator, .{ .construct = .{ .constructor = "Cons" } }, &[_]*pexpr.PExpr{ o_expr, nil_expr });    const get_ctor_expr = try pexpr.PExpr.initWithArgs(allocator, .get_constructor, &[_]*pexpr.PExpr{cons_expr});    const result = try compileInner(get_ctor_expr, Env.empty, Bdd.TRUE, &state);    try std.testing.expectEqual(@as(usize, 1), result.worlds.len);    const val = result.worlds[0].value;    try std.testing.expect(val.data == .native);    try std.testing.expect(val.data.native == .symbol);    try std.testing.expectEqualStrings("Cons", val.data.native.symbol);}test "get_args extracts empty arguments from nullary constructor" {    var arena = std.heap.ArenaAllocator.init(std.testing.allocator);    defer arena.deinit();    const allocator = arena.allocator();    var manager = try Manager.init(allocator);    defer manager.deinit();    var defs = pexpr.Definitions.init(allocator);    defer defs.deinit();    var state = try state_module.initChecked(allocator, &manager, &defs, .{});    defer state_module.deinit(&state);    const true_expr = try pexpr.PExpr.initWithArgs(allocator, .{ .construct = .{ .constructor = "True" } }, &[_]*pexpr.PExpr{});    const get_args_expr = try pexpr.PExpr.initWithArgs(allocator, .get_args, &[_]*pexpr.PExpr{true_expr});    const result = try compileInner(get_args_expr, Env.empty, Bdd.TRUE, &state);    try std.testing.expectEqual(@as(usize, 1), result.worlds.len);    const val = result.worlds[0].value;    try std.testing.expect(val.data == .constructed);    try std.testing.expectEqualStrings("Nil", val.data.constructed.constructor);    try std.testing.expectEqual(@as(usize, 0), val.data.constructed.args.len);}test "get_args extracts arguments from Cons constructor" {    var arena = std.heap.ArenaAllocator.init(std.testing.allocator);    defer arena.deinit();    const allocator = arena.allocator();    var manager = try Manager.init(allocator);    defer manager.deinit();    var defs = pexpr.Definitions.init(allocator);    defer defs.deinit();    var state = try state_module.initChecked(allocator, &manager, &defs, .{});    defer state_module.deinit(&state);    const o_expr = try pexpr.PExpr.initWithArgs(allocator, .{ .construct = .{ .constructor = "O" } }, &[_]*pexpr.PExpr{});    const nil_expr = try pexpr.PExpr.initWithArgs(allocator, .{ .construct = .{ .constructor = "Nil" } }, &[_]*pexpr.PExpr{});    const cons_expr = try pexpr.PExpr.initWithArgs(allocator, .{ .construct = .{ .constructor = "Cons" } }, &[_]*pexpr.PExpr{ o_expr, nil_expr });    const get_args_expr = try pexpr.PExpr.initWithArgs(allocator, .get_args, &[_]*pexpr.PExpr{cons_expr});    const result = try compileInner(get_args_expr, Env.empty, Bdd.TRUE, &state);    try std.testing.expectEqual(@as(usize, 1), result.worlds.len);    const val = result.worlds[0].value;    try std.testing.expect(val.data == .constructed);    try std.testing.expectEqualStrings("Cons", val.data.constructed.constructor);    try std.testing.expectEqual(@as(usize, 2), val.data.constructed.args.len);    const first = val.data.constructed.args[0];    const first_result = try evaluateThunk(allocator, first, Bdd.TRUE, &state);    try std.testing.expectEqual(@as(usize, 1), first_result.worlds.len);    const first_val = first_result.worlds[0].value;    try std.testing.expect(first_val.data == .constructed);    try std.testing.expectEqualStrings("O", first_val.data.constructed.constructor);    const rest = val.data.constructed.args[1];    const rest_result = try evaluateThunk(allocator, rest, Bdd.TRUE, &state);    try std.testing.expectEqual(@as(usize, 1), rest_result.worlds.len);    const rest_val = rest_result.worlds[0].value;    try std.testing.expect(rest_val.data == .constructed);    try std.testing.expectEqualStrings("Cons", rest_val.data.constructed.constructor);    const second = rest_val.data.constructed.args[0];    const second_result = try evaluateThunk(allocator, second, Bdd.TRUE, &state);    try std.testing.expectEqual(@as(usize, 1), second_result.worlds.len);    const second_val = second_result.worlds[0].value;    try std.testing.expect(second_val.data == .constructed);    try std.testing.expectEqualStrings("Nil", second_val.data.constructed.constructor);    const tail = rest_val.data.constructed.args[1];    const tail_result = try evaluateThunk(allocator, tail, Bdd.TRUE, &state);    try std.testing.expectEqual(@as(usize, 1), tail_result.worlds.len);    const tail_val = tail_result.worlds[0].value;    try std.testing.expect(tail_val.data == .constructed);    try std.testing.expectEqualStrings("Nil", tail_val.data.constructed.constructor);}test "get_args with S(O) returns single-element list" {    var arena = std.heap.ArenaAllocator.init(std.testing.allocator);    defer arena.deinit();    const allocator = arena.allocator();    var manager = try Manager.init(allocator);    defer manager.deinit();    var defs = pexpr.Definitions.init(allocator);    defer defs.deinit();    var state = try state_module.initChecked(allocator, &manager, &defs, .{});    defer state_module.deinit(&state);    const o_expr = try pexpr.PExpr.initWithArgs(allocator, .{ .construct = .{ .constructor = "O" } }, &[_]*pexpr.PExpr{});    const s_expr = try pexpr.PExpr.initWithArgs(allocator, .{ .construct = .{ .constructor = "S" } }, &[_]*pexpr.PExpr{o_expr});    const get_args_expr = try pexpr.PExpr.initWithArgs(allocator, .get_args, &[_]*pexpr.PExpr{s_expr});    const result = try compileInner(get_args_expr, Env.empty, Bdd.TRUE, &state);    try std.testing.expectEqual(@as(usize, 1), result.worlds.len);    const val = result.worlds[0].value;    try std.testing.expect(val.data == .constructed);    try std.testing.expectEqualStrings("Cons", val.data.constructed.constructor);    try std.testing.expectEqual(@as(usize, 2), val.data.constructed.args.len);    const first = val.data.constructed.args[0];    const first_result = try evaluateThunk(allocator, first, Bdd.TRUE, &state);    try std.testing.expectEqual(@as(usize, 1), first_result.worlds.len);    const first_val = first_result.worlds[0].value;    try std.testing.expect(first_val.data == .constructed);    try std.testing.expectEqualStrings("O", first_val.data.constructed.constructor);    const rest = val.data.constructed.args[1];    const rest_result = try evaluateThunk(allocator, rest, Bdd.TRUE, &state);    try std.testing.expectEqual(@as(usize, 1), rest_result.worlds.len);    const rest_val = rest_result.worlds[0].value;    try std.testing.expect(rest_val.data == .constructed);    try std.testing.expectEqualStrings("Nil", rest_val.data.constructed.constructor);}test "ThunkRegistry basic operations" {    const allocator = std.testing.allocator;    var registry = ThunkRegistry.init(allocator);    defer registry.deinit();    var manager = try Manager.init(allocator);    defer manager.deinit();    const expr = try PExpr.init(allocator, .{ .const_native = .{ .float = 1.0 } });    defer expr.deinit(allocator);    const callstack: []const i32 = &[_]i32{ 0, 1, 2 };    const thunk = try LazyKCThunk.init(allocator, expr, Env.empty, 0, callstack);    defer thunk.deinit(allocator);    try registry.register(thunk, expr, callstack);    try std.testing.expectEqual(@as(usize, 1), registry.count());    const id = ThunkId.init(expr, callstack);    const found = registry.get(id);    try std.testing.expect(found != null);    try std.testing.expect(found.? == thunk);}test "ThunkId stability across lookups" {    const allocator = std.testing.allocator;    const expr = try PExpr.init(allocator, .{ .const_native = .{ .float = 42.0 } });    defer expr.deinit(allocator);    const callstack1: []const i32 = &[_]i32{ 1, 2, 3 };    const callstack2: []const i32 = &[_]i32{ 1, 2, 3 };    const id1 = ThunkId.init(expr, callstack1);    const id2 = ThunkId.init(expr, callstack2);    try std.testing.expect(id1.eql(id2));    try std.testing.expectEqual(id1.hash(), id2.hash());    const callstack3: []const i32 = &[_]i32{ 1, 2, 4 };    const id3 = ThunkId.init(expr, callstack3);    try std.testing.expect(!id1.eql(id3));}test "ThunkRegistry refineVariable restricts guards" {    const allocator = std.testing.allocator;    var manager = try Manager.init(allocator);    defer manager.deinit();    var registry = ThunkRegistry.init(allocator);    defer registry.deinit();    const expr = try PExpr.init(allocator, .{ .const_native = .{ .float = 1.0 } });    defer expr.deinit(allocator);    const callstack: []const i32 = &[_]i32{};    const thunk = try LazyKCThunk.init(allocator, expr, Env.empty, 0, callstack);    defer thunk.deinit(allocator);    try registry.register(thunk, expr, callstack);    const x = try manager.newVar(true);    const y = try manager.newVar(true);    const guard = try manager.bddAnd(x, y);    const dummy_val = try RuntimeValue.initNative(allocator, .{ .int = 42 });    defer dummy_val.deinit(allocator);    const worlds_slice = try allocator.alloc(World, 1);    worlds_slice[0] = .{ .value = dummy_val, .guard = guard };    try thunk.cache.append(allocator, .{        .worlds = worlds_slice,        .validity_guard = guard,    });    try registry.refineVariable(&manager, 0, true);    try std.testing.expectEqual(@as(usize, 1), thunk.cache.items.len);    const new_validity_guard = thunk.cache.items[0].validity_guard;    try std.testing.expect(manager.eq(new_validity_guard, y));    const new_world_guard = thunk.cache.items[0].worlds[0].guard;    try std.testing.expect(manager.eq(new_world_guard, y));}test "ThunkDependencies basic operations" {    const allocator = std.testing.allocator;    var deps = ThunkDependencies.init(allocator);    defer deps.deinit();    const id1 = ThunkId{ .session = .{ .expr_ptr = 0x1000, .callstack_hash = 100 } };    const id2 = ThunkId{ .session = .{ .expr_ptr = 0x2000, .callstack_hash = 200 } };    try deps.addDependency(id1, 0);    try deps.addDependency(id1, 1);    try deps.addDependency(id2, 1);    try deps.addDependency(id2, 2);    const vars1 = deps.getVariables(id1).?;    try std.testing.expect(vars1.contains(0));    try std.testing.expect(vars1.contains(1));    try std.testing.expect(!vars1.contains(2));    const thunks_for_var1 = deps.getThunks(1).?;    try std.testing.expect(thunks_for_var1.contains(id1));    try std.testing.expect(thunks_for_var1.contains(id2));    const thunks_for_var0 = deps.getThunks(0).?;    try std.testing.expect(thunks_for_var0.contains(id1));    try std.testing.expect(!thunks_for_var0.contains(id2));}test "ThunkDependencies dirty marking" {    const allocator = std.testing.allocator;    var deps = ThunkDependencies.init(allocator);    defer deps.deinit();    const id1 = ThunkId{ .session = .{ .expr_ptr = 0x1000, .callstack_hash = 100 } };    const id2 = ThunkId{ .session = .{ .expr_ptr = 0x2000, .callstack_hash = 200 } };    try deps.addDependency(id1, 0);    try deps.addDependency(id1, 1);    try deps.addDependency(id2, 1);    try std.testing.expectEqual(@as(usize, 0), deps.dirtyCount());    try std.testing.expect(!deps.isDirty(id1));    try deps.markDirty(0);    try std.testing.expectEqual(@as(usize, 1), deps.dirtyCount());    try std.testing.expect(deps.isDirty(id1));    try std.testing.expect(!deps.isDirty(id2));    deps.clearDirty(id1);    try std.testing.expectEqual(@as(usize, 0), deps.dirtyCount());    try deps.markDirty(1);    try std.testing.expectEqual(@as(usize, 2), deps.dirtyCount());    try std.testing.expect(deps.isDirty(id1));    try std.testing.expect(deps.isDirty(id2));    deps.clearAllDirty();    try std.testing.expectEqual(@as(usize, 0), deps.dirtyCount());}test "ThunkDependencies removeThunk cleans up correctly" {    const allocator = std.testing.allocator;    var deps = ThunkDependencies.init(allocator);    defer deps.deinit();    const id1 = ThunkId{ .session = .{ .expr_ptr = 0x1000, .callstack_hash = 100 } };    const id2 = ThunkId{ .session = .{ .expr_ptr = 0x2000, .callstack_hash = 200 } };    try deps.addDependency(id1, 0);    try deps.addDependency(id1, 1);    try deps.addDependency(id2, 1);    try deps.markDirty(0);    try std.testing.expect(deps.isDirty(id1));    deps.removeThunk(id1);    try std.testing.expect(deps.getVariables(id1) == null);    const thunks_for_var0 = deps.getThunks(0).?;    try std.testing.expect(!thunks_for_var0.contains(id1));    const thunks_for_var1 = deps.getThunks(1).?;    try std.testing.expect(!thunks_for_var1.contains(id1));    try std.testing.expect(thunks_for_var1.contains(id2));    try std.testing.expect(!deps.isDirty(id1));}test "IncrementalLPSMC init and deinit" {    const allocator = std.testing.allocator;    var lpsmc = lpsmc_module.init(allocator);    defer lpsmc_module.deinit(&lpsmc);    try std.testing.expectEqual(@as(u32, 0), lpsmc.last_iteration_count);    try std.testing.expectEqual(@as(f64, 1.0), lpsmc.final_multiplier);    try std.testing.expectEqual(@as(usize, 0), lpsmc.subproblem_cache.count());    try std.testing.expectEqual(@as(usize, 0), lpsmc.path_choices.count());}test "IncrementalLPSMC affectsPathChoices" {    const allocator = std.testing.allocator;    var lpsmc = lpsmc_module.init(allocator);    defer lpsmc_module.deinit(&lpsmc);    var deps = VarLabelSet{};    try deps.put(allocator, 1, {});    try deps.put(allocator, 2, {});    try lpsmc.path_choices.put(allocator, 0, PathChoice{        .top_k_bdd = Bdd.TRUE,        .sampled_bdd = null,        .sampled_probability = 0.0,        .k_used = 1,        .ess_ratio = 1.0,        .depends_on_vars = deps,    });    try std.testing.expect(lpsmc_module.affectsPathChoices(&lpsmc, 1));    try std.testing.expect(lpsmc_module.affectsPathChoices(&lpsmc, 2));    try std.testing.expect(!lpsmc_module.affectsPathChoices(&lpsmc, 3));}test "IncrementalLPSMC getAffectedSubproblems" {    const allocator = std.testing.allocator;    var lpsmc = lpsmc_module.init(allocator);    defer lpsmc_module.deinit(&lpsmc);    try lpsmc_module.recordDependency(&lpsmc, 0, 1);    try lpsmc_module.recordDependency(&lpsmc, 1, 1);    try lpsmc_module.recordDependency(&lpsmc, 2, 2);    var affected1: std.ArrayList(u32) = .empty;    defer affected1.deinit(allocator);    try lpsmc_module.getAffectedSubproblems(&lpsmc, 1, &affected1);    try std.testing.expectEqual(@as(usize, 2), affected1.items.len);    var affected2: std.ArrayList(u32) = .empty;    defer affected2.deinit(allocator);    try lpsmc_module.getAffectedSubproblems(&lpsmc, 2, &affected2);    try std.testing.expectEqual(@as(usize, 1), affected2.items.len);    var affected3: std.ArrayList(u32) = .empty;    defer affected3.deinit(allocator);    try lpsmc_module.getAffectedSubproblems(&lpsmc, 3, &affected3);    try std.testing.expectEqual(@as(usize, 0), affected3.items.len);}test "IncrementalLPSMC clearCaches" {    const allocator = std.testing.allocator;    var lpsmc = lpsmc_module.init(allocator);    defer lpsmc_module.deinit(&lpsmc);    var deps = VarLabelSet{};    try deps.put(allocator, 1, {});    try lpsmc.path_choices.put(allocator, 0, PathChoice{        .top_k_bdd = Bdd.TRUE,        .sampled_bdd = null,        .sampled_probability = 0.0,        .k_used = 1,        .ess_ratio = 1.0,        .depends_on_vars = deps,    });    try lpsmc_module.recordDependency(&lpsmc, 0, 1);    lpsmc.last_iteration_count = 5;    lpsmc.final_multiplier = 2.5;    lpsmc_module.clearCaches(&lpsmc);    try std.testing.expectEqual(@as(u32, 0), lpsmc.last_iteration_count);    try std.testing.expectEqual(@as(f64, 1.0), lpsmc.final_multiplier);    try std.testing.expectEqual(@as(usize, 0), lpsmc.path_choices.count());    try std.testing.expectEqual(@as(usize, 0), lpsmc.var_to_subproblems.count());}test "LPSMCVarianceStats effective sample size" {    var stats = LPSMCVarianceStats{};    try std.testing.expectEqual(@as(f64, 0.0), stats.effectiveSampleSize());    try std.testing.expectEqual(@as(f64, 1.0), stats.essRatio());    stats.recordWeight(1.0);    stats.recordWeight(1.0);    stats.recordWeight(1.0);    try std.testing.expectApproxEqAbs(@as(f64, 3.0), stats.effectiveSampleSize(), 0.01);    try std.testing.expectApproxEqAbs(@as(f64, 1.0), stats.essRatio(), 0.01);}test "LPSMCVarianceStats high variance detection" {    var stats = LPSMCVarianceStats{};    stats.recordWeight(1.0);    stats.recordWeight(1.0);    try std.testing.expect(!stats.isHighVariance(0.5));    var high_var_stats = LPSMCVarianceStats{};    high_var_stats.recordWeight(0.1);    high_var_stats.recordWeight(0.1);    high_var_stats.recordWeight(100.0);    try std.testing.expect(high_var_stats.essRatio() < 0.5);}test "LPSMCVarianceStats max multiplier tracking" {    var stats = LPSMCVarianceStats{};    try std.testing.expectEqual(@as(f64, 1.0), stats.max_multiplier);    stats.recordWeight(2.0);    try std.testing.expectEqual(@as(f64, 2.0), stats.max_multiplier);    stats.recordWeight(1.0);    try std.testing.expectEqual(@as(f64, 2.0), stats.max_multiplier);    stats.recordWeight(10.0);    try std.testing.expectEqual(@as(f64, 10.0), stats.max_multiplier);}test "IncrementalLPSMC clearCaches resets variance stats" {    const allocator = std.testing.allocator;    var lpsmc = lpsmc_module.init(allocator);    defer lpsmc_module.deinit(&lpsmc);    lpsmc.variance_stats.recordWeight(5.0);    lpsmc.variance_stats.recordWeight(10.0);    lpsmc.variance_stats.high_variance_warning = true;    try std.testing.expectEqual(@as(u32, 2), lpsmc.variance_stats.num_samples);    try std.testing.expect(lpsmc.variance_stats.high_variance_warning);    lpsmc_module.clearCaches(&lpsmc);    try std.testing.expectEqual(@as(u32, 0), lpsmc.variance_stats.num_samples);    try std.testing.expectEqual(@as(f64, 0.0), lpsmc.variance_stats.sum_weights);    try std.testing.expect(!lpsmc.variance_stats.high_variance_warning);}

Source: lib/pluck/src/root.zig:13

zig
pub const evaluator = @import("evaluator.zig");

Complete caller list for evaluator.compile

9 direct callers.

Complete call list for evaluator.compile

7 direct calls.

Complete caller list for evaluator.compileInner

24 direct callers.

Complete call list for evaluator.compileInner

20 direct calls.

Complete caller list for evaluator.evaluateThunk

13 direct callers.

Complete caller list for evaluator.makeThunk

8 direct callers.

Complete caller list for evaluator.tracedCompileInner

21 direct callers.

Complete caller list for evaluator.bindMonad

15 direct callers.

Complete caller list for evaluator.freeWorldsSlice

20 direct callers.

Complete caller list for evaluator.programErrorWorlds

18 direct callers.

Complete caller list for evaluator.pureMonad

18 direct callers.

Audit

Definitions44
Public names56
Members90
Version26.7.0
Revisiondaab053ee433