///|
/// Maximum-cardinality union selection and its disjoint independent blocks.
pub(all) struct UnionSelection {
elements : Array[Int32]
groups : Array[Array[Int32]]
proof : Intersection
}
///|
/// Reduce matroid union to intersection on (block, original-element) copies.
/// The copied ground is bounded by 64; no exponential enumeration is used.
pub fn union_partition(
models : Array[Matroid],
max_calls~ : Int32 = 1000000,
) -> UnionSelection raise MatroidError {
if models.length() == 0 || models.length() > 64 {
raise InvalidInput("union requires 1..64 models")
}
let n = models[0].size
if n * models.length() > 64 {
raise InvalidInput("union copied ground exceeds 64")
}
let mut combined = Matroid::zero(0)
for m in models {
if m.size != n {
raise InvalidInput("union ground mismatch")
}
combined = combined.direct_sum(m)
}
let copies = n * models.length()
let labels = Array::makei(copies, e => e % n)
let once = Matroid::partition(labels, Array::make(n, 1))
let proof = intersect(combined, once, max_calls~)
verify_intersection(combined, once, proof)
let groups : Array[Array[Int32]] = Array::makei(models.length(), _ => [])
let selected = []
for e in proof.elements {
groups[e / n].push(e % n)
selected.push(e % n)
}
let elements = checked_set(n, selected)
verify_partition(models, groups, elements)
{ elements, groups, proof, }
}