///|
fn pair_budget(a : Mass, b : Mass, budget : Limits) -> Unit raise BeliefError {
same_frame(a, b)
budget.check_dense(a.domain)
if a.focal_count() > budget.pairs / b.focal_count() {
raise Budget("focal cross product exceeds pair budget")
}
}
///|
/// Unnormalized conjunctive fusion: each pair transfers mass to intersection.
/// Empty intersections are preserved as conflict, never renormalized here.
pub fn conjunctive(
a : Mass,
b : Mass,
budget~ : Limits = default_limits(),
) -> Mass raise BeliefError {
pair_budget(a, b, budget)
let bins = Array::make(a.domain.universe() + 1, 0.0)
let corrections = Array::make(bins.length(), 0.0)
for (left, x) in a.focals {
for (right, y) in b.focals {
let target = left & right
let adjusted = x * y - corrections[target]
let next = bins[target] + adjusted
corrections[target] = next - bins[target] - adjusted
bins[target] = next
}
}
from_bins(a.domain, bins, budget)
}