///|
/// Greedy discrimination over already-computed exact-size fingerprints.
pub(all) struct FingerprintPanel {
selected_columns : Array[Int]
unresolved_pairs : Array[(Int, Int)]
complete : Bool
} derive(Eq, Debug, ToJson)
///|
/// Rows are samples and columns are synthetic-model fingerprints.
/// Deterministic greedy set cover; not a minimum-panel or laboratory-success claim.
pub fn select_panel(
fingerprints : Array[Array[String]],
max_columns : Int,
) -> FingerprintPanel raise RestrictError {
if fingerprints.length() < 2 || fingerprints.length() > 32 {
raise InvalidInput("PANEL_SAMPLES")
}
let columns = fingerprints[0].length()
if columns < 1 || columns > 32 || max_columns < 0 || max_columns > columns {
raise InvalidInput("PANEL_COLUMNS")
}
let mut bytes = 0
for row in fingerprints {
if row.length() != columns {
raise InvalidInput("PANEL_RAGGED")
}
for value in row {
bytes = bytes + value.length()
if bytes > 1000000 {
raise LimitExceeded("PANEL_BYTES")
}
}
}
let mut remaining : Array[(Int, Int)] = []
for i in 0.. best_gain {
best = c
best_gain = gain
}
}
if best < 0 {
break
}
used[best] = true
selected.push(best)
remaining = remaining.filter(pair => {
fingerprints[pair.0][best] == fingerprints[pair.1][best]
})
}
{
selected_columns: selected,
unresolved_pairs: remaining,
complete: remaining.is_empty(),
}
}