Skip to main content

_diffctx/
select.rs

1use std::cmp::Ordering;
2use std::collections::BinaryHeap;
3use std::sync::Arc;
4
5use rustc_hash::{FxHashMap, FxHashSet};
6
7use crate::config::limits::UTILITY;
8use crate::config::selection::selection;
9use crate::interval::IntervalIndex;
10use crate::types::{Fragment, FragmentId};
11use crate::utility::needs::InformationNeed;
12use crate::utility::scoring::{
13    UtilityState, apply_fragment, compute_density, marginal_gain, utility_value,
14};
15
16const SENTINEL_TOKEN_COUNT: u32 = 1_000_000_000;
17
18/// `used_tokens` is a reported contract, so it is derived from the returned
19/// selection rather than reconstructed from budget arithmetic that can drift
20/// away from what was actually placed.
21fn selection_cost(selected: &[Fragment]) -> u32 {
22    selected.iter().map(|f| f.token_count).sum()
23}
24
25#[derive(Debug, Clone, Copy, PartialEq, Eq)]
26pub enum SelectionReason {
27    TopK,
28    NoCandidates,
29    BudgetExhausted,
30    NoUtility,
31    StoppedByTau,
32    BestSingleton,
33}
34
35impl SelectionReason {
36    pub fn as_str(&self) -> &'static str {
37        match self {
38            Self::TopK => "topk",
39            Self::NoCandidates => "no_candidates",
40            Self::BudgetExhausted => "budget_exhausted",
41            Self::NoUtility => "no_utility",
42            Self::StoppedByTau => "stopped_by_tau",
43            Self::BestSingleton => "best_singleton",
44        }
45    }
46}
47
48pub struct SelectionResult {
49    pub selected: Vec<Fragment>,
50    pub reason: SelectionReason,
51    pub used_tokens: u32,
52    pub utility: f64,
53    /// Greedy iterations actually executed (number of `apply_fragment`
54    /// calls in `run_greedy_loop_heap`). Diagnoses lazy-heap blowup:
55    /// expected ≈ output size, pathological ≫ output size when
56    /// stale-version rejections dominate.
57    pub greedy_iters: usize,
58    /// Additive certificate for adaptive stopping: an upper bound
59    /// (`tau * peak_density * remaining_budget`) on the utility that
60    /// continuing the same greedy to the feasibility frontier could
61    /// still have added. 0 when the loop ended for any other reason.
62    pub stopping_certificate: f64,
63}
64
65struct HeapEntry {
66    neg_density: f64,
67    frag_id: FragmentId,
68    version: u32,
69}
70
71impl PartialEq for HeapEntry {
72    fn eq(&self, other: &Self) -> bool {
73        self.neg_density.to_bits() == other.neg_density.to_bits() && self.frag_id == other.frag_id
74    }
75}
76
77impl Eq for HeapEntry {}
78
79impl PartialOrd for HeapEntry {
80    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
81        Some(self.cmp(other))
82    }
83}
84
85impl Ord for HeapEntry {
86    fn cmp(&self, other: &Self) -> Ordering {
87        other
88            .neg_density
89            .total_cmp(&self.neg_density)
90            .then_with(|| other.frag_id.cmp(&self.frag_id))
91    }
92}
93
94struct SelectionState {
95    selected: Vec<Fragment>,
96    selected_ids: IntervalIndex,
97    remaining_budget: u32,
98    utility_state: UtilityState,
99}
100
101fn drop_redundant_signatures(candidates: &[Fragment], budget: u32) -> Vec<Fragment> {
102    let mut full_token_by_loc: FxHashMap<(Arc<str>, u32), u32> = FxHashMap::default();
103    for f in candidates {
104        if !f.kind.is_signature() {
105            // Keep the LARGEST co-located full fragment, not the last one seen.
106            // Two non-signature fragments can share a start line (a class header
107            // `Definition` at [10,12] and the full class at [10,300]), and with a
108            // plain `insert` whichever came last in the candidate vec won the
109            // slot. When the small header won, the class's stub was filtered out
110            // as "redundant" precisely when the full class did not fit and the
111            // stub was its only affordable representation — and the outcome
112            // depended on vec order rather than on anything meaningful.
113            full_token_by_loc
114                .entry((f.id.path.clone(), f.start_line()))
115                .and_modify(|t| *t = (*t).max(f.token_count))
116                .or_insert(f.token_count);
117        }
118    }
119    candidates
120        .iter()
121        .filter(|f| {
122            if !f.kind.is_signature() {
123                return true;
124            }
125            let key = (f.id.path.clone(), f.start_line());
126            full_token_by_loc
127                .get(&key)
128                .copied()
129                .unwrap_or(SENTINEL_TOKEN_COUNT)
130                > budget
131        })
132        .cloned()
133        .collect()
134}
135
136fn compute_r_cap(
137    rel: &FxHashMap<FragmentId, f64>,
138    core_ids: Option<&FxHashSet<FragmentId>>,
139) -> f64 {
140    let values: Vec<f64> = rel
141        .iter()
142        .filter(|(fid, v)| **v > 0.0 && core_ids.map_or(true, |c| !c.contains(*fid)))
143        .map(|(_, v)| *v)
144        .collect();
145
146    if values.len() < 2 {
147        return if let Some(&v) = values.first() {
148            v.max(selection().r_cap_min)
149        } else {
150            1.0
151        };
152    }
153
154    let mut sorted = values.clone();
155    sorted.sort_by(|a, b| a.total_cmp(b));
156    let mid = sorted.len() / 2;
157    let med = if sorted.len() % 2 == 0 {
158        (sorted[mid - 1] + sorted[mid]) / 2.0
159    } else {
160        sorted[mid]
161    };
162
163    let mean: f64 = values.iter().sum::<f64>() / values.len() as f64;
164    let variance: f64 =
165        values.iter().map(|v| (v - mean).powi(2)).sum::<f64>() / (values.len() - 1) as f64;
166    let std = variance.sqrt();
167
168    (med + UTILITY.r_cap_sigma * std).max(1e-9)
169}
170
171fn build_signature_lookup(
172    fragments: &[Fragment],
173    core_fragments: &[Fragment],
174    core_excerpts: Option<&FxHashMap<FragmentId, Fragment>>,
175) -> FxHashMap<FragmentId, Fragment> {
176    let mut sig_by_loc: FxHashMap<(Arc<str>, u32), Fragment> = FxHashMap::default();
177    for f in fragments {
178        if f.kind.is_signature() {
179            sig_by_loc.insert((f.id.path.clone(), f.start_line()), f.clone());
180        }
181    }
182    let mut sig_lookup = FxHashMap::default();
183    for cf in core_fragments {
184        let key = (cf.id.path.clone(), cf.start_line());
185        if let Some(sig) = sig_by_loc.get(&key) {
186            sig_lookup.insert(cf.id.clone(), sig.clone());
187            continue;
188        }
189        // Kinds without a signature (chunk, section) fall back to the excerpt
190        // around the hunk; without it an oversized core is skipped outright and
191        // the change signal disappears from the output (#103).
192        if let Some(excerpt) = core_excerpts.and_then(|e| e.get(&cf.id)) {
193            sig_lookup.insert(cf.id.clone(), excerpt.clone());
194        }
195    }
196    sig_lookup
197}
198
199fn select_core_fragments(
200    core_fragments: &[Fragment],
201    rel: &FxHashMap<FragmentId, f64>,
202    needs: &[InformationNeed],
203    state: &mut SelectionState,
204    budget_tokens: u32,
205    sig_lookup: &FxHashMap<FragmentId, Fragment>,
206    core_excerpts: Option<&FxHashMap<FragmentId, Fragment>>,
207) -> FxHashSet<FragmentId> {
208    // Which cores came out represented — by themselves, by a signature stub, or
209    // by a downshifted excerpt. A substitute has its own id, so membership in
210    // the selection cannot answer this, and treating a substituted core as
211    // "skipped" hands the full fragment straight back to the greedy.
212    let mut satisfied: FxHashSet<FragmentId> = FxHashSet::default();
213    let core_budget = (budget_tokens as f64 * selection().core_budget_fraction) as u32;
214    // Counter for cores placed; the first pass keeps `core_used <= core_budget`,
215    // but the rescue pass below intentionally allows it to exceed `core_budget`
216    // up to `budget_tokens`. Don't assume the tighter bound past this scope.
217    let mut core_used = 0u32;
218
219    let mut sorted_core: Vec<&Fragment> = core_fragments.iter().collect();
220    sorted_core.sort_by(|a, b| {
221        let ra = rel.get(&a.id).copied().unwrap_or(0.0);
222        let rb = rel.get(&b.id).copied().unwrap_or(0.0);
223        rb.total_cmp(&ra)
224    });
225
226    let place_fragment =
227        |frag: &Fragment, core_used: &mut u32, state: &mut SelectionState, rel_score: f64| {
228            state.selected.push(frag.clone());
229            state.selected_ids.add_id(&frag.id);
230            state.remaining_budget = state.remaining_budget.saturating_sub(frag.token_count);
231            *core_used += frag.token_count;
232            apply_fragment(frag, rel_score, needs, &mut state.utility_state);
233        };
234
235    // (originating core id, the fragment actually offered for it — the core
236    // itself or its downshifted excerpt).
237    let mut skipped: Vec<(FragmentId, &Fragment)> = Vec::new();
238    for frag in &sorted_core {
239        // Downshift before the budget is consulted, not only when it forces the
240        // issue. A core whose hunk window covers a small share of it is mostly
241        // unchanged context, and emitting it whole is the over-dump behind
242        // #105/#107/#149 — behaviour that otherwise flips purely on how much
243        // budget happens to be left.
244        let core_id = frag.id.clone();
245        let frag: &Fragment = core_excerpts
246            .and_then(|e| e.get(&frag.id))
247            .filter(|excerpt| crate::excerpt::is_downshift_worthwhile(frag, excerpt))
248            .unwrap_or(frag);
249        if state.selected_ids.is_superset_of(frag) {
250            satisfied.insert(core_id);
251            continue;
252        }
253        if core_used + frag.token_count > core_budget {
254            if let Some(sig) = sig_lookup.get(&core_id) {
255                if !state.selected_ids.contains(&sig.id)
256                    && core_used + sig.token_count <= core_budget
257                {
258                    let rel_score = rel.get(&core_id).copied().unwrap_or(0.0);
259                    place_fragment(sig, &mut core_used, state, rel_score);
260                    satisfied.insert(core_id);
261                    continue;
262                }
263            }
264            skipped.push((core_id, frag));
265            continue;
266        }
267
268        let rel_score = rel.get(&core_id).copied().unwrap_or(0.0);
269        place_fragment(frag, &mut core_used, state, rel_score);
270        satisfied.insert(core_id);
271    }
272
273    // Bug #2 fix: cores that didn't fit the core_budget reservation must not be
274    // demoted to ordinary greedy candidates without a chance to be placed first.
275    // Sweep skipped cores cheapest-first against the *full* remaining budget
276    // (not just the core slice) so seeds aren't dropped purely because the
277    // highest-relevance core happened to be heavy.
278    if !skipped.is_empty() {
279        skipped.sort_by(|(_, a), (_, b)| a.token_count.cmp(&b.token_count));
280        for (core_id, frag) in skipped {
281            if state.remaining_budget == 0 {
282                break;
283            }
284            if state.selected_ids.is_superset_of(frag) {
285                satisfied.insert(core_id);
286                continue;
287            }
288            let rel_score = rel.get(&core_id).copied().unwrap_or(0.0);
289            if frag.token_count <= state.remaining_budget {
290                place_fragment(frag, &mut core_used, state, rel_score);
291                satisfied.insert(core_id);
292            } else if let Some(sig) = sig_lookup.get(&core_id) {
293                if !state.selected_ids.contains(&sig.id)
294                    && sig.token_count <= state.remaining_budget
295                {
296                    place_fragment(sig, &mut core_used, state, rel_score);
297                    satisfied.insert(core_id);
298                }
299            }
300        }
301    }
302
303    satisfied
304}
305
306fn build_initial_heap(
307    candidates: &[Fragment],
308    rel: &FxHashMap<FragmentId, f64>,
309    needs: &[InformationNeed],
310    state: &UtilityState,
311    id_to_frag: &mut FxHashMap<FragmentId, Fragment>,
312) -> BinaryHeap<HeapEntry> {
313    let mut heap = BinaryHeap::new();
314    for frag in candidates {
315        if frag.token_count > 0 {
316            let density = compute_density(
317                frag,
318                rel.get(&frag.id).copied().unwrap_or(0.0),
319                needs,
320                state,
321            );
322            heap.push(HeapEntry {
323                neg_density: -density,
324                frag_id: frag.id.clone(),
325                version: 0,
326            });
327            id_to_frag.insert(frag.id.clone(), frag.clone());
328        }
329    }
330    heap
331}
332
333fn find_best_candidate_heap(
334    heap: &mut BinaryHeap<HeapEntry>,
335    current_version: u32,
336    id_to_frag: &FxHashMap<FragmentId, Fragment>,
337    selected_ids: &IntervalIndex,
338    remaining_budget: u32,
339    rel: &FxHashMap<FragmentId, f64>,
340    needs: &[InformationNeed],
341    state: &UtilityState,
342) -> (Option<Fragment>, f64, u32) {
343    let cv = current_version;
344    while let Some(entry) = heap.pop() {
345        let frag = match id_to_frag.get(&entry.frag_id) {
346            Some(f) => f,
347            None => continue,
348        };
349        if frag.token_count > remaining_budget {
350            continue;
351        }
352        if selected_ids.overlaps(frag) {
353            continue;
354        }
355        if entry.version < cv {
356            let new_density = compute_density(
357                frag,
358                rel.get(&frag.id).copied().unwrap_or(0.0),
359                needs,
360                state,
361            );
362            heap.push(HeapEntry {
363                neg_density: -new_density,
364                frag_id: frag.id.clone(),
365                version: cv,
366            });
367            continue;
368        }
369        let actual_density = -entry.neg_density;
370        if actual_density <= 0.0 {
371            return (None, 0.0, cv);
372        }
373        return (Some(frag.clone()), actual_density, cv + 1);
374    }
375    (None, 0.0, cv)
376}
377
378fn find_best_singleton(
379    non_core: &[Fragment],
380    base_selected_ids: &IntervalIndex,
381    base_budget: u32,
382    rel: &FxHashMap<FragmentId, f64>,
383    needs: &[InformationNeed],
384    base_state: &UtilityState,
385) -> (Option<Fragment>, f64) {
386    let mut best_singleton = None;
387    let mut best_gain = 0.0;
388    for f in non_core {
389        if f.token_count > base_budget {
390            continue;
391        }
392        if base_selected_ids.overlaps(f) {
393            continue;
394        }
395        let gain = marginal_gain(f, rel.get(&f.id).copied().unwrap_or(0.0), needs, base_state);
396        if gain > best_gain {
397            best_gain = gain;
398            best_singleton = Some(f.clone());
399        }
400    }
401    (best_singleton, best_gain)
402}
403
404/// Paper-aligned strict Khuller H₁: `argmax_{f ∈ F, |f| ≤ B} U({f})`.
405/// Iterates the FULL ground set (including core), gates on the FULL
406/// budget B (not the residual after partial-core packing), and evaluates
407/// each candidate as a lone selection against an empty utility state.
408///
409/// This is the comparator that, together with the greedy chain, gives
410/// the `(1-1/e)/2` approximation guarantee from Khuller-Moss-Naor 1999
411/// for monotone submodular maximization under a knapsack constraint.
412/// Restricting H₁ to `non_core` with budget `B − cost(packed_core)`
413/// (the prior `find_best_singleton`) is a strict relaxation that
414/// excludes any single high-utility fragment with `|f| > B − β_core·B`.
415fn find_best_singleton_full_set(
416    fragments: &[Fragment],
417    budget_tokens: u32,
418    rel: &FxHashMap<FragmentId, f64>,
419    needs: &[InformationNeed],
420    empty_state: &UtilityState,
421) -> (Option<Fragment>, f64) {
422    let mut best = None;
423    let mut best_gain = 0.0;
424    for f in fragments {
425        if f.token_count == 0 || f.token_count > budget_tokens {
426            continue;
427        }
428        let gain = marginal_gain(
429            f,
430            rel.get(&f.id).copied().unwrap_or(0.0),
431            needs,
432            empty_state,
433        );
434        if gain > best_gain {
435            best_gain = gain;
436            best = Some(f.clone());
437        }
438    }
439    (best, best_gain)
440}
441
442fn init_selection_state(
443    core_ids: &FxHashSet<FragmentId>,
444    rel: &FxHashMap<FragmentId, f64>,
445    budget_tokens: u32,
446    file_importance: Option<&FxHashMap<Arc<str>, f64>>,
447) -> SelectionState {
448    let mut utility_state = UtilityState::default();
449    utility_state.r_cap = compute_r_cap(rel, Some(core_ids));
450    utility_state.changed_dirs = core_ids
451        .iter()
452        .filter_map(|cid| {
453            std::path::Path::new(cid.path.as_ref())
454                .parent()
455                .map(|p| p.to_path_buf())
456        })
457        .collect();
458    if let Some(fi) = file_importance {
459        utility_state.file_importance.clone_from(fi);
460    }
461    SelectionState {
462        selected: Vec::new(),
463        selected_ids: IntervalIndex::new(),
464        remaining_budget: budget_tokens,
465        utility_state,
466    }
467}
468
469fn run_greedy_loop_heap(
470    heap: &mut BinaryHeap<HeapEntry>,
471    id_to_frag: &FxHashMap<FragmentId, Fragment>,
472    state: &mut SelectionState,
473    rel: &FxHashMap<FragmentId, f64>,
474    needs: &[InformationNeed],
475    tau: f64,
476    _initial_budget: u32,
477) -> (usize, f64, usize) {
478    let mut current_version = 0u32;
479    let mut peak_density: f64 = 0.0;
480    let mut loop_iters: usize = 0;
481
482    while !heap.is_empty() && state.remaining_budget > 0 {
483        loop_iters += 1;
484        let (best_frag, best_density, new_version) = find_best_candidate_heap(
485            heap,
486            current_version,
487            id_to_frag,
488            &state.selected_ids,
489            state.remaining_budget,
490            rel,
491            needs,
492            &state.utility_state,
493        );
494        current_version = new_version;
495
496        let best_frag = match best_frag {
497            Some(f) => f,
498            None => break,
499        };
500        if best_density <= 0.0 {
501            break;
502        }
503
504        if best_density > peak_density {
505            peak_density = best_density;
506        } else if peak_density > 0.0 && best_density < tau * peak_density {
507            break;
508        }
509
510        state.selected.push(best_frag.clone());
511        state.selected_ids.add_id(&best_frag.id);
512        state.remaining_budget = state.remaining_budget.saturating_sub(best_frag.token_count);
513        let rel_score = rel.get(&best_frag.id).copied().unwrap_or(0.0);
514        apply_fragment(&best_frag, rel_score, needs, &mut state.utility_state);
515    }
516
517    let threshold = tau * peak_density;
518    (state.selected.len(), threshold, loop_iters)
519}
520
521fn setup_and_select_core(
522    fragments: &[Fragment],
523    core_ids: &FxHashSet<FragmentId>,
524    rel: &FxHashMap<FragmentId, f64>,
525    needs: &[InformationNeed],
526    budget_tokens: u32,
527    file_importance: Option<&FxHashMap<Arc<str>, f64>>,
528    core_excerpts: Option<&FxHashMap<FragmentId, Fragment>>,
529) -> (SelectionState, Vec<Fragment>, Vec<Fragment>, bool) {
530    let mut core_fragments: Vec<Fragment> = fragments
531        .iter()
532        .filter(|f| core_ids.contains(&f.id))
533        .cloned()
534        .collect();
535    core_fragments.sort_by(|a, b| {
536        let ta = if a.token_count > 0 {
537            a.token_count
538        } else {
539            SENTINEL_TOKEN_COUNT
540        };
541        let tb = if b.token_count > 0 {
542            b.token_count
543        } else {
544            SENTINEL_TOKEN_COUNT
545        };
546        ta.cmp(&tb)
547            .then(a.line_count().cmp(&b.line_count()))
548            .then(a.start_line().cmp(&b.start_line()))
549    });
550
551    let non_core_fragments: Vec<Fragment> = fragments
552        .iter()
553        .filter(|f| !core_ids.contains(&f.id))
554        .cloned()
555        .collect();
556
557    let sig_lookup = build_signature_lookup(fragments, &core_fragments, core_excerpts);
558    let mut state = init_selection_state(core_ids, rel, budget_tokens, file_importance);
559    let satisfied_core_ids = select_core_fragments(
560        &core_fragments,
561        rel,
562        needs,
563        &mut state,
564        budget_tokens,
565        &sig_lookup,
566        core_excerpts,
567    );
568
569    // A core represented by a substitute (signature stub or downshifted
570    // excerpt) is satisfied even though its own id is absent from the
571    // selection — offering the full fragment back to the greedy would undo the
572    // substitution.
573    let skipped_core: Vec<FragmentId> = core_ids
574        .iter()
575        .filter(|id| !satisfied_core_ids.contains(*id))
576        .cloned()
577        .collect();
578
579    let mut non_core_with_skipped = non_core_fragments;
580    if !skipped_core.is_empty() {
581        let skipped_set: FxHashSet<FragmentId> = skipped_core.into_iter().collect();
582        for cf in &core_fragments {
583            if skipped_set.contains(&cf.id) {
584                non_core_with_skipped.push(cf.clone());
585            }
586        }
587    }
588
589    let should_return_early = state.remaining_budget == 0;
590    let selected_copy = state.selected.clone();
591    (
592        state,
593        non_core_with_skipped,
594        selected_copy,
595        should_return_early,
596    )
597}
598
599pub fn lazy_greedy_select(
600    fragments: Vec<Fragment>,
601    core_ids: &FxHashSet<FragmentId>,
602    rel: &FxHashMap<FragmentId, f64>,
603    needs: &[InformationNeed],
604    budget_tokens: u32,
605    tau: f64,
606    file_importance: Option<&FxHashMap<Arc<str>, f64>>,
607    core_excerpts: Option<&FxHashMap<FragmentId, Fragment>>,
608) -> SelectionResult {
609    if fragments.is_empty() {
610        return SelectionResult {
611            selected: Vec::new(),
612            reason: SelectionReason::NoCandidates,
613            used_tokens: 0,
614            utility: 0.0,
615            greedy_iters: 0,
616            stopping_certificate: 0.0,
617        };
618    }
619
620    let (mut state, non_core_fragments, _selected_core, should_return_early) =
621        setup_and_select_core(
622            &fragments,
623            core_ids,
624            rel,
625            needs,
626            budget_tokens,
627            file_importance,
628            core_excerpts,
629        );
630
631    if should_return_early {
632        let used = budget_tokens - state.remaining_budget;
633        return SelectionResult {
634            selected: state.selected,
635            reason: SelectionReason::BudgetExhausted,
636            used_tokens: used,
637            utility: utility_value(&state.utility_state),
638            greedy_iters: 0,
639            stopping_certificate: 0.0,
640        };
641    }
642
643    let base_state = state.utility_state.copy();
644    let base_selected = state.selected.clone();
645    let base_budget = state.remaining_budget;
646
647    let candidates: Vec<Fragment> = non_core_fragments
648        .iter()
649        .filter(|f| !state.selected_ids.overlaps(f))
650        .cloned()
651        .collect();
652    let candidates = drop_redundant_signatures(&candidates, state.remaining_budget);
653
654    let mut id_to_frag: FxHashMap<FragmentId, Fragment> = FxHashMap::default();
655    let mut heap = build_initial_heap(
656        &candidates,
657        rel,
658        needs,
659        &state.utility_state,
660        &mut id_to_frag,
661    );
662
663    let (_, threshold, greedy_iters) = run_greedy_loop_heap(
664        &mut heap,
665        &id_to_frag,
666        &mut state,
667        rel,
668        needs,
669        tau,
670        budget_tokens,
671    );
672
673    let greedy_utility = utility_value(&state.utility_state);
674
675    let mut base_selected_ids = IntervalIndex::new();
676    for f in &base_selected {
677        base_selected_ids.add_id(&f.id);
678    }
679
680    let (best_singleton, best_gain) = find_best_singleton(
681        &non_core_fragments,
682        &base_selected_ids,
683        base_budget,
684        rel,
685        needs,
686        &base_state,
687    );
688
689    let empty_state = init_selection_state(core_ids, rel, budget_tokens, file_importance);
690    let (full_singleton, full_singleton_gain) = find_best_singleton_full_set(
691        &fragments,
692        budget_tokens,
693        rel,
694        needs,
695        &empty_state.utility_state,
696    );
697
698    let mut best_alt_utility = greedy_utility;
699    let mut best_alt: Option<(u32, Vec<Fragment>)> = None;
700
701    if let Some(ref singleton) = best_singleton {
702        let u = utility_value(&base_state) + best_gain;
703        if u > best_alt_utility {
704            best_alt_utility = u;
705            let mut sel = base_selected.clone();
706            sel.push(singleton.clone());
707            best_alt = Some((selection_cost(&sel), sel));
708        }
709    }
710
711    if let Some(ref full) = full_singleton {
712        let u = utility_value(&empty_state.utility_state) + full_singleton_gain;
713        // Additive on top of the core selection, never a replacement for it.
714        // This branch used to return `vec![full]` outright, so a single heavy
715        // fragment whose standalone utility beat the greedy chain's discarded
716        // every changed-code fragment — the one thing the output exists to
717        // carry. `ensure_changed_files_represented` could not reliably undo it
718        // either: it only had `budget - full.token_count` left and only picks a
719        // fragment that fits. The two utilities are also measured from
720        // different baselines (this one from an empty state, `greedy_utility`
721        // from the core base), so the comparison can only ever be a heuristic
722        // nudge — not grounds for dropping the core.
723        //
724        // H₁ iterates the FULL ground set, so its winner can be a core the
725        // core pass already packed. Then this arm has nothing to add: its
726        // "alternative" is `base_selected` verbatim, a strict subset of the
727        // greedy result. Utility is monotone and `greedy_utility` already
728        // contains that core's contribution, so `u > best_alt_utility` should
729        // be unreachable in that case — this makes the reasoning a condition
730        // rather than an assumption, because the arm's cost accounting has no
731        // meaning when nothing is appended.
732        let already_selected = base_selected.iter().any(|f| f.id == full.id);
733        if u > best_alt_utility && full.token_count <= base_budget && !already_selected {
734            best_alt_utility = u;
735            let mut sel = base_selected.clone();
736            sel.push(full.clone());
737            best_alt = Some((selection_cost(&sel), sel));
738        }
739    }
740
741    if let Some((used, sel)) = best_alt {
742        return SelectionResult {
743            selected: sel,
744            reason: SelectionReason::BestSingleton,
745            used_tokens: used,
746            utility: best_alt_utility,
747            greedy_iters,
748            stopping_certificate: 0.0,
749        };
750    }
751
752    let used = budget_tokens - state.remaining_budget;
753    let reason = if state.remaining_budget == 0 {
754        SelectionReason::BudgetExhausted
755    } else if greedy_utility <= 0.0 {
756        SelectionReason::NoUtility
757    } else if state.selected.is_empty() || state.selected.len() == base_selected.len() {
758        SelectionReason::NoCandidates
759    } else if threshold > 0.0 && !heap.is_empty() {
760        SelectionReason::StoppedByTau
761    } else {
762        SelectionReason::NoCandidates
763    };
764
765    let stopping_certificate = if matches!(reason, SelectionReason::StoppedByTau) {
766        threshold * f64::from(state.remaining_budget)
767    } else {
768        0.0
769    };
770
771    SelectionResult {
772        selected: state.selected,
773        reason,
774        used_tokens: used,
775        utility: greedy_utility,
776        greedy_iters,
777        stopping_certificate,
778    }
779}
780
781#[cfg(test)]
782mod tests {
783    use super::*;
784    use crate::types::FragmentKind;
785
786    fn frag(path: &str, start: u32, end: u32, kind: FragmentKind, tokens: u32) -> Fragment {
787        let mut identifiers = FxHashSet::default();
788        identifiers.insert(format!("sym_{path}_{start}"));
789        Fragment {
790            id: FragmentId::new(Arc::from(path), start, end),
791            kind,
792            content: Arc::from(format!("// {path}:{start}-{end}\n")),
793            identifiers,
794            token_count: tokens,
795            symbol_name: Some(format!("sym_{start}")),
796        }
797    }
798
799    fn rel_map(frags: &[Fragment], score: f64) -> FxHashMap<FragmentId, f64> {
800        frags.iter().map(|f| (f.id.clone(), score)).collect()
801    }
802
803    fn cost_of(selected: &[Fragment]) -> u32 {
804        selected.iter().map(|f| f.token_count).sum()
805    }
806
807    /// The budget is a hard contract (`cost(C) <= B`); four separate call sites
808    /// gate on it and none of them was asserted. A `pick_smallest_fitting` that
809    /// returns a non-fitting candidate is one `if` away, and the oracle corpus
810    /// can never catch it because its budget is always >=2.5x the whole repo.
811    #[test]
812    fn selection_never_exceeds_the_budget() {
813        let frags: Vec<Fragment> = (0..12)
814            .map(|i| {
815                frag(
816                    "a.rs",
817                    1 + i * 50,
818                    40 + i * 50,
819                    FragmentKind::Function,
820                    30 + i * 17,
821                )
822            })
823            .collect();
824        let core: FxHashSet<FragmentId> = std::iter::once(frags[0].id.clone()).collect();
825        let rel = rel_map(&frags, 0.7);
826
827        for budget in [1u32, 7, 30, 31, 60, 200, 1_000] {
828            let result =
829                lazy_greedy_select(frags.clone(), &core, &rel, &[], budget, 0.12, None, None);
830            assert!(
831                cost_of(&result.selected) <= budget,
832                "budget {budget} overrun: cost {} via {:?}",
833                cost_of(&result.selected),
834                result.reason
835            );
836            assert!(
837                result.used_tokens <= budget,
838                "reported used_tokens {} exceeds budget {budget}",
839                result.used_tokens
840            );
841        }
842    }
843
844    /// `used_tokens` is what every downstream budget report reads, and it was
845    /// only ever asserted as `<= budget`. Three code paths compute it by
846    /// different budget arithmetic; this pins the equality they all have to
847    /// satisfy, so a future path that reconstructs the figure instead of
848    /// measuring the selection fails here rather than in a results table.
849    #[test]
850    fn reported_used_tokens_always_equals_the_cost_of_the_returned_selection() {
851        let shapes: Vec<Vec<Fragment>> = vec![
852            vec![
853                frag("changed.rs", 1, 8, FragmentKind::Function, 20),
854                frag("other.rs", 1, 400, FragmentKind::Class, 900),
855                frag("other.rs", 500, 520, FragmentKind::Function, 40),
856            ],
857            // A lone, heavy core: H₁ over the full ground set can only win with
858            // a fragment the core pass already placed.
859            vec![frag("changed.rs", 1, 200, FragmentKind::Class, 400)],
860            (0..6)
861                .map(|i| {
862                    frag(
863                        "a.rs",
864                        1 + i * 20,
865                        10 + i * 20,
866                        FragmentKind::Function,
867                        20 + i * 60,
868                    )
869                })
870                .collect(),
871        ];
872
873        for frags in shapes {
874            let core: FxHashSet<FragmentId> = std::iter::once(frags[0].id.clone()).collect();
875            let rel = rel_map(&frags, 0.8);
876            for budget in [50u32, 120, 460, 1_000, 5_000] {
877                let result =
878                    lazy_greedy_select(frags.clone(), &core, &rel, &[], budget, 0.12, None, None);
879                assert_eq!(
880                    result.used_tokens,
881                    cost_of(&result.selected),
882                    "reason {:?} at budget {budget}: reported {} but selection costs {}",
883                    result.reason,
884                    result.used_tokens,
885                    cost_of(&result.selected)
886                );
887            }
888        }
889    }
890
891    /// A core that is mostly unchanged must be placed as its hunk-window
892    /// excerpt, not in full — the over-dump behind #105/#107/#149. The excerpt
893    /// arrives through `core_excerpts`, keyed by the core it replaces.
894    #[test]
895    fn a_mostly_unchanged_core_is_placed_as_its_excerpt() {
896        let core = frag("script.sh", 1, 122, FragmentKind::Chunk, 600);
897        let excerpt = frag("script.sh", 58, 64, FragmentKind::Excerpt, 40);
898        let core_ids: FxHashSet<FragmentId> = std::iter::once(core.id.clone()).collect();
899        let rel = rel_map(&[core.clone()], 1.0);
900        let mut excerpts: FxHashMap<FragmentId, Fragment> = FxHashMap::default();
901        excerpts.insert(core.id.clone(), excerpt.clone());
902
903        let result = lazy_greedy_select(
904            vec![core.clone()],
905            &core_ids,
906            &rel,
907            &[],
908            8_000,
909            0.12,
910            None,
911            Some(&excerpts),
912        );
913
914        let ids: Vec<String> = result
915            .selected
916            .iter()
917            .map(|f| format!("{}:{}-{}", f.id.path, f.id.start_line, f.id.end_line))
918            .collect();
919        assert!(
920            result.selected.iter().any(|f| f.id == excerpt.id),
921            "core was not downshifted to its excerpt: {ids:?}"
922        );
923        assert!(
924            !result.selected.iter().any(|f| f.id == core.id),
925            "the full core was emitted alongside the excerpt: {ids:?}"
926        );
927    }
928
929    #[test]
930    fn selected_fragments_never_overlap_and_are_never_duplicated() {
931        let frags: Vec<Fragment> = (0..8)
932            .map(|i| frag("a.rs", 1 + i * 10, 12 + i * 10, FragmentKind::Function, 25))
933            .collect();
934        let core: FxHashSet<FragmentId> = std::iter::once(frags[0].id.clone()).collect();
935        let rel = rel_map(&frags, 0.9);
936        let result = lazy_greedy_select(frags, &core, &rel, &[], 400, 0.12, None, None);
937
938        let ids: FxHashSet<FragmentId> = result.selected.iter().map(|f| f.id.clone()).collect();
939        assert_eq!(
940            ids.len(),
941            result.selected.len(),
942            "duplicate fragment selected"
943        );
944    }
945
946    /// `find_best_singleton_full_set` used to return `vec![full]`, discarding
947    /// every core fragment. The core IS the changed code, so a selection that
948    /// drops all of it answers a different question than the one asked.
949    #[test]
950    fn a_winning_singleton_never_evicts_the_core_selection() {
951        // A heavy, highly relevant non-core fragment is the shape that makes the
952        // full-set singleton win.
953        let core_frag = frag("changed.rs", 1, 8, FragmentKind::Function, 20);
954        let heavy = frag("other.rs", 1, 400, FragmentKind::Class, 900);
955        let filler = frag("other.rs", 500, 520, FragmentKind::Function, 40);
956        let frags = vec![core_frag.clone(), heavy.clone(), filler];
957
958        let core: FxHashSet<FragmentId> = std::iter::once(core_frag.id.clone()).collect();
959        let mut rel = FxHashMap::default();
960        rel.insert(core_frag.id.clone(), 0.05);
961        rel.insert(heavy.id.clone(), 1.0);
962        rel.insert(frags[2].id.clone(), 0.1);
963
964        let result = lazy_greedy_select(frags, &core, &rel, &[], 2_000, 0.12, None, None);
965        assert!(
966            result.selected.iter().any(|f| core.contains(&f.id)),
967            "no core fragment survived; reason was {:?}",
968            result.reason
969        );
970        assert!(cost_of(&result.selected) <= 2_000);
971    }
972
973    #[test]
974    fn empty_ground_set_reports_no_candidates() {
975        let result = lazy_greedy_select(
976            Vec::new(),
977            &FxHashSet::default(),
978            &FxHashMap::default(),
979            &[],
980            1_000,
981            0.12,
982            None,
983            None,
984        );
985        assert!(result.selected.is_empty());
986        assert_eq!(result.reason, SelectionReason::NoCandidates);
987        assert_eq!(result.used_tokens, 0);
988    }
989
990    /// Keyed on `(path, start_line)`, this used to be last-write-wins, so a
991    /// small co-located sibling could delete the stub that was the only
992    /// affordable representation of an oversized fragment.
993    #[test]
994    fn drop_redundant_signatures_is_independent_of_candidate_order() {
995        let header = frag("a.rs", 10, 12, FragmentKind::Definition, 40);
996        let whole = frag("a.rs", 10, 300, FragmentKind::Class, 4_000);
997        let stub = frag("a.rs", 10, 11, FragmentKind::ClassSignature, 15);
998
999        let forward =
1000            drop_redundant_signatures(&[header.clone(), whole.clone(), stub.clone()], 500);
1001        let backward = drop_redundant_signatures(&[whole, header, stub], 500);
1002
1003        let kinds = |v: &[Fragment]| -> Vec<FragmentKind> { v.iter().map(|f| f.kind).collect() };
1004        assert!(
1005            kinds(&forward).contains(&FragmentKind::ClassSignature),
1006            "the stub for an unaffordable class was dropped: {:?}",
1007            kinds(&forward)
1008        );
1009        let mut a: Vec<FragmentKind> = kinds(&forward);
1010        let mut b: Vec<FragmentKind> = kinds(&backward);
1011        a.sort_by_key(|k| format!("{k:?}"));
1012        b.sort_by_key(|k| format!("{k:?}"));
1013        assert_eq!(a, b, "verdict depended on candidate order");
1014    }
1015
1016    #[test]
1017    fn drop_redundant_signatures_removes_a_stub_whose_full_fragment_fits() {
1018        let whole = frag("a.rs", 10, 40, FragmentKind::Class, 100);
1019        let stub = frag("a.rs", 10, 11, FragmentKind::ClassSignature, 15);
1020        let kept = drop_redundant_signatures(&[whole, stub], 500);
1021        assert!(
1022            !kept.iter().any(|f| f.kind.is_signature()),
1023            "stub survived even though the full fragment fits the budget"
1024        );
1025    }
1026    /// tau is the adaptive stop: once a candidate's density falls below
1027    /// `tau * peak_density` the loop stops instead of spending the rest of the
1028    /// budget. The oracle corpus used to run at tau=0.0, which made the
1029    /// predicate unreachable, so deleting the rule failed no test; the corpus
1030    /// now runs at the shipped default too (#175). This keeps a direct
1031    /// assertion on the rule that does not depend on corpus wiring.
1032    #[test]
1033    fn tau_stops_the_greedy_loop_before_the_budget_is_spent() {
1034        // Descending relevance against escalating cost gives sharply
1035        // descending density, which is what the stop rule reacts to.
1036        let frags: Vec<Fragment> = (0..6)
1037            .map(|i| {
1038                frag(
1039                    "a.rs",
1040                    1 + i * 20,
1041                    10 + i * 20,
1042                    FragmentKind::Function,
1043                    20 + i * i * 120,
1044                )
1045            })
1046            .collect();
1047        let core: FxHashSet<FragmentId> = std::iter::once(frags[0].id.clone()).collect();
1048        let mut rel = FxHashMap::default();
1049        for (i, f) in frags.iter().enumerate() {
1050            rel.insert(f.id.clone(), 1.0 / (1.0 + 3.0 * i as f64));
1051        }
1052
1053        let budget = 10_000;
1054        let default_tau = lazy_greedy_select(
1055            frags.clone(),
1056            &core,
1057            &rel,
1058            &[],
1059            budget,
1060            crate::config::limits::DEFAULT_STOPPING_THRESHOLD,
1061            None,
1062            None,
1063        );
1064        let no_tau = lazy_greedy_select(frags, &core, &rel, &[], budget, 0.0, None, None);
1065
1066        assert_eq!(
1067            default_tau.reason,
1068            SelectionReason::StoppedByTau,
1069            "the adaptive stop did not fire at the shipped default"
1070        );
1071        assert!(
1072            default_tau.selected.len() < no_tau.selected.len(),
1073            "tau={} selected {} fragments, same as tau=0.0 — the rule is inert",
1074            crate::config::limits::DEFAULT_STOPPING_THRESHOLD,
1075            default_tau.selected.len()
1076        );
1077        assert!(
1078            default_tau.used_tokens < no_tau.used_tokens,
1079            "the stop saved no budget: {} vs {}",
1080            default_tau.used_tokens,
1081            no_tau.used_tokens
1082        );
1083        assert!(
1084            default_tau.stopping_certificate > 0.0,
1085            "StoppedByTau must carry a positive certificate"
1086        );
1087        assert_eq!(
1088            no_tau.stopping_certificate, 0.0,
1089            "tau=0.0 cannot produce a stopping certificate"
1090        );
1091    }
1092}