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 core_budget = (budget_tokens as f64 * selection().core_budget_fraction) as u32;
214 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 let mut skipped: Vec<(FragmentId, &Fragment)> = Vec::new();
238 for frag in &sorted_core {
239 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 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
404fn 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 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 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 #[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 #[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 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 #[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 #[test]
950 fn a_winning_singleton_never_evicts_the_core_selection() {
951 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 #[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 #[test]
1033 fn tau_stops_the_greedy_loop_before_the_budget_is_spent() {
1034 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}