use std::collections::HashMap;
#[derive(Debug, Clone)]
pub struct Candidate {
pub content: String,
pub prior: f32,
}
impl Candidate {
pub fn new(content: impl Into<String>, prior: f32) -> Self {
Self {
content: content.into(),
prior: prior.max(0.0),
}
}
}
const PRESERVED_KEYWORDS: &[&str] = &[
"fn", "let", "mut", "if", "else", "for", "while", "loop", "match", "return",
"struct", "enum", "impl", "trait", "pub", "use", "mod", "const", "static",
"self", "Self", "true", "false", "in", "as", "ref", "move", "where", "type",
"def", "class", "import", "from", "return", "elif", "None", "True", "False",
];
pub fn canonical_form(src: &str) -> String {
let mut out = String::with_capacity(src.len());
let mut mapping: HashMap<String, String> = HashMap::new();
let mut next_id = 0usize;
let bytes = src.as_bytes();
let mut i = 0usize;
let mut prev_was_word = false;
let is_word = |c: u8| c.is_ascii_alphanumeric() || c == b'_';
while i < bytes.len() {
let c = bytes[i];
if c.is_ascii_whitespace() {
i += 1;
continue;
}
if is_word(c) {
let start = i;
i += 1;
while i < bytes.len() && is_word(bytes[i]) {
i += 1;
}
let token = &src[start..i];
if prev_was_word {
out.push(' ');
}
if token.as_bytes()[0].is_ascii_digit() {
out.push_str(token);
} else if PRESERVED_KEYWORDS.contains(&token) {
out.push_str(token);
} else {
let placeholder = mapping.entry(token.to_string()).or_insert_with(|| {
let p = format!("V{next_id}");
next_id += 1;
p
});
out.push_str(placeholder);
}
prev_was_word = true;
} else {
out.push(c as char);
prev_was_word = false;
i += 1;
}
}
out
}
pub fn rerank_by_self_consistency(candidates: &[Candidate]) -> Vec<usize> {
if candidates.is_empty() {
return Vec::new();
}
let mut cluster_vote: HashMap<String, f32> = HashMap::new();
let canon: Vec<String> = candidates.iter().map(|c| canonical_form(&c.content)).collect();
for (idx, key) in canon.iter().enumerate() {
*cluster_vote.entry(key.clone()).or_insert(0.0) += candidates[idx].prior.max(0.0);
}
let mut order: Vec<usize> = (0..candidates.len()).collect();
order.sort_by(|&a, &b| {
let va = cluster_vote[&canon[a]];
let vb = cluster_vote[&canon[b]];
vb.partial_cmp(&va)
.unwrap_or(std::cmp::Ordering::Equal)
.then(
candidates[b]
.prior
.partial_cmp(&candidates[a].prior)
.unwrap_or(std::cmp::Ordering::Equal),
)
.then(a.cmp(&b))
});
order
}
pub fn order_candidate_contents(candidates: &[Candidate]) -> Vec<String> {
rerank_by_self_consistency(candidates)
.into_iter()
.map(|i| candidates[i].content.clone())
.collect()
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn canonical_form_is_invariant_to_renaming_and_whitespace() {
let a = "fn add(x: i32, y: i32) -> i32 { x + y }";
let b = "fn add( alpha : i32 , beta : i32 ) -> i32 { alpha + beta }";
assert_eq!(canonical_form(a), canonical_form(b));
}
#[test]
fn canonical_form_distinguishes_real_structural_differences() {
let a = "fn f() { return x + y; }";
let b = "fn f() { return x - y; }";
assert_ne!(canonical_form(a), canonical_form(b), "+ vs - must differ");
}
#[test]
fn keywords_are_preserved_so_control_flow_is_not_aliased() {
let with_if = canonical_form("if cond { a }");
let without = canonical_form("xx cond { a }");
assert!(with_if.starts_with("if "));
assert_ne!(with_if, without);
}
#[test]
fn voting_promotes_the_consensus_candidate() {
let cands = vec![
Candidate::new("fn g() { return a + b; }", 1.0),
Candidate::new("fn g() { return foo - bar; }", 1.5), Candidate::new("fn g() { return p + q; }", 1.0), ];
let order = rerank_by_self_consistency(&cands);
assert_eq!(order[2], 1, "lone higher-prior candidate must rank last");
assert!(
(order[0] == 0 && order[1] == 2) || (order[0] == 2 && order[1] == 0),
"consensus cluster must occupy the top two slots, got {order:?}"
);
}
#[test]
fn prior_breaks_ties_within_equal_votes() {
let cands = vec![
Candidate::new("fn a() { x }", 0.5),
Candidate::new("fn b() { y - z }", 3.0),
];
let order = rerank_by_self_consistency(&cands);
assert_eq!(order, vec![1, 0]);
}
#[test]
fn empty_input_yields_empty_ranking() {
assert!(rerank_by_self_consistency(&[]).is_empty());
}
#[test]
fn order_contents_returns_best_first() {
let cands = vec![
Candidate::new("fn g() { a + b }", 1.0),
Candidate::new("fn g() { c + d }", 1.0), Candidate::new("fn g() { lonely() }", 1.0),
];
let ordered = order_candidate_contents(&cands);
assert_eq!(ordered.len(), 3);
assert_ne!(ordered[0], "fn g() { lonely() }");
}
}