Skip to main content

ruranges_core/
complement_single.rs

1use rustc_hash::FxHashMap;
2
3use crate::{
4    ruranges_structs::{GroupType, PositionType},
5    sorts,
6};
7
8pub fn sweep_line_complement<G: GroupType, T: PositionType>(
9    chrs: &[G],
10    starts: &[T],
11    ends: &[T],
12    slack: T,
13    chrom_lens: &FxHashMap<G, T>,
14    include_first_interval: bool, // <-- new parameter
15) -> (Vec<G>, Vec<T>, Vec<T>, Vec<u32>) {
16    let mut out_chrs = Vec::with_capacity(chrs.len());
17    let mut out_starts = Vec::with_capacity(chrs.len());
18    let mut out_ends = Vec::with_capacity(chrs.len());
19    let mut out_idxs = Vec::with_capacity(chrs.len());
20
21    // Early return if no input
22    if chrs.is_empty() {
23        return (out_chrs, out_starts, out_ends, out_idxs);
24    }
25
26    // Build your events array, sorted by chr and pos
27    let events = sorts::build_sorted_events_single_collection(chrs, starts, ends, slack);
28
29    // Initialize
30    let mut current_chr = events[0].chr;
31    let mut active_count = 0_i64;
32    // Whether we start "in a hole" (i.e., complement) depends on `include_first_interval`
33    let mut in_complement = include_first_interval;
34    // Start the first hole at position 0 of the chromosome (only matters if `in_complement == true`)
35    let mut current_start = T::zero();
36    let mut current_index = 0_u32;
37
38    for e in events {
39        // If we hit a new chromosome
40        if e.chr != current_chr {
41            // If we ended the previous chromosome still in a hole,
42            // optionally close it out at the chromosome’s end
43            if let Some(chlen) = chrom_lens.get(&current_chr) {
44                if in_complement {
45                    out_chrs.push(current_chr);
46                    out_starts.push(current_start);
47                    out_ends.push(*chlen);
48                    out_idxs.push(current_index);
49                }
50            }
51
52            // Reset for new chromosome
53            current_chr = e.chr;
54            active_count = 0;
55            in_complement = include_first_interval;
56            current_start = T::zero();
57            current_index = e.idx;
58        }
59
60        // Process this event
61        if e.is_start {
62            // coverage X → X + 1
63            active_count += 1;
64            // If coverage was zero, we just ended a hole
65            if active_count == 1 && in_complement && current_start != e.pos {
66                // That hole ends at e.pos
67                out_chrs.push(current_chr);
68                out_starts.push(current_start);
69                out_ends.push(e.pos);
70                out_idxs.push(current_index);
71
72                // We're no longer in a hole
73                in_complement = false;
74            }
75        } else {
76            // coverage X → X - 1
77            active_count -= 1;
78            // If coverage has just dropped back to zero,
79            // we start a new hole here
80            if active_count == 0 {
81                in_complement = true;
82                current_start = e.pos;
83            }
84        }
85    }
86
87    // End of all events: if we finished in a hole and have chromosome lengths
88    if let Some(chlen) = chrom_lens.get(&current_chr) {
89        if in_complement {
90            out_chrs.push(current_chr);
91            out_starts.push(current_start);
92            out_ends.push(*chlen);
93            out_idxs.push(current_index);
94        }
95    }
96
97    (out_chrs, out_starts, out_ends, out_idxs)
98}