skydive/
record.rs

1use std::fmt;
2
3use crate::edges::Edges;
4
5/// Represents a de Bruijn graph record.
6#[derive(Debug, Clone, PartialEq, Eq)]
7pub struct Record {
8    coverage: u16,
9    cov_fw: u16,
10    cov_rc: u16,
11    edges: Edges,
12}
13
14impl Record {
15    /// Create an empty de Bruijn graph record.
16    #[must_use]
17    pub fn new(coverage: u16, edges: Option<Edges>) -> Self {
18        Record {
19            coverage,
20            cov_fw: 0,
21            cov_rc: 0,
22            edges: edges.unwrap_or(Edges::empty()),
23        }
24    }
25
26    // /// Create a default de Bruijn graph record with 0 coverage and no edges.
27    // pub fn default() -> Self {
28    //     Record {
29    //         coverage: 0,
30    //         edges: Edges::empty(),
31    //     }
32    // }
33
34    /// Return the Record's coverage.
35    #[must_use]
36    pub fn coverage(&self) -> u16 {
37        self.coverage
38    }
39
40    /// Return the Record's forward coverage.
41    #[must_use]
42    pub fn fw_coverage(&self) -> u16 {
43        self.cov_fw
44    }
45
46    /// Return the Record's reverse complement coverage.
47    #[must_use]
48    pub fn rc_coverage(&self) -> u16 {
49        self.cov_rc
50    }
51
52    // Return all edges.
53    #[must_use]
54    pub fn edges(&self) -> Edges {
55        self.edges
56    }
57
58    /// Return incoming edges
59    #[must_use]
60    pub fn incoming_edges(&self) -> Vec<u8> {
61        let mut edges = Vec::new();
62
63        if self.edges.contains(Edges::FLAG_EDGE_IN_A) {
64            edges.push(b'A');
65        }
66        if self.edges.contains(Edges::FLAG_EDGE_IN_C) {
67            edges.push(b'C');
68        }
69        if self.edges.contains(Edges::FLAG_EDGE_IN_G) {
70            edges.push(b'G');
71        }
72        if self.edges.contains(Edges::FLAG_EDGE_IN_T) {
73            edges.push(b'T');
74        }
75
76        edges
77    }
78
79    /// Return outgoing edges
80    #[must_use]
81    pub fn outgoing_edges(&self) -> Vec<u8> {
82        let mut edges = Vec::new();
83
84        if self.edges.contains(Edges::FLAG_EDGE_OUT_A) {
85            edges.push(b'A');
86        }
87        if self.edges.contains(Edges::FLAG_EDGE_OUT_C) {
88            edges.push(b'C');
89        }
90        if self.edges.contains(Edges::FLAG_EDGE_OUT_G) {
91            edges.push(b'G');
92        }
93        if self.edges.contains(Edges::FLAG_EDGE_OUT_T) {
94            edges.push(b'T');
95        }
96
97        edges
98    }
99
100    /// Increment the coverage value by 1.
101    pub fn increment_coverage(&mut self) {
102        self.coverage = self.coverage.saturating_add(1);
103    }
104
105    /// Increment the forward coverage value by 1.
106    pub fn increment_fw_coverage(&mut self) {
107        self.cov_fw = self.cov_fw.saturating_add(1);
108    }
109
110    /// Increment the reverse complement coverage value by 1.
111    pub fn increment_rc_coverage(&mut self) {
112        self.cov_rc = self.cov_rc.saturating_add(1);
113    }
114
115    /// Set the coverage value.
116    pub fn set_coverage(&mut self, coverage: u16) {
117        self.coverage = coverage;
118    }
119
120    /// Set incoming edge.
121    pub fn set_incoming_edge(&mut self, nucleotide: u8) {
122        match nucleotide {
123            b'A' => self.edges.insert(Edges::FLAG_EDGE_IN_A),
124            b'C' => self.edges.insert(Edges::FLAG_EDGE_IN_C),
125            b'G' => self.edges.insert(Edges::FLAG_EDGE_IN_G),
126            b'T' => self.edges.insert(Edges::FLAG_EDGE_IN_T),
127            _ => (),
128        }
129    }
130
131    /// Set outgoing edge.
132    pub fn set_outgoing_edge(&mut self, nucleotide: u8) {
133        match nucleotide {
134            b'A' => self.edges.insert(Edges::FLAG_EDGE_OUT_A),
135            b'C' => self.edges.insert(Edges::FLAG_EDGE_OUT_C),
136            b'G' => self.edges.insert(Edges::FLAG_EDGE_OUT_G),
137            b'T' => self.edges.insert(Edges::FLAG_EDGE_OUT_T),
138            _ => (),
139        }
140    }
141
142    /// The in-degree of a particular k-mer.
143    #[must_use]
144    pub fn in_degree(&self) -> u8 {
145        let mut degree: u8 = 0;
146        degree += u8::from(self.edges.contains(Edges::FLAG_EDGE_IN_A));
147        degree += u8::from(self.edges.contains(Edges::FLAG_EDGE_IN_C));
148        degree += u8::from(self.edges.contains(Edges::FLAG_EDGE_IN_G));
149        degree += u8::from(self.edges.contains(Edges::FLAG_EDGE_IN_T));
150        degree
151    }
152
153    /// The out-degree of a particular k-mer.
154    #[must_use]
155    pub fn out_degree(&self) -> u8 {
156        let mut degree: u8 = 0;
157        degree += u8::from(self.edges.contains(Edges::FLAG_EDGE_OUT_A));
158        degree += u8::from(self.edges.contains(Edges::FLAG_EDGE_OUT_C));
159        degree += u8::from(self.edges.contains(Edges::FLAG_EDGE_OUT_G));
160        degree += u8::from(self.edges.contains(Edges::FLAG_EDGE_OUT_T));
161        degree
162    }
163
164    /// Identifies junctions in the graph
165    #[must_use]
166    pub fn is_junction(&self) -> bool {
167        self.in_degree() > 1 || self.out_degree() > 1
168    }
169}
170
171impl fmt::Display for Record {
172    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
173        write!(
174            f,
175            "{} {}{}{}{}{}{}{}{}",
176            self.coverage,
177            if self.edges.contains(Edges::FLAG_EDGE_IN_A) {
178                'a'
179            } else {
180                '.'
181            },
182            if self.edges.contains(Edges::FLAG_EDGE_IN_C) {
183                'c'
184            } else {
185                '.'
186            },
187            if self.edges.contains(Edges::FLAG_EDGE_IN_G) {
188                'g'
189            } else {
190                '.'
191            },
192            if self.edges.contains(Edges::FLAG_EDGE_IN_T) {
193                't'
194            } else {
195                '.'
196            },
197            if self.edges.contains(Edges::FLAG_EDGE_OUT_A) {
198                'A'
199            } else {
200                '.'
201            },
202            if self.edges.contains(Edges::FLAG_EDGE_OUT_C) {
203                'C'
204            } else {
205                '.'
206            },
207            if self.edges.contains(Edges::FLAG_EDGE_OUT_G) {
208                'G'
209            } else {
210                '.'
211            },
212            if self.edges.contains(Edges::FLAG_EDGE_OUT_T) {
213                'T'
214            } else {
215                '.'
216            },
217        )
218    }
219}
220
221#[cfg(test)]
222mod tests {
223    use super::*;
224    // use proptest::prelude::*;
225
226    #[test]
227    fn test_get_coverage() {
228        let r1 = Record::new(1, None);
229        let r10 = Record::new(10, None);
230
231        assert!(r1.coverage() == 1);
232        assert!(r10.coverage() == 10);
233    }
234
235    #[test]
236    fn test_set_coverage() {
237        let mut r100 = Record::new(0, None);
238        r100.set_coverage(100);
239
240        let mut r1000 = Record::new(0, None);
241        r1000.set_coverage(1000);
242
243        assert!(r100.coverage() == 100);
244        assert!(r1000.coverage() == 1000);
245    }
246
247    #[test]
248    fn test_increment_coverage() {
249        let mut r100 = Record::new(99, None);
250        r100.increment_coverage();
251
252        let mut r1000 = Record::new(999, None);
253        r1000.increment_coverage();
254
255        assert!(r100.coverage() == 100);
256        assert!(r1000.coverage() == 1000);
257    }
258
259    #[test]
260    fn test_increment_coverage_saturates() {
261        let mut rmax = Record::new(u16::MAX, None);
262        rmax.increment_coverage();
263
264        assert!(rmax.coverage() == u16::MAX);
265    }
266
267    #[test]
268    fn test_set_incoming_edge() {
269        let mut r = Record::new(1, None);
270
271        assert!(r.edges.is_empty());
272
273        r.set_incoming_edge('A' as u8);
274        assert!(r.edges.contains(Edges::FLAG_EDGE_IN_A));
275        assert!(!r.edges.contains(Edges::FLAG_EDGE_IN_C));
276        assert!(!r.edges.contains(Edges::FLAG_EDGE_IN_G));
277        assert!(!r.edges.contains(Edges::FLAG_EDGE_IN_T));
278
279        r.set_incoming_edge('C' as u8);
280        assert!(r.edges.contains(Edges::FLAG_EDGE_IN_A));
281        assert!(r.edges.contains(Edges::FLAG_EDGE_IN_C));
282        assert!(!r.edges.contains(Edges::FLAG_EDGE_IN_G));
283        assert!(!r.edges.contains(Edges::FLAG_EDGE_IN_T));
284
285        r.set_incoming_edge('G' as u8);
286        assert!(r.edges.contains(Edges::FLAG_EDGE_IN_A));
287        assert!(r.edges.contains(Edges::FLAG_EDGE_IN_C));
288        assert!(r.edges.contains(Edges::FLAG_EDGE_IN_G));
289        assert!(!r.edges.contains(Edges::FLAG_EDGE_IN_T));
290
291        r.set_incoming_edge('T' as u8);
292        assert!(r.edges.contains(Edges::FLAG_EDGE_IN_A));
293        assert!(r.edges.contains(Edges::FLAG_EDGE_IN_C));
294        assert!(r.edges.contains(Edges::FLAG_EDGE_IN_G));
295        assert!(r.edges.contains(Edges::FLAG_EDGE_IN_T));
296
297        assert!(!r.edges.contains(Edges::FLAG_EDGE_OUT_A));
298        assert!(!r.edges.contains(Edges::FLAG_EDGE_OUT_C));
299        assert!(!r.edges.contains(Edges::FLAG_EDGE_OUT_G));
300        assert!(!r.edges.contains(Edges::FLAG_EDGE_OUT_T));
301    }
302
303    #[test]
304    fn test_set_outgoing_edge() {
305        let mut r = Record::new(1, None);
306
307        assert!(r.edges.is_empty());
308
309        r.set_outgoing_edge('A' as u8);
310        assert!(r.edges.contains(Edges::FLAG_EDGE_OUT_A));
311        assert!(!r.edges.contains(Edges::FLAG_EDGE_OUT_C));
312        assert!(!r.edges.contains(Edges::FLAG_EDGE_OUT_G));
313        assert!(!r.edges.contains(Edges::FLAG_EDGE_OUT_T));
314
315        r.set_outgoing_edge('C' as u8);
316        assert!(r.edges.contains(Edges::FLAG_EDGE_OUT_A));
317        assert!(r.edges.contains(Edges::FLAG_EDGE_OUT_C));
318        assert!(!r.edges.contains(Edges::FLAG_EDGE_OUT_G));
319        assert!(!r.edges.contains(Edges::FLAG_EDGE_OUT_T));
320
321        r.set_outgoing_edge('G' as u8);
322        assert!(r.edges.contains(Edges::FLAG_EDGE_OUT_A));
323        assert!(r.edges.contains(Edges::FLAG_EDGE_OUT_C));
324        assert!(r.edges.contains(Edges::FLAG_EDGE_OUT_G));
325        assert!(!r.edges.contains(Edges::FLAG_EDGE_OUT_T));
326
327        r.set_outgoing_edge('T' as u8);
328        assert!(r.edges.contains(Edges::FLAG_EDGE_OUT_A));
329        assert!(r.edges.contains(Edges::FLAG_EDGE_OUT_C));
330        assert!(r.edges.contains(Edges::FLAG_EDGE_OUT_G));
331        assert!(r.edges.contains(Edges::FLAG_EDGE_OUT_T));
332
333        assert!(!r.edges.contains(Edges::FLAG_EDGE_IN_A));
334        assert!(!r.edges.contains(Edges::FLAG_EDGE_IN_C));
335        assert!(!r.edges.contains(Edges::FLAG_EDGE_IN_G));
336        assert!(!r.edges.contains(Edges::FLAG_EDGE_IN_T));
337    }
338
339    #[test]
340    fn test_in_degree() {
341        let mut r = Record::new(1, None);
342
343        assert!(r.in_degree() == 0);
344
345        r.set_incoming_edge('A' as u8);
346        assert!(r.in_degree() == 1);
347
348        r.set_incoming_edge('C' as u8);
349        assert!(r.in_degree() == 2);
350
351        r.set_incoming_edge('G' as u8);
352        assert!(r.in_degree() == 3);
353
354        r.set_incoming_edge('T' as u8);
355        assert!(r.in_degree() == 4);
356
357        r.set_outgoing_edge('A' as u8);
358        assert!(r.in_degree() == 4);
359    }
360
361    #[test]
362    fn test_out_degree() {
363        let mut r = Record::new(1, None);
364
365        assert!(r.out_degree() == 0);
366
367        r.set_outgoing_edge('A' as u8);
368        assert!(r.out_degree() == 1);
369
370        r.set_outgoing_edge('C' as u8);
371        assert!(r.out_degree() == 2);
372
373        r.set_outgoing_edge('G' as u8);
374        assert!(r.out_degree() == 3);
375
376        r.set_outgoing_edge('T' as u8);
377        assert!(r.out_degree() == 4);
378
379        r.set_outgoing_edge('A' as u8);
380        assert!(r.out_degree() == 4);
381    }
382
383    #[test]
384    fn test_incoming_junction() {
385        let mut r = Record::new(1, None);
386
387        assert!(r.is_junction() == false);
388
389        r.set_incoming_edge('A' as u8);
390        assert!(r.is_junction() == false);
391
392        r.set_incoming_edge('C' as u8);
393        assert!(r.is_junction() == true);
394    }
395
396    #[test]
397    fn test_outgoing_junction() {
398        let mut r = Record::new(1, None);
399
400        assert!(r.is_junction() == false);
401
402        r.set_outgoing_edge('A' as u8);
403        assert!(r.is_junction() == false);
404
405        r.set_outgoing_edge('C' as u8);
406        assert!(r.is_junction() == true);
407    }
408}