1
use std::collections::HashMap;
2
use std::path::PathBuf;
3

            
4
use color_eyre::Result;
5
use rand::{seq::IteratorRandom, thread_rng};
6
use rayon::prelude::*;
7
use rust_htslib::bcf::Read;
8

            
9
use crate::args::MergeRule;
10
use crate::read_vcf::{get_reader, get_samples};
11
use crate::subcommands::rwc;
12
use crate::{io::read_sample_ids, utils::filter_samples};
13
use crate::core::{read_tabix, open_csv_writer};
14

            
15
// Contig, start pos, stop pos, marker count, length in bp
16
type Shared = Vec<(String, i64, i64, usize, i64)>;
17

            
18
type Location = (String, i64);
19
type Data<'a> = (i64, usize, i64, Vec<&'a String>);
20

            
21
#[doc(hidden)]
22
12
#[tracing::instrument]
23
6
#[allow(clippy::too_many_arguments)]
24
pub fn run(
25
    file: PathBuf,
26
    samples_path: Option<PathBuf>,
27
    mut output: PathBuf,
28
    info_limit: Option<f32>,
29
    sample_size: usize,
30
    niters: i64,
31
    sort: bool,
32
    min_marker_len: usize,
33
    top_markers_n: usize,
34
    merge_rule: MergeRule,
35
    prefix: Option<String>,
36
) -> Result<()> {
37
    let tabix = read_tabix(&file)?;
38
    let contigs: Vec<String> = tabix.keys().cloned().collect();
39

            
40
    if let Some(prefix) = &prefix {
41
        match merge_rule {
42
            MergeRule::Start => output.push(format!("{prefix}_random_sampled_rwc_start.csv")),
43
            MergeRule::Stop => output.push(format!("{prefix}_random_sampled_rwc_stop.csv")),
44
        };
45
    } else {
46
        match merge_rule {
47
            MergeRule::Start => output.push("random_sampled_rwc_start.csv"),
48
            MergeRule::Stop => output.push("random_sampled_rwc_stop.csv"),
49
        };
50
    }
51

            
52
    let mut writer = open_csv_writer(output)?;
53

            
54
    let shared = (0..niters)
55
        .par_bridge()
56
600
        .map(|_| {
57
600
            contigs
58
600
                .par_iter()
59
600
                .map(|contig| {
60
                    // Fetch readers
61
600
                    let mut reader = get_reader(&file, contig, None)?;
62

            
63
600
                    let samples = get_samples(reader.header())?;
64
600
                    let wanted = read_sample_ids(&samples_path)?;
65
600
                    let sample_indexes = filter_samples(&samples, wanted);
66
600

            
67
600
                    // Randomly subsample sample_indexes with size n
68
600
                    let mut rng = thread_rng();
69
600
                    let sample_indexes = sample_indexes
70
600
                        .iter()
71
600
                        .copied()
72
600
                        .choose_multiple(&mut rng, sample_size);
73
600

            
74
600
                    let records = reader.records();
75

            
76
                    // Program logic
77
600
                    let (merged_gt_vec, pos_vec) =
78
600
                        rwc::read_vcf_to_rwc_vec(records, &sample_indexes, info_limit)?;
79

            
80
600
                    let vec = find_shared_sequence_lengths(
81
600
                        contig,
82
600
                        merged_gt_vec,
83
600
                        pos_vec,
84
600
                        min_marker_len,
85
600
                        top_markers_n,
86
600
                    )?;
87
600
                    Ok((vec, sample_indexes))
88
600
                })
89
600
                .collect::<Result<Vec<(Shared, Vec<usize>)>>>()
90
600
        })
91
        .collect::<Result<Vec<Vec<(Shared, Vec<usize>)>>>>()?;
92

            
93
    let reader = crate::read_vcf::get_reader(&file, &contigs[0], None)?;
94
    let samples = crate::read_vcf::get_samples(reader.header())?;
95

            
96
    let hm = merge_runs(shared, &samples, &merge_rule);
97

            
98
    write_matches_to_csv(hm, &mut writer, sort)?;
99
    Ok(())
100
}
101

            
102
6
fn merge_runs<'a>(
103
6
    shared: Vec<Vec<(Shared, Vec<usize>)>>,
104
6
    samples: &'a [String],
105
6
    merge_rule: &MergeRule,
106
6
) -> HashMap<Location, Data<'a>> {
107
6
    let mut hm = HashMap::<Location, Data>::new();
108
606
    for iteration in shared {
109
1200
        for (shared, indexes) in iteration {
110
2400
            let shared_samples: Vec<&String> = indexes.iter().map(|s| &samples[*s]).collect();
111
1200
            for (contig, start, stop, snp_count, bp_length) in shared {
112
600
                let merge_value = match merge_rule {
113
600
                    MergeRule::Start => start,
114
                    MergeRule::Stop => stop,
115
                };
116

            
117
600
                let (count, snp_count_avg, bp_length_avg, samples) = hm
118
600
                    .entry((contig, merge_value))
119
600
                    .or_insert((0, snp_count, bp_length, shared_samples.clone()));
120
600
                *count += 1;
121
600
                *snp_count_avg = (*snp_count_avg + snp_count) / 2;
122
600
                *bp_length_avg = (*bp_length_avg + bp_length) / 2;
123
600
                samples.extend(shared_samples.clone());
124
600
                samples.sort();
125
600
                samples.dedup();
126
            }
127
        }
128
    }
129
6
    hm
130
6
}
131

            
132
6
fn write_matches_to_csv(
133
6
    hm: HashMap<Location, Data>,
134
6
    writer: &mut csv::Writer<Box<dyn std::io::Write>>,
135
6
    sort: bool,
136
6
) -> Result<()> {
137
6
    let hm = match sort {
138
        true => {
139
6
            let mut data: Vec<_> = hm.into_iter().collect();
140
6
            data.sort_by(|(a, _), (b, _)| b.1.cmp(&a.1));
141
6
            data
142
        }
143
        false => hm.into_iter().collect(),
144
    };
145

            
146
6
    writer.write_record(vec![
147
6
        "chr",
148
6
        "pos",
149
6
        "count",
150
6
        "snp_count_avg",
151
6
        "bp_length_avg",
152
6
        "samples",
153
6
    ])?;
154

            
155
12
    for ((contig, start), (count, snp_count_avg, bp_length_avg, samples)) in hm {
156
6
        writer.write_record(vec![
157
6
            contig,
158
6
            start.to_string(),
159
6
            count.to_string(),
160
6
            snp_count_avg.to_string(),
161
6
            bp_length_avg.to_string(),
162
6
            samples
163
6
                .into_iter()
164
6
                .cloned()
165
6
                .collect::<Vec<String>>()
166
6
                .join("&"),
167
6
        ])?;
168
    }
169
6
    Ok(())
170
6
}
171

            
172
// Every iteration looks for ROH of over 20 markers and takes the top 10 hits
173
#[doc(hidden)]
174
600
pub fn find_shared_sequence_lengths(
175
600
    contig: &str,
176
600
    merged_gt_vec: Vec<u8>,
177
600
    pos_vec: Vec<i64>,
178
600
    min_marker_len: usize,
179
600
    top_markers_n: usize,
180
600
) -> Result<Shared> {
181
600
    let mut counter = 1;
182
600

            
183
600
    let mut shared_sequences = vec![];
184
600
    merged_gt_vec
185
600
        .iter()
186
600
        .enumerate()
187
600
        .fold(1, |last, (index, curr)| {
188
            // The last and current element are 0, no contrahomozygotes are present
189
37800
            if last == 0 && *curr == 0 {
190
37176
                counter += 1;
191
37176
            // If the last element is 0 and the current is 1, a shared sequence has ended
192
37176
            } else if last == 0 && *curr == 1 {
193
                if counter >= min_marker_len {
194
                    let start = &pos_vec[index - counter];
195
                    let stop = &pos_vec[index];
196
                    shared_sequences.push((
197
                        contig.to_string(),
198
                        *start,
199
                        *stop,
200
                        counter,
201
                        stop - start,
202
                    ));
203
                }
204
                counter = 1;
205
600
            }
206

            
207
37776
            *curr
208
37776
        });
209
600

            
210
600
    counter -= 1;
211
600
    if counter >= min_marker_len {
212
600
        let start = &pos_vec[merged_gt_vec.len() - 1 - counter];
213
600
        let stop = &pos_vec[merged_gt_vec.len() - 1];
214
600
        shared_sequences.push((
215
600
            contig.to_string(),
216
600
            *start,
217
600
            *stop,
218
600
            counter,
219
600
            stop - start,
220
600
        ));
221
600
    }
222

            
223
600
    shared_sequences.sort_by(|a, b| b.3.cmp(&a.3));
224
600

            
225
600
    Ok(shared_sequences.into_iter().take(top_markers_n).collect())
226
600
}