@@ -541,3 +541,115 @@ pub(crate) fn run(pepidx_path: &str, peptides: Vec<(String, String)>, k: usize,
541541 . collect ( ) ;
542542 unzip_records ( records)
543543}
544+
545+ // ── Counts-only path (aggregate; O(unique queries), no per-hit materialization) ──
546+
547+ pub ( crate ) type CountColumns = ( Vec < String > , Vec < String > , Vec < i64 > , Vec < u64 > ) ;
548+
549+ /// Tally accepted matches per mismatch level for one peptide, mirroring
550+ /// mismatch_match's dedup + validity walk exactly but without building the
551+ /// matched sequence, metadata, or any per-hit row.
552+ fn mismatch_count ( peptide : & str , k : usize , max_mismatches : usize , index : & PepIndex , counts : & mut [ u64 ] ) {
553+ let pep_bytes = peptide. as_bytes ( ) ;
554+ if pep_bytes. len ( ) < k { return ; }
555+
556+ let num_kmers = pep_bytes. len ( ) - k + 1 ;
557+ let peptide_len = pep_bytes. len ( ) ;
558+ let mut seen: HashSet < u64 > = HashSet :: new ( ) ;
559+
560+ if peptide_len % k == 0 {
561+ let mut idx = 0 ;
562+ while idx < num_kmers {
563+ let kmer = & pep_bytes[ idx..idx + k] ;
564+ if let Some ( positions) = index. lookup ( kmer) {
565+ for kmer_hit in positions {
566+ let start = ( kmer_hit as i64 - idx as i64 ) as u64 ;
567+ if seen. contains ( & start) { continue ; }
568+ let mismatches = check_left_neighbors ( pep_bytes, idx, kmer_hit, index, k, max_mismatches, 0 ) ;
569+ let mismatches = check_right_neighbors ( pep_bytes, idx, kmer_hit, index, k, max_mismatches, mismatches) ;
570+ if mismatches <= max_mismatches {
571+ let mut i = 0 ;
572+ let mut valid = true ;
573+ while i < peptide_len {
574+ if index. resolve ( start + i as u64 ) . is_none ( ) { valid = false ; break ; }
575+ i += k;
576+ }
577+ if valid {
578+ seen. insert ( start) ;
579+ counts[ mismatches] += 1 ;
580+ }
581+ }
582+ }
583+ }
584+ idx += k;
585+ }
586+ } else {
587+ for idx in 0 ..num_kmers {
588+ let kmer = & pep_bytes[ idx..idx + k] ;
589+ if let Some ( positions) = index. lookup ( kmer) {
590+ for kmer_hit in positions {
591+ let start = ( kmer_hit as i64 - idx as i64 ) as u64 ;
592+ if seen. contains ( & start) { continue ; }
593+ let mismatches = check_left_residues ( pep_bytes, idx, kmer_hit, index, max_mismatches, 0 ) ;
594+ let mismatches = check_right_residues ( pep_bytes, idx, kmer_hit, index, k, max_mismatches, mismatches) ;
595+ if mismatches <= max_mismatches {
596+ let mut i = 0 ;
597+ let mut valid = true ;
598+ while i < peptide_len {
599+ if i + k > peptide_len {
600+ let remaining = peptide_len - i;
601+ let back = k - remaining;
602+ if index. resolve ( start + i as u64 - back as u64 ) . is_none ( ) { valid = false ; break ; }
603+ } else if index. resolve ( start + i as u64 ) . is_none ( ) {
604+ valid = false ; break ;
605+ }
606+ i += k;
607+ }
608+ if valid {
609+ seen. insert ( start) ;
610+ counts[ mismatches] += 1 ;
611+ }
612+ }
613+ }
614+ }
615+ }
616+ }
617+ }
618+
619+ fn count_peptide ( query_id : & str , peptide : & str , k : usize , max_mismatches : usize , index : & PepIndex ) -> Vec < ( String , String , i64 , u64 ) > {
620+ let mut counts = vec ! [ 0u64 ; max_mismatches + 1 ] ;
621+ if max_mismatches == 0 {
622+ counts[ 0 ] = exact_match ( peptide, k, index) . len ( ) as u64 ;
623+ } else {
624+ mismatch_count ( peptide, k, max_mismatches, index, & mut counts) ;
625+ }
626+ let mut out = Vec :: new ( ) ;
627+ for ( mm, & c) in counts. iter ( ) . enumerate ( ) {
628+ if c > 0 {
629+ out. push ( ( query_id. to_string ( ) , peptide. to_string ( ) , mm as i64 , c) ) ;
630+ }
631+ }
632+ out
633+ }
634+
635+ pub ( crate ) fn run_counts ( pepidx_path : & str , peptides : Vec < ( String , String ) > , k : usize , max_mismatches : usize ) -> CountColumns {
636+ let index = PepIndex :: open ( pepidx_path) ;
637+ let rows: Vec < ( String , String , i64 , u64 ) > = peptides
638+ . par_iter ( )
639+ . flat_map_iter ( |( query_id, peptide) | {
640+ count_peptide ( query_id, peptide, k, max_mismatches, & index) . into_iter ( )
641+ } )
642+ . collect ( ) ;
643+ let n = rows. len ( ) ;
644+ let mut qid = Vec :: with_capacity ( n) ;
645+ let mut qseq = Vec :: with_capacity ( n) ;
646+ let mut mm = Vec :: with_capacity ( n) ;
647+ let mut cnt = Vec :: with_capacity ( n) ;
648+ for ( a, b, c, d) in rows {
649+ qid. push ( a) ;
650+ qseq. push ( b) ;
651+ mm. push ( c) ;
652+ cnt. push ( d) ;
653+ }
654+ ( qid, qseq, mm, cnt)
655+ }
0 commit comments