Skip to main content

vyre_libs/security/
sink_intersection.rs

1//! `sink_intersection`  -  count how many of a query set are also in
2//! a sink-family bitset. Used by rules that want a fractional
3//! confidence ("X% of nodes reachable from source landed in sinks").
4
5use vyre_foundation::ir::Program;
6use vyre_primitives::bitset::and::bitset_and;
7use vyre_primitives::bitset::bitset_words;
8use vyre_primitives::reduce::count::reduce_count;
9
10use crate::security::flow_composition::fuse_security_flow;
11
12pub(crate) const OP_ID: &str = "vyre-libs::security::sink_intersection";
13
14/// Build a sink-intersection-count Program: AND `query_set` with
15/// `sink_set` into `intersect_buf`, then popcount-reduce that into
16/// `out_scalar`.
17///
18/// `reduce_count` seeds `out_scalar` to zero before accumulating, so
19/// the count is the intersection's population and not an addition to
20/// whatever the caller left in the slot.
21#[must_use]
22pub fn sink_intersection(
23    node_count: u32,
24    query_set: &str,
25    sink_set: &str,
26    intersect_buf: &str,
27    out_scalar: &str,
28) -> Program {
29    let words = bitset_words(node_count);
30    fuse_security_flow(
31        OP_ID,
32        &[
33            bitset_and(query_set, sink_set, intersect_buf, words),
34            reduce_count(intersect_buf, out_scalar, words),
35        ],
36        out_scalar,
37    )
38}
39
40/// CPU oracle: count of bits set in `query AND sink`.
41#[must_use]
42#[cfg(test)]
43pub(crate) fn cpu_ref(query_set: &[u32], sink_set: &[u32]) -> u32 {
44    vyre_primitives::reduce::count::cpu_ref(&vyre_primitives::bitset::and::cpu_ref(
45        query_set, sink_set,
46    ))
47}
48
49/// Soundness marker for [`sink_intersection`].
50pub struct SinkIntersection;
51impl vyre_spec::soundness::SoundnessTagged for SinkIntersection {
52    fn soundness(&self) -> vyre_spec::soundness::Soundness {
53        vyre_spec::soundness::Soundness::Exact
54    }
55}
56
57#[cfg(test)]
58mod tests {
59    use super::*;
60
61    #[test]
62    fn full_overlap_counts_all_set_bits() {
63        assert_eq!(cpu_ref(&[0b1111], &[0b1111]), 4);
64    }
65
66    #[test]
67    fn no_overlap_returns_zero() {
68        assert_eq!(cpu_ref(&[0b1010], &[0b0101]), 0);
69    }
70
71    #[test]
72    fn partial_overlap_counts_intersection() {
73        assert_eq!(cpu_ref(&[0b1110], &[0b0111]), 2);
74    }
75
76    #[test]
77    fn distributes_across_words() {
78        assert_eq!(cpu_ref(&[0xFF00, 0x00FF], &[0xFFFF, 0xFFFF]), 16);
79    }
80}