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
18fn 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 pub greedy_iters: usize,
58 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 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 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 let mut satisfied: FxHashSet<FragmentId> = FxHashSet::default();
213 let sel_cfg = selection();
214 let core_budget = (budget_tokens as f64 * sel_cfg.core_budget_fraction) as u32;
215 let file_ceiling = (budget_tokens as f64 * sel_cfg.per_file_budget_fraction) as u32;
219 let mut file_spent: FxHashMap<Arc<str>, u32> = FxHashMap::default();
220 let mut core_used = 0u32;
224
225 let mut sorted_core: Vec<&Fragment> = core_fragments.iter().collect();
226 sorted_core.sort_by(|a, b| {
227 let ra = rel.get(&a.id).copied().unwrap_or(0.0);
228 let rb = rel.get(&b.id).copied().unwrap_or(0.0);
229 rb.total_cmp(&ra)
230 });
231
232 let place_fragment = |frag: &Fragment,
233 core_used: &mut u32,
234 state: &mut SelectionState,
235 rel_score: f64,
236 file_spent: &mut FxHashMap<Arc<str>, u32>| {
237 state.selected.push(frag.clone());
238 state.selected_ids.add_id(&frag.id);
239 state.remaining_budget = state.remaining_budget.saturating_sub(frag.token_count);
240 *core_used += frag.token_count;
241 *file_spent.entry(frag.id.path.clone()).or_insert(0) += frag.token_count;
242 apply_fragment(frag, rel_score, needs, &mut state.utility_state);
243 };
244
245 let mut skipped: Vec<(FragmentId, &Fragment)> = Vec::new();
248 for frag in &sorted_core {
249 let core_id = frag.id.clone();
255 let frag: &Fragment = core_excerpts
256 .and_then(|e| e.get(&frag.id))
257 .filter(|excerpt| crate::excerpt::is_downshift_worthwhile(frag, excerpt))
258 .unwrap_or(frag);
259 if state.selected_ids.is_superset_of(frag) {
260 satisfied.insert(core_id);
261 continue;
262 }
263 let spent = file_spent.get(&frag.id.path).copied().unwrap_or(0);
264 let over_file_ceiling = spent > 0 && spent + frag.token_count > file_ceiling;
265 if core_used + frag.token_count > core_budget || over_file_ceiling {
266 if let Some(sig) = sig_lookup.get(&core_id) {
267 if !state.selected_ids.contains(&sig.id)
268 && core_used + sig.token_count <= core_budget
269 {
270 let rel_score = rel.get(&core_id).copied().unwrap_or(0.0);
271 place_fragment(sig, &mut core_used, state, rel_score, &mut file_spent);
272 satisfied.insert(core_id);
273 continue;
274 }
275 }
276 skipped.push((core_id, frag));
277 continue;
278 }
279
280 let rel_score = rel.get(&core_id).copied().unwrap_or(0.0);
281 place_fragment(frag, &mut core_used, state, rel_score, &mut file_spent);
282 satisfied.insert(core_id);
283 }
284
285 if !skipped.is_empty() {
291 skipped.sort_by(|(_, a), (_, b)| a.token_count.cmp(&b.token_count));
292 for (core_id, frag) in skipped {
293 if state.remaining_budget == 0 {
294 break;
295 }
296 if state.selected_ids.is_superset_of(frag) {
297 satisfied.insert(core_id);
298 continue;
299 }
300 let rel_score = rel.get(&core_id).copied().unwrap_or(0.0);
301 if frag.token_count <= state.remaining_budget {
302 place_fragment(frag, &mut core_used, state, rel_score, &mut file_spent);
303 satisfied.insert(core_id);
304 } else if let Some(sig) = sig_lookup.get(&core_id) {
305 if !state.selected_ids.contains(&sig.id)
306 && sig.token_count <= state.remaining_budget
307 {
308 place_fragment(sig, &mut core_used, state, rel_score, &mut file_spent);
309 satisfied.insert(core_id);
310 }
311 }
312 }
313 }
314
315 satisfied
316}
317
318fn build_initial_heap(
319 candidates: &[Fragment],
320 rel: &FxHashMap<FragmentId, f64>,
321 needs: &[InformationNeed],
322 state: &UtilityState,
323 id_to_frag: &mut FxHashMap<FragmentId, Fragment>,
324) -> BinaryHeap<HeapEntry> {
325 let mut heap = BinaryHeap::new();
326 for frag in candidates {
327 if frag.token_count > 0 {
328 let density = compute_density(
329 frag,
330 rel.get(&frag.id).copied().unwrap_or(0.0),
331 needs,
332 state,
333 );
334 heap.push(HeapEntry {
335 neg_density: -density,
336 frag_id: frag.id.clone(),
337 version: 0,
338 });
339 id_to_frag.insert(frag.id.clone(), frag.clone());
340 }
341 }
342 heap
343}
344
345fn find_best_candidate_heap(
346 heap: &mut BinaryHeap<HeapEntry>,
347 current_version: u32,
348 id_to_frag: &FxHashMap<FragmentId, Fragment>,
349 selected_ids: &IntervalIndex,
350 remaining_budget: u32,
351 rel: &FxHashMap<FragmentId, f64>,
352 needs: &[InformationNeed],
353 state: &UtilityState,
354) -> (Option<Fragment>, f64, u32) {
355 let cv = current_version;
356 while let Some(entry) = heap.pop() {
357 let frag = match id_to_frag.get(&entry.frag_id) {
358 Some(f) => f,
359 None => continue,
360 };
361 if frag.token_count > remaining_budget {
362 continue;
363 }
364 if selected_ids.overlaps(frag) {
365 continue;
366 }
367 if entry.version < cv {
368 let new_density = compute_density(
369 frag,
370 rel.get(&frag.id).copied().unwrap_or(0.0),
371 needs,
372 state,
373 );
374 heap.push(HeapEntry {
375 neg_density: -new_density,
376 frag_id: frag.id.clone(),
377 version: cv,
378 });
379 continue;
380 }
381 let actual_density = -entry.neg_density;
382 if actual_density <= 0.0 {
383 return (None, 0.0, cv);
384 }
385 return (Some(frag.clone()), actual_density, cv + 1);
386 }
387 (None, 0.0, cv)
388}
389
390fn find_best_singleton(
391 non_core: &[Fragment],
392 base_selected_ids: &IntervalIndex,
393 base_budget: u32,
394 rel: &FxHashMap<FragmentId, f64>,
395 needs: &[InformationNeed],
396 base_state: &UtilityState,
397) -> (Option<Fragment>, f64) {
398 let mut best_singleton = None;
399 let mut best_gain = 0.0;
400 for f in non_core {
401 if f.token_count > base_budget {
402 continue;
403 }
404 if base_selected_ids.overlaps(f) {
405 continue;
406 }
407 let gain = marginal_gain(f, rel.get(&f.id).copied().unwrap_or(0.0), needs, base_state);
408 if gain > best_gain {
409 best_gain = gain;
410 best_singleton = Some(f.clone());
411 }
412 }
413 (best_singleton, best_gain)
414}
415
416fn find_best_singleton_full_set(
428 fragments: &[Fragment],
429 budget_tokens: u32,
430 rel: &FxHashMap<FragmentId, f64>,
431 needs: &[InformationNeed],
432 empty_state: &UtilityState,
433) -> (Option<Fragment>, f64) {
434 let mut best = None;
435 let mut best_gain = 0.0;
436 for f in fragments {
437 if f.token_count == 0 || f.token_count > budget_tokens {
438 continue;
439 }
440 let gain = marginal_gain(
441 f,
442 rel.get(&f.id).copied().unwrap_or(0.0),
443 needs,
444 empty_state,
445 );
446 if gain > best_gain {
447 best_gain = gain;
448 best = Some(f.clone());
449 }
450 }
451 (best, best_gain)
452}
453
454fn init_selection_state(
455 core_ids: &FxHashSet<FragmentId>,
456 rel: &FxHashMap<FragmentId, f64>,
457 budget_tokens: u32,
458 file_importance: Option<&FxHashMap<Arc<str>, f64>>,
459) -> SelectionState {
460 let mut utility_state = UtilityState::default();
461 utility_state.r_cap = compute_r_cap(rel, Some(core_ids));
462 utility_state.changed_dirs = core_ids
463 .iter()
464 .filter_map(|cid| {
465 std::path::Path::new(cid.path.as_ref())
466 .parent()
467 .map(|p| p.to_path_buf())
468 })
469 .collect();
470 if let Some(fi) = file_importance {
471 utility_state.file_importance.clone_from(fi);
472 }
473 SelectionState {
474 selected: Vec::new(),
475 selected_ids: IntervalIndex::new(),
476 remaining_budget: budget_tokens,
477 utility_state,
478 }
479}
480
481#[allow(clippy::too_many_arguments)]
482fn run_greedy_loop_heap(
483 heap: &mut BinaryHeap<HeapEntry>,
484 id_to_frag: &FxHashMap<FragmentId, Fragment>,
485 state: &mut SelectionState,
486 rel: &FxHashMap<FragmentId, f64>,
487 needs: &[InformationNeed],
488 tau: f64,
489 _initial_budget: u32,
490 admissible_files: Option<&FxHashSet<Arc<str>>>,
491) -> (usize, f64, usize) {
492 let mut current_version = 0u32;
493 let mut peak_density: f64 = 0.0;
494 let mut loop_iters: usize = 0;
495 let mut open_files: FxHashSet<Arc<str>> =
501 state.selected.iter().map(|f| f.id.path.clone()).collect();
502 let file_ceiling = (_initial_budget as f64 * selection().per_file_budget_fraction) as u32;
508 let mut file_spent: FxHashMap<Arc<str>, u32> = FxHashMap::default();
509 for f in &state.selected {
510 *file_spent.entry(f.id.path.clone()).or_insert(0) += f.token_count;
511 }
512 let mut deferred: Vec<HeapEntry> = Vec::new();
513 let mut ceiling_active = true;
514
515 loop {
516 while !heap.is_empty() && state.remaining_budget > 0 {
517 loop_iters += 1;
518 let (best_frag, best_density, new_version) = find_best_candidate_heap(
519 heap,
520 current_version,
521 id_to_frag,
522 &state.selected_ids,
523 state.remaining_budget,
524 rel,
525 needs,
526 &state.utility_state,
527 );
528 current_version = new_version;
529
530 let best_frag = match best_frag {
531 Some(f) => f,
532 None => break,
533 };
534 if best_density <= 0.0 {
535 break;
536 }
537
538 if let Some(admissible) = admissible_files {
539 if !open_files.contains(&best_frag.id.path)
540 && !admissible.contains(&best_frag.id.path)
541 {
542 continue;
543 }
544 }
545
546 if ceiling_active {
547 let spent = file_spent.get(&best_frag.id.path).copied().unwrap_or(0);
548 if spent > 0 && spent + best_frag.token_count > file_ceiling {
549 deferred.push(HeapEntry {
550 neg_density: -best_density,
551 frag_id: best_frag.id.clone(),
552 version: current_version,
553 });
554 continue;
555 }
556 }
557
558 if best_density > peak_density {
559 peak_density = best_density;
560 } else if peak_density > 0.0 && best_density < tau * peak_density {
561 break;
562 }
563
564 open_files.insert(best_frag.id.path.clone());
565 *file_spent.entry(best_frag.id.path.clone()).or_insert(0) += best_frag.token_count;
566 state.selected.push(best_frag.clone());
567 state.selected_ids.add_id(&best_frag.id);
568 state.remaining_budget = state.remaining_budget.saturating_sub(best_frag.token_count);
569 let rel_score = rel.get(&best_frag.id).copied().unwrap_or(0.0);
570 apply_fragment(&best_frag, rel_score, needs, &mut state.utility_state);
571 }
572
573 if ceiling_active && !deferred.is_empty() && state.remaining_budget > 0 {
574 ceiling_active = false;
575 for e in deferred.drain(..) {
576 heap.push(e);
577 }
578 continue;
579 }
580 break;
581 }
582
583 let threshold = tau * peak_density;
584 (state.selected.len(), threshold, loop_iters)
585}
586
587fn setup_and_select_core(
588 fragments: &[Fragment],
589 core_ids: &FxHashSet<FragmentId>,
590 rel: &FxHashMap<FragmentId, f64>,
591 needs: &[InformationNeed],
592 budget_tokens: u32,
593 file_importance: Option<&FxHashMap<Arc<str>, f64>>,
594 core_excerpts: Option<&FxHashMap<FragmentId, Fragment>>,
595) -> (SelectionState, Vec<Fragment>, Vec<Fragment>, bool) {
596 let mut core_fragments: Vec<Fragment> = fragments
597 .iter()
598 .filter(|f| core_ids.contains(&f.id))
599 .cloned()
600 .collect();
601 core_fragments.sort_by(|a, b| {
602 let ta = if a.token_count > 0 {
603 a.token_count
604 } else {
605 SENTINEL_TOKEN_COUNT
606 };
607 let tb = if b.token_count > 0 {
608 b.token_count
609 } else {
610 SENTINEL_TOKEN_COUNT
611 };
612 ta.cmp(&tb)
613 .then(a.line_count().cmp(&b.line_count()))
614 .then(a.start_line().cmp(&b.start_line()))
615 });
616
617 let non_core_fragments: Vec<Fragment> = fragments
618 .iter()
619 .filter(|f| !core_ids.contains(&f.id))
620 .cloned()
621 .collect();
622
623 let sig_lookup = build_signature_lookup(fragments, &core_fragments, core_excerpts);
624 let mut state = init_selection_state(core_ids, rel, budget_tokens, file_importance);
625 let satisfied_core_ids = select_core_fragments(
626 &core_fragments,
627 rel,
628 needs,
629 &mut state,
630 budget_tokens,
631 &sig_lookup,
632 core_excerpts,
633 );
634
635 let skipped_core: Vec<FragmentId> = core_ids
640 .iter()
641 .filter(|id| !satisfied_core_ids.contains(*id))
642 .cloned()
643 .collect();
644
645 let mut non_core_with_skipped = non_core_fragments;
646 if !skipped_core.is_empty() {
647 let skipped_set: FxHashSet<FragmentId> = skipped_core.into_iter().collect();
648 for cf in &core_fragments {
649 if skipped_set.contains(&cf.id) {
650 non_core_with_skipped.push(cf.clone());
651 }
652 }
653 }
654
655 let should_return_early = state.remaining_budget == 0;
656 let selected_copy = state.selected.clone();
657 (
658 state,
659 non_core_with_skipped,
660 selected_copy,
661 should_return_early,
662 )
663}
664
665pub fn lazy_greedy_select(
666 fragments: Vec<Fragment>,
667 core_ids: &FxHashSet<FragmentId>,
668 rel: &FxHashMap<FragmentId, f64>,
669 needs: &[InformationNeed],
670 budget_tokens: u32,
671 tau: f64,
672 file_importance: Option<&FxHashMap<Arc<str>, f64>>,
673 core_excerpts: Option<&FxHashMap<FragmentId, Fragment>>,
674 admissible_files: Option<&FxHashSet<Arc<str>>>,
675) -> SelectionResult {
676 if fragments.is_empty() {
677 return SelectionResult {
678 selected: Vec::new(),
679 reason: SelectionReason::NoCandidates,
680 used_tokens: 0,
681 utility: 0.0,
682 greedy_iters: 0,
683 stopping_certificate: 0.0,
684 };
685 }
686
687 let (mut state, non_core_fragments, _selected_core, should_return_early) =
688 setup_and_select_core(
689 &fragments,
690 core_ids,
691 rel,
692 needs,
693 budget_tokens,
694 file_importance,
695 core_excerpts,
696 );
697
698 if should_return_early {
699 let used = budget_tokens - state.remaining_budget;
700 return SelectionResult {
701 selected: state.selected,
702 reason: SelectionReason::BudgetExhausted,
703 used_tokens: used,
704 utility: utility_value(&state.utility_state),
705 greedy_iters: 0,
706 stopping_certificate: 0.0,
707 };
708 }
709
710 let base_state = state.utility_state.copy();
711 let base_selected = state.selected.clone();
712 let base_budget = state.remaining_budget;
713
714 let candidates: Vec<Fragment> = non_core_fragments
715 .iter()
716 .filter(|f| !state.selected_ids.overlaps(f))
717 .cloned()
718 .collect();
719 let candidates = drop_redundant_signatures(&candidates, state.remaining_budget);
720
721 let mut id_to_frag: FxHashMap<FragmentId, Fragment> = FxHashMap::default();
722 let mut heap = build_initial_heap(
723 &candidates,
724 rel,
725 needs,
726 &state.utility_state,
727 &mut id_to_frag,
728 );
729
730 let (_, threshold, greedy_iters) = run_greedy_loop_heap(
731 &mut heap,
732 &id_to_frag,
733 &mut state,
734 rel,
735 needs,
736 tau,
737 budget_tokens,
738 admissible_files,
739 );
740
741 let greedy_utility = utility_value(&state.utility_state);
742
743 let mut base_selected_ids = IntervalIndex::new();
744 for f in &base_selected {
745 base_selected_ids.add_id(&f.id);
746 }
747
748 let (best_singleton, best_gain) = find_best_singleton(
749 &non_core_fragments,
750 &base_selected_ids,
751 base_budget,
752 rel,
753 needs,
754 &base_state,
755 );
756
757 let empty_state = init_selection_state(core_ids, rel, budget_tokens, file_importance);
758 let (full_singleton, full_singleton_gain) = find_best_singleton_full_set(
759 &fragments,
760 budget_tokens,
761 rel,
762 needs,
763 &empty_state.utility_state,
764 );
765
766 let mut best_alt_utility = greedy_utility;
767 let mut best_alt: Option<(u32, Vec<Fragment>)> = None;
768
769 if let Some(ref singleton) = best_singleton {
770 let u = utility_value(&base_state) + best_gain;
771 if u > best_alt_utility {
772 best_alt_utility = u;
773 let mut sel = base_selected.clone();
774 sel.push(singleton.clone());
775 best_alt = Some((selection_cost(&sel), sel));
776 }
777 }
778
779 if let Some(ref full) = full_singleton {
780 let u = utility_value(&empty_state.utility_state) + full_singleton_gain;
781 let already_selected = base_selected.iter().any(|f| f.id == full.id);
801 if u > best_alt_utility && full.token_count <= base_budget && !already_selected {
802 best_alt_utility = u;
803 let mut sel = base_selected.clone();
804 sel.push(full.clone());
805 best_alt = Some((selection_cost(&sel), sel));
806 }
807 }
808
809 if let Some((used, sel)) = best_alt {
810 return SelectionResult {
811 selected: sel,
812 reason: SelectionReason::BestSingleton,
813 used_tokens: used,
814 utility: best_alt_utility,
815 greedy_iters,
816 stopping_certificate: 0.0,
817 };
818 }
819
820 let used = budget_tokens - state.remaining_budget;
821 let reason = if state.remaining_budget == 0 {
822 SelectionReason::BudgetExhausted
823 } else if greedy_utility <= 0.0 {
824 SelectionReason::NoUtility
825 } else if state.selected.is_empty() || state.selected.len() == base_selected.len() {
826 SelectionReason::NoCandidates
827 } else if threshold > 0.0 && !heap.is_empty() {
828 SelectionReason::StoppedByTau
829 } else {
830 SelectionReason::NoCandidates
831 };
832
833 let stopping_certificate = if matches!(reason, SelectionReason::StoppedByTau) {
834 threshold * f64::from(state.remaining_budget)
835 } else {
836 0.0
837 };
838
839 SelectionResult {
840 selected: state.selected,
841 reason,
842 used_tokens: used,
843 utility: greedy_utility,
844 greedy_iters,
845 stopping_certificate,
846 }
847}
848
849#[cfg(test)]
850mod tests {
851 use super::*;
852 use crate::types::FragmentKind;
853
854 fn frag(path: &str, start: u32, end: u32, kind: FragmentKind, tokens: u32) -> Fragment {
855 let mut identifiers = FxHashSet::default();
856 identifiers.insert(format!("sym_{path}_{start}"));
857 Fragment {
858 id: FragmentId::new(Arc::from(path), start, end),
859 kind,
860 content: Arc::from(format!("// {path}:{start}-{end}\n")),
861 identifiers,
862 token_count: tokens,
863 symbol_name: Some(format!("sym_{start}")),
864 }
865 }
866
867 fn rel_map(frags: &[Fragment], score: f64) -> FxHashMap<FragmentId, f64> {
868 frags.iter().map(|f| (f.id.clone(), score)).collect()
869 }
870
871 fn cost_of(selected: &[Fragment]) -> u32 {
872 selected.iter().map(|f| f.token_count).sum()
873 }
874
875 #[test]
880 fn selection_never_exceeds_the_budget() {
881 let frags: Vec<Fragment> = (0..12)
882 .map(|i| {
883 frag(
884 "a.rs",
885 1 + i * 50,
886 40 + i * 50,
887 FragmentKind::Function,
888 30 + i * 17,
889 )
890 })
891 .collect();
892 let core: FxHashSet<FragmentId> = std::iter::once(frags[0].id.clone()).collect();
893 let rel = rel_map(&frags, 0.7);
894
895 for budget in [1u32, 7, 30, 31, 60, 200, 1_000] {
896 let result = lazy_greedy_select(
897 frags.clone(),
898 &core,
899 &rel,
900 &[],
901 budget,
902 0.12,
903 None,
904 None,
905 None,
906 );
907 assert!(
908 cost_of(&result.selected) <= budget,
909 "budget {budget} overrun: cost {} via {:?}",
910 cost_of(&result.selected),
911 result.reason
912 );
913 assert!(
914 result.used_tokens <= budget,
915 "reported used_tokens {} exceeds budget {budget}",
916 result.used_tokens
917 );
918 }
919 }
920
921 #[test]
927 fn reported_used_tokens_always_equals_the_cost_of_the_returned_selection() {
928 let shapes: Vec<Vec<Fragment>> = vec![
929 vec![
930 frag("changed.rs", 1, 8, FragmentKind::Function, 20),
931 frag("other.rs", 1, 400, FragmentKind::Class, 900),
932 frag("other.rs", 500, 520, FragmentKind::Function, 40),
933 ],
934 vec![frag("changed.rs", 1, 200, FragmentKind::Class, 400)],
937 (0..6)
938 .map(|i| {
939 frag(
940 "a.rs",
941 1 + i * 20,
942 10 + i * 20,
943 FragmentKind::Function,
944 20 + i * 60,
945 )
946 })
947 .collect(),
948 ];
949
950 for frags in shapes {
951 let core: FxHashSet<FragmentId> = std::iter::once(frags[0].id.clone()).collect();
952 let rel = rel_map(&frags, 0.8);
953 for budget in [50u32, 120, 460, 1_000, 5_000] {
954 let result = lazy_greedy_select(
955 frags.clone(),
956 &core,
957 &rel,
958 &[],
959 budget,
960 0.12,
961 None,
962 None,
963 None,
964 );
965 assert_eq!(
966 result.used_tokens,
967 cost_of(&result.selected),
968 "reason {:?} at budget {budget}: reported {} but selection costs {}",
969 result.reason,
970 result.used_tokens,
971 cost_of(&result.selected)
972 );
973 }
974 }
975 }
976
977 #[test]
981 fn a_mostly_unchanged_core_is_placed_as_its_excerpt() {
982 let core = frag("script.sh", 1, 122, FragmentKind::Chunk, 600);
983 let excerpt = frag("script.sh", 58, 64, FragmentKind::Excerpt, 40);
984 let core_ids: FxHashSet<FragmentId> = std::iter::once(core.id.clone()).collect();
985 let rel = rel_map(&[core.clone()], 1.0);
986 let mut excerpts: FxHashMap<FragmentId, Fragment> = FxHashMap::default();
987 excerpts.insert(core.id.clone(), excerpt.clone());
988
989 let result = lazy_greedy_select(
990 vec![core.clone()],
991 &core_ids,
992 &rel,
993 &[],
994 8_000,
995 0.12,
996 None,
997 Some(&excerpts),
998 None,
999 );
1000
1001 let ids: Vec<String> = result
1002 .selected
1003 .iter()
1004 .map(|f| format!("{}:{}-{}", f.id.path, f.id.start_line, f.id.end_line))
1005 .collect();
1006 assert!(
1007 result.selected.iter().any(|f| f.id == excerpt.id),
1008 "core was not downshifted to its excerpt: {ids:?}"
1009 );
1010 assert!(
1011 !result.selected.iter().any(|f| f.id == core.id),
1012 "the full core was emitted alongside the excerpt: {ids:?}"
1013 );
1014 }
1015
1016 #[test]
1017 fn selected_fragments_never_overlap_and_are_never_duplicated() {
1018 let frags: Vec<Fragment> = (0..8)
1019 .map(|i| frag("a.rs", 1 + i * 10, 12 + i * 10, FragmentKind::Function, 25))
1020 .collect();
1021 let core: FxHashSet<FragmentId> = std::iter::once(frags[0].id.clone()).collect();
1022 let rel = rel_map(&frags, 0.9);
1023 let result = lazy_greedy_select(frags, &core, &rel, &[], 400, 0.12, None, None, None);
1024
1025 let ids: FxHashSet<FragmentId> = result.selected.iter().map(|f| f.id.clone()).collect();
1026 assert_eq!(
1027 ids.len(),
1028 result.selected.len(),
1029 "duplicate fragment selected"
1030 );
1031 }
1032
1033 #[test]
1037 fn a_winning_singleton_never_evicts_the_core_selection() {
1038 let core_frag = frag("changed.rs", 1, 8, FragmentKind::Function, 20);
1041 let heavy = frag("other.rs", 1, 400, FragmentKind::Class, 900);
1042 let filler = frag("other.rs", 500, 520, FragmentKind::Function, 40);
1043 let frags = vec![core_frag.clone(), heavy.clone(), filler];
1044
1045 let core: FxHashSet<FragmentId> = std::iter::once(core_frag.id.clone()).collect();
1046 let mut rel = FxHashMap::default();
1047 rel.insert(core_frag.id.clone(), 0.05);
1048 rel.insert(heavy.id.clone(), 1.0);
1049 rel.insert(frags[2].id.clone(), 0.1);
1050
1051 let result = lazy_greedy_select(frags, &core, &rel, &[], 2_000, 0.12, None, None, None);
1052 assert!(
1053 result.selected.iter().any(|f| core.contains(&f.id)),
1054 "no core fragment survived; reason was {:?}",
1055 result.reason
1056 );
1057 assert!(cost_of(&result.selected) <= 2_000);
1058 }
1059
1060 #[test]
1061 fn empty_ground_set_reports_no_candidates() {
1062 let result = lazy_greedy_select(
1063 Vec::new(),
1064 &FxHashSet::default(),
1065 &FxHashMap::default(),
1066 &[],
1067 1_000,
1068 0.12,
1069 None,
1070 None,
1071 None,
1072 );
1073 assert!(result.selected.is_empty());
1074 assert_eq!(result.reason, SelectionReason::NoCandidates);
1075 assert_eq!(result.used_tokens, 0);
1076 }
1077
1078 #[test]
1082 fn drop_redundant_signatures_is_independent_of_candidate_order() {
1083 let header = frag("a.rs", 10, 12, FragmentKind::Definition, 40);
1084 let whole = frag("a.rs", 10, 300, FragmentKind::Class, 4_000);
1085 let stub = frag("a.rs", 10, 11, FragmentKind::ClassSignature, 15);
1086
1087 let forward =
1088 drop_redundant_signatures(&[header.clone(), whole.clone(), stub.clone()], 500);
1089 let backward = drop_redundant_signatures(&[whole, header, stub], 500);
1090
1091 let kinds = |v: &[Fragment]| -> Vec<FragmentKind> { v.iter().map(|f| f.kind).collect() };
1092 assert!(
1093 kinds(&forward).contains(&FragmentKind::ClassSignature),
1094 "the stub for an unaffordable class was dropped: {:?}",
1095 kinds(&forward)
1096 );
1097 let mut a: Vec<FragmentKind> = kinds(&forward);
1098 let mut b: Vec<FragmentKind> = kinds(&backward);
1099 a.sort_by_key(|k| format!("{k:?}"));
1100 b.sort_by_key(|k| format!("{k:?}"));
1101 assert_eq!(a, b, "verdict depended on candidate order");
1102 }
1103
1104 #[test]
1105 fn drop_redundant_signatures_removes_a_stub_whose_full_fragment_fits() {
1106 let whole = frag("a.rs", 10, 40, FragmentKind::Class, 100);
1107 let stub = frag("a.rs", 10, 11, FragmentKind::ClassSignature, 15);
1108 let kept = drop_redundant_signatures(&[whole, stub], 500);
1109 assert!(
1110 !kept.iter().any(|f| f.kind.is_signature()),
1111 "stub survived even though the full fragment fits the budget"
1112 );
1113 }
1114 #[test]
1121 fn tau_stops_the_greedy_loop_before_the_budget_is_spent() {
1122 let frags: Vec<Fragment> = (0..6)
1125 .map(|i| {
1126 frag(
1127 "a.rs",
1128 1 + i * 20,
1129 10 + i * 20,
1130 FragmentKind::Function,
1131 20 + i * i * 120,
1132 )
1133 })
1134 .collect();
1135 let core: FxHashSet<FragmentId> = std::iter::once(frags[0].id.clone()).collect();
1136 let mut rel = FxHashMap::default();
1137 for (i, f) in frags.iter().enumerate() {
1141 rel.insert(f.id.clone(), 0.25f64.powi(i as i32).max(1e-6));
1142 }
1143
1144 let budget = 10_000;
1145 let default_tau = lazy_greedy_select(
1146 frags.clone(),
1147 &core,
1148 &rel,
1149 &[],
1150 budget,
1151 crate::config::limits::DEFAULT_STOPPING_THRESHOLD,
1152 None,
1153 None,
1154 None,
1155 );
1156 let no_tau = lazy_greedy_select(frags, &core, &rel, &[], budget, 0.0, None, None, None);
1157
1158 assert_eq!(
1159 default_tau.reason,
1160 SelectionReason::StoppedByTau,
1161 "the adaptive stop did not fire at the shipped default"
1162 );
1163 assert!(
1164 default_tau.selected.len() < no_tau.selected.len(),
1165 "tau={} selected {} fragments, same as tau=0.0 — the rule is inert",
1166 crate::config::limits::DEFAULT_STOPPING_THRESHOLD,
1167 default_tau.selected.len()
1168 );
1169 assert!(
1170 default_tau.used_tokens < no_tau.used_tokens,
1171 "the stop saved no budget: {} vs {}",
1172 default_tau.used_tokens,
1173 no_tau.used_tokens
1174 );
1175 assert!(
1176 default_tau.stopping_certificate > 0.0,
1177 "StoppedByTau must carry a positive certificate"
1178 );
1179 assert_eq!(
1180 no_tau.stopping_certificate, 0.0,
1181 "tau=0.0 cannot produce a stopping certificate"
1182 );
1183 }
1184
1185 #[test]
1189 fn per_file_ceiling_blocks_monopoly_but_releases_leftovers() {
1190 let mut frags: Vec<Fragment> = (0..20)
1192 .map(|i| {
1193 frag(
1194 "blob.json",
1195 i * 10 + 1,
1196 i * 10 + 9,
1197 FragmentKind::Chunk,
1198 100,
1199 )
1200 })
1201 .collect();
1202 frags.push(frag("a.rs", 1, 9, FragmentKind::Function, 100));
1203 frags.push(frag("b.rs", 1, 9, FragmentKind::Function, 100));
1204 let core: FxHashSet<FragmentId> = FxHashSet::default();
1205 let mut rel: FxHashMap<FragmentId, f64> = FxHashMap::default();
1206 for f in &frags {
1208 let w = if f.id.path.as_ref() == "blob.json" {
1209 1.0
1210 } else {
1211 0.5
1212 };
1213 rel.insert(f.id.clone(), w);
1214 }
1215 let budget = 1_000u32; let result = lazy_greedy_select(
1217 frags.clone(),
1218 &core,
1219 &rel,
1220 &[],
1221 budget,
1222 0.0,
1223 None,
1224 None,
1225 None,
1226 );
1227 let by_file =
1228 |sel: &Vec<Fragment>, p: &str| sel.iter().filter(|f| f.id.path.as_ref() == p).count();
1229 assert!(
1230 by_file(&result.selected, "a.rs") == 1 && by_file(&result.selected, "b.rs") == 1,
1231 "small files must not be crowded out: {:?}",
1232 result
1233 .selected
1234 .iter()
1235 .map(|f| f.id.path.to_string())
1236 .collect::<Vec<_>>()
1237 );
1238 assert!(
1239 by_file(&result.selected, "blob.json") >= 5,
1240 "leftover budget must flow back to the blob once competitors are served"
1241 );
1242
1243 let lone: Vec<Fragment> = (0..20)
1245 .map(|i| {
1246 frag(
1247 "blob.json",
1248 i * 10 + 1,
1249 i * 10 + 9,
1250 FragmentKind::Chunk,
1251 100,
1252 )
1253 })
1254 .collect();
1255 let result = lazy_greedy_select(lone, &core, &rel, &[], budget, 0.0, None, None, None);
1256 assert!(
1257 result.selected.len() >= 9,
1258 "single-file selection stranded budget: {} fragments",
1259 result.selected.len()
1260 );
1261 }
1262}