• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

jlab / rust-debruijn / 14644057144

24 Apr 2025 02:22PM UTC coverage: 85.013% (+0.2%) from 84.785%
14644057144

Pull #16

github

web-flow
Merge 6a082e950 into 8f229478f
Pull Request #16: Fix/unreachable kmers

125 of 133 new or added lines in 3 files covered. (93.98%)

2 existing lines in 1 file now uncovered.

6722 of 7907 relevant lines covered (85.01%)

3446407.62 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

92.59
/src/reads.rs
1
use std::collections::HashMap;
2
use std::mem::take;
3
use std::ops::Range;
4
use itertools::Itertools;
5
use serde_derive::{Deserialize, Serialize};
6
use std::fmt::{Debug, Display};
7
use std::hash::Hash;
8
use std::{mem, str};
9
use crate::dna_string::DnaString;
10
use crate::{base_to_bits, base_to_bits_checked, Exts, Vmer};
11

12
#[derive(Debug, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord, Clone, Hash, Copy)]
13
pub enum Strandedness {
14
    Forward,
15
    Reverse,
16
    Unstranded
17
}
18

19
/// Store many DNA sequences together with an Exts and data each compactly packed together
20
/// 
21
/// #### fields:
22
/// 
23
/// * `storage`: `Vec` with 2-bit encoded DNA bases of all sequences
24
/// * `ends`:  `Vec` with the ends (exclusive) of the separate sequences in the `Reads`
25
/// * `exts`: `Vec` with one Exts for each sequence
26
/// * `data`: `Vec` with data for each sequence
27
/// * `len`: length of all sequences together
28
/// * `stranded`: [`Stranded`] conveying the strandedness and direction of the reads
29
#[derive(Ord, PartialOrd, Clone, PartialEq, Eq, Hash, Serialize, Deserialize, Debug)]
30
pub struct Reads<D> {
31
    storage: Vec<u64>,
32
    ends: Vec<usize>,
33
    exts: Vec<Exts>,
34
    data: Vec<D>,
35
    len: usize,
36
    stranded: Strandedness
37
}
38

39
impl<D: Clone + Copy> Reads<D> {
40

41
    /// Returns a new `Reads`
42
    pub fn new(stranded: Strandedness) -> Self {
1,433✔
43
        Reads {
1,433✔
44
            storage: Vec::new(),
1,433✔
45
            ends: Vec::new(),
1,433✔
46
            exts: Vec::new(),
1,433✔
47
            data: Vec::new(),
1,433✔
48
            len: 0,
1,433✔
49
            stranded
1,433✔
50
        }
1,433✔
51
    }
1,433✔
52

53
    #[inline(always)]
54
    pub fn stranded(&self) -> Strandedness {
1✔
55
        self.stranded
1✔
56
    }
1✔
57

58
    #[inline(always)]
59
    /// Returns the number of reads stored
60
    pub fn n_reads(&self) -> usize {
800,630✔
61
        self.ends.len()
800,630✔
62
    }
800,630✔
63

64
    pub fn mem(&self) -> usize {
7✔
65
        mem::size_of_val(self) + size_of_val(&*self.storage) + size_of_val(&*self.data) + size_of_val(&*self.ends) + size_of_val(&*self.exts)
7✔
66
    }
7✔
67

68
    /// Adds a new read to the `Reads`
69
    // maybe push_base until u64 is full and then do extend like in DnaString::extend ? with accellerated mode
70
    pub fn add_read<V: Vmer>(&mut self, read: V, exts: Exts, data: D) {
5,500,039✔
71
        for base in read.iter() {
201,002,419✔
72
            self.push_base(base);
201,002,419✔
73
        }
201,002,419✔
74
        self.ends.push(self.len);
5,500,039✔
75
        self.data.push(data);
5,500,039✔
76
        self.exts.push(exts);
5,500,039✔
77
       
5,500,039✔
78
    }
5,500,039✔
79

80
    /// Transforms a `[(vmer, exts, data)]` into a `Reads` - watch for memory usage
81
    // TODO test if memory efficient
82
    pub fn from_vmer_vec<V: Vmer, S: IntoIterator<Item=(V, Exts, D)>>(vec_iter: S, stranded: Strandedness) -> Self {
1,405✔
83
        let mut reads = Reads::new(stranded);
1,405✔
84
        for (vmer, exts, data) in vec_iter {
188,510✔
85
            for base in vmer.iter() {
14,568,953✔
86
                reads.push_base(base);
14,568,953✔
87
            }
14,568,953✔
88
            reads.ends.push(reads.len);
187,105✔
89
            reads.data.push(data);
187,105✔
90
            reads.exts.push(exts);
187,105✔
91
        }
92

93
        reads.storage.shrink_to_fit();
1,405✔
94
        reads.data.shrink_to_fit();
1,405✔
95
        reads.exts.shrink_to_fit();
1,405✔
96
        reads.ends.shrink_to_fit();
1,405✔
97
        
1,405✔
98
        reads
1,405✔
99
    }
1,405✔
100

101

102
    /// add ASCII encoded bases to the Reads
103
    /// 
104
    /// will transform all ascii characters outside of ACGTacgt into A
105
    /// see also: [`Reads::add_from_bytes_checked`]
106
    pub fn add_from_bytes(&mut self, bytes: &[u8], exts: Exts, data: D) {
5,510,031✔
107
        
5,510,031✔
108
        // fill the last incomplete u64 block
5,510,031✔
109
        let missing = 32 - (self.len % 32);
5,510,031✔
110
        if missing != 0 {
5,510,031✔
111
            if  missing > bytes.len() {
5,510,031✔
112
                let fill = bytes.iter().map(|c| base_to_bits(*c));
13,156,319✔
113
                self.extend(fill);
1,406,253✔
114
                self.exts.push(exts);
1,406,253✔
115
                self.data.push(data);
1,406,253✔
116
                self.ends.push(self.len);
1,406,253✔
117
                return;
1,406,253✔
118
            } else {
4,103,778✔
119
                let fill = bytes[0..missing].iter().map(|c| base_to_bits(*c));
62,264,224✔
120
                self.extend(fill);
4,103,778✔
121
            }
4,103,778✔
122
        }
×
123
        
124
        // Accelerated avx2 mode. Should run on most machines made since 2013.
125
        #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
126
        {
127
            if is_x86_feature_detected!("avx2") {
4,103,778✔
128
                for chunk in bytes[missing..bytes.len()].chunks(32) {
6,077,533✔
129
                    if chunk.len() == 32 {
6,077,533✔
130
                        let (conv_chunk, _) = unsafe { crate::bitops_avx2::convert_bases(chunk) };
2,224,381✔
131
                        let packed = unsafe { crate::bitops_avx2::pack_32_bases(conv_chunk) };
2,224,381✔
132
                        self.storage.push(packed);
2,224,381✔
133
                        self.len += 32;
2,224,381✔
134
                    } else {
3,853,152✔
135
                        let b = chunk.iter().map(|c| base_to_bits(*c));
55,900,483✔
136
                        self.extend(b);
3,853,152✔
137
                    }
3,853,152✔
138
                }
139

140
                self.exts.push(exts);
4,103,778✔
141
                self.data.push(data);
4,103,778✔
142
                self.ends.push(self.len);
4,103,778✔
143
                return;
4,103,778✔
144
            }
×
145
        }
×
146

×
147
        let b = bytes.iter().map(|c| base_to_bits(*c));
×
148
        self.extend(b);
×
149

×
150
        self.exts.push(exts);
×
151
        self.data.push(data);
×
152
        self.ends.push(self.len);
×
153
        
154
    }
5,510,031✔
155

156
    /// add ASCII encoded bases to the Reads
157
    /// 
158
    /// will return `false` if the bytes contained characters outside of `ACGTacgt`, otherwise return true and add the bases
159
    /// see also: [`Reads::add_from_bytes`]
160
    pub fn add_from_bytes_checked(&mut self, bytes: &[u8], exts: Exts, data: D) -> bool {
11✔
161

11✔
162
        let (_, corrects): (Vec<u8>, Vec<bool>) = bytes.iter().map(|c| base_to_bits_checked(*c)).collect();
402✔
163
        if corrects.iter().contains(&false) { return false }
11✔
164

6✔
165
        
6✔
166
        // fill the last incomplete u64 block
6✔
167
        let missing = 32 - (self.len % 32);
6✔
168
        if missing != 0 {
6✔
169
            if  missing > bytes.len() {
6✔
170
                let fill = bytes.iter().map(|c| base_to_bits(*c));
31✔
171
                self.extend(fill);
2✔
172
                self.exts.push(exts);
2✔
173
                self.data.push(data);
2✔
174
                self.ends.push(self.len);
2✔
175
                return true;
2✔
176
            } else {
4✔
177
                let fill = bytes[0..missing].iter().map(|c| base_to_bits(*c));
61✔
178
                self.extend(fill);
4✔
179
            }
4✔
180
        }
×
181
        
182
        // Accelerated avx2 mode. Should run on most machines made since 2013.
183
        #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
184
        {
185
            if is_x86_feature_detected!("avx2") {
4✔
186
                for chunk in bytes[missing..bytes.len()].chunks(32) {
5✔
187
                    if chunk.len() == 32 {
5✔
188
                        let (conv_chunk, _) = unsafe { crate::bitops_avx2::convert_bases(chunk) };
2✔
189
                        let packed = unsafe { crate::bitops_avx2::pack_32_bases(conv_chunk) };
2✔
190
                        self.storage.push(packed);
2✔
191
                        self.len += 32;
2✔
192
                    } else {
3✔
193
                        let b = chunk.iter().map(|c| base_to_bits(*c));
37✔
194
                        self.extend(b);
3✔
195
                    }
3✔
196
                }
197

198
                self.exts.push(exts);
4✔
199
                self.data.push(data);
4✔
200
                self.ends.push(self.len);
4✔
201
                return true;
4✔
202
            }
×
203
        }
×
204

×
205
        let b = bytes.iter().map(|c| base_to_bits(*c));
×
206
        self.extend(b);
×
207

×
208
        self.exts.push(exts);
×
209
        self.data.push(data);
×
210
        self.ends.push(self.len);
×
211

×
212
        true         
×
213
    }
11✔
214

215

216
    /// Add new 2-bit encoded base to the `Reads`
217
    fn push_base(&mut self, base: u8) {
284,909,237✔
218
        let bit = (self.len % 32) * 2;
284,909,237✔
219
        if bit != 0 {
284,909,237✔
220
            match self.storage.pop() {
278,172,094✔
221
                Some(last) => {
278,172,094✔
222
                    let last = last + ((base as u64) << (64 - bit - 2));
278,172,094✔
223
                    self.storage.push(last);
278,172,094✔
224
                },
278,172,094✔
225
                None => panic!("tried to push base to empty vector (?)")
×
226
            }
227
        } else {
6,737,143✔
228
            self.storage.push((base as u64) << 62);
6,737,143✔
229
        }
6,737,143✔
230
        self.len += 1; 
284,909,237✔
231
    }
284,909,237✔
232

233

234
    /// extend the reads' storage by 2-bit encoded bases
235
    fn extend(&mut self, mut bytes: impl Iterator<Item = u8>) {
9,363,192✔
236
        // fill the last incomplete u64 block
237
        while self.len % 32 != 0 {
78,701,057✔
238
            match bytes.next() {
70,587,866✔
239
                Some(b) => self.push_base(b),
69,337,865✔
240
                None => return,
1,250,001✔
241
            }
242
        }
243

244
        let mut bytes = bytes.peekable();
8,113,191✔
245

246
        // chunk the remaining items into groups of at most 32 and handle them together
247
        while bytes.peek().is_some() {
12,216,981✔
248
            let mut val: u64 = 0;
4,103,790✔
249
            let mut offset = 62;
4,103,790✔
250
            let mut n_added = 0;
4,103,790✔
251

252
            for _ in 0..32 {
66,087,080✔
253
                if let Some(b) = bytes.next() {
65,992,699✔
254
                    assert!(b < 4);
61,983,290✔
255
                    val |= (b as u64) << offset;
61,983,290✔
256
                    offset -= 2;
61,983,290✔
257
                    n_added += 1;
61,983,290✔
258
                } else {
259
                    break;
4,009,409✔
260
                }
261
            }
262

263
            self.storage.push(val);
4,103,790✔
264
            self.len += n_added;
4,103,790✔
265
        }
266
    }
9,363,192✔
267

268
    #[inline(always)]
269
    fn addr(&self, i: &usize) -> (usize, usize) {
32,154,961✔
270
        (i / 32, (i % 32 ) * 2)
32,154,961✔
271
    }
32,154,961✔
272

273
    /// get the `i`th read in a `Reads`
274
    pub fn get_read(&self, i: usize) -> Option<(DnaString, Exts, D, Strandedness)> {
394,521✔
275
        if i >= self.n_reads() { return None }
394,521✔
276

394,521✔
277
        let mut sequence = DnaString::new();
394,521✔
278
        let end = self.ends[i];
394,521✔
279
        //let start = if i != 0 { self.ends[i-1] } else { 0 };
280
        let start = match i {
394,521✔
281
            0 => 0,
2,868✔
282
            1.. => self.ends[i-1]
391,653✔
283
        };
284

285
        for b in start..end {
32,154,961✔
286
            let (block, bit) = self.addr(&b);
32,154,961✔
287
            let base = ((self.storage[block] >> (62 - bit)) & 3u64) as u8;
32,154,961✔
288
            sequence.push(base);
32,154,961✔
289
        }
32,154,961✔
290

291
        Some((sequence, self.exts[i], self.data[i], self.stranded))
394,521✔
292
    }
394,521✔
293

294

295
    /// shrink the vectors' capacity to fit the length
296
    /// 
297
    /// use sparsely
298
    pub fn shrink_to_fit(&mut self)  {
×
299
        self.storage.shrink_to_fit();
×
300
        self.data.shrink_to_fit();
×
301
        self.exts.shrink_to_fit();
×
302
        self.ends.shrink_to_fit();
×
303
    }
×
304

305
    /// Iterate over the reads as (DnaString, Exts, D).
306
    pub fn iter(&self) -> ReadsIter<'_, D> {
2,839✔
307
        ReadsIter {
2,839✔
308
            reads: self,
2,839✔
309
            i: 0,
2,839✔
310
            end: self.n_reads(),
2,839✔
311
            length: self.n_reads(),
2,839✔
312
        }
2,839✔
313
    }
2,839✔
314

315
    /// Iterate over a range start reads as (DnaString, Exts, D).
316
    pub fn partial_iter(&self, range: Range<usize>) -> ReadsIter<'_, D> {
63✔
317
        assert!(range.end <= self.n_reads());
63✔
318
        assert!(range.start < self.n_reads());
63✔
319
        assert!(range.start < range.end);
63✔
320
        ReadsIter {
63✔
321
            reads: self,
63✔
322
            i: range.start,
63✔
323
            end: range.end,
63✔
324
            length: (range.end - range.start)
63✔
325
        }
63✔
326
    }
63✔
327

328
    pub fn info(&self) -> String {
7✔
329
        format!("Reads {{ n reads: {}, stranded: {:?} }}", self.n_reads(), self.stranded)
7✔
330
    }
7✔
331
}
332

333
impl<D: Clone + Copy + Eq + Hash> Reads<D> {
334
    pub fn data_kmers(&self, k: usize) -> HashMap<D, usize> {
7✔
335
        let mut hm = HashMap::new();
7✔
336

7✔
337
        self.iter().for_each(|(read, _, data, _)| {
24✔
338
            let kmers = read.len().saturating_sub(k - 1);
24✔
339
            if let Some(count) = hm.get_mut(&data) {
24✔
NEW
340
                *count += kmers;
×
341
            } else {
24✔
342
                hm.insert(data, kmers);
24✔
343
            }
24✔
344
        });
24✔
345

7✔
346
        hm
7✔
347
    }
7✔
348
}
349

350
impl<D: Clone + Copy> Default for Reads<D> {
351
    fn default() -> Self {
2✔
352
        Self::new(Strandedness::Unstranded)
2✔
353
    }
2✔
354
}
355

356
/// Iterator over values of a DnaStringoded sequence (values will be unpacked into bytes).
357
pub struct ReadsIter<'a, D> {
358
    reads: &'a Reads<D>,
359
    i: usize,
360
    end: usize,
361
    length: usize,
362
}
363

364
impl<D: Clone + Copy> Iterator for ReadsIter<'_, D> {
365
    type Item = (DnaString, Exts, D, Strandedness);
366

367
    fn next(&mut self) -> Option<Self::Item> {
397,409✔
368
        if (self.i < self.reads.n_reads()) && (self.i < self.end) {
397,409✔
369
            let value = self.reads.get_read(self.i);
394,507✔
370
            self.i += 1;
394,507✔
371
            value
394,507✔
372
        } else {
373
            None
2,902✔
374
        }
375
    }
397,409✔
376
}
377

378
impl<D: Copy> ExactSizeIterator for ReadsIter<'_, D> {
379
    fn len(&self) -> usize {
×
380
        self.length
×
381
    }
×
382
}
383

384
impl<D: Clone + Copy + Debug> Display for Reads<D> {
385
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
×
386
        let vec: Vec<(DnaString, Exts, D, Strandedness)> = self.iter().collect();
×
387
        write!(f, "{:?}", vec)
×
388
    }
×
389
}
390

391
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord, Hash)]
392
pub enum ReadsPaired<D> {
393
    Empty,
394
    Unpaired { reads: Reads<D> },
395
    Paired { paired1: Reads<D>, paired2: Reads<D> },
396
    Combined {paired1: Reads<D>, paired2: Reads<D>, unpaired: Reads<D>}
397
}
398

399
impl<D: Clone + Copy> ReadsPaired<D> {
400
    pub fn iterable(&self) -> Vec<&Reads<D>> {
4✔
401
        match self {
4✔
402
            Self::Empty => vec![],
1✔
403
            Self::Unpaired { reads  } => vec![reads],
1✔
404
            Self::Paired { paired1, paired2 } => vec![paired1, paired2],
1✔
405
            Self::Combined { paired1, paired2, unpaired } => vec![paired1, paired2, unpaired],
1✔
406
        }
407
    }
4✔
408

409
    pub fn n_reads(&self) -> usize {
2,834✔
410
        match self {
2,834✔
411
            Self::Empty => 0,
1✔
412
            Self::Unpaired { reads  } => reads.n_reads(),
2,831✔
413
            Self::Paired { paired1, paired2 } => paired1.n_reads() + paired2.n_reads(),
1✔
414
            Self::Combined { paired1, paired2, unpaired } => paired1.n_reads() + paired2.n_reads() + unpaired.n_reads(),
1✔
415
        }
416
    }
2,834✔
417

418
    pub fn mem(&self) -> usize {
4✔
419
        match self {
4✔
420
            Self::Empty => 0,
1✔
421
            Self::Unpaired { reads } => reads.mem(),
1✔
422
            Self::Paired { paired1, paired2 } => paired1.mem() + paired2.mem(),
1✔
423
            Self::Combined { paired1, paired2, unpaired } => paired1.mem() + paired2.mem() + unpaired.mem(),
1✔
424
        }
425
    }
4✔
426

427
    /// transform a tuple of two paired [`Reads`] and one unpaired [`Reads`] into a `ReadsPaired`
428
    /// depending on the contents of the [`Reads`]
429
    pub fn from_reads((paired1, paired2, unpaired): (Reads<D>, Reads<D>, Reads<D>)) -> Self {
5✔
430
        // first two elements should be paired reads and thus have same n
5✔
431
        assert_eq!(paired1.n_reads(), paired2.n_reads(), "Error: R1 read and R2 read counts have to match");
5✔
432

433
        if (paired1.n_reads() + paired2.n_reads() + unpaired.n_reads()) == 0 {
4✔
434
            // no reads
435
            ReadsPaired::Empty
1✔
436
        } else if paired1.n_reads() == 0 && unpaired.n_reads() > 0 {
3✔
437
            // only reads in third element -> unpaired
438
            ReadsPaired::Unpaired { reads: unpaired }
1✔
439
        } else if paired1.n_reads() > 0 && unpaired.n_reads() == 0 {
2✔
440
            // reads in first and second element -> paired
441
            ReadsPaired::Paired { paired1, paired2 }
1✔
442
        } else if paired1.n_reads() > 0 && unpaired.n_reads() > 0 {
1✔
443
            // reads in all elements: both paired and unpaired reads
444
            ReadsPaired::Combined { paired1, paired2, unpaired }
1✔
445
        } else {
446
            panic!("error in transforming Reads into ReadsPaired")
×
447
        }
448
    }
4✔
449

450
    pub fn iter(&self) -> Box<dyn Iterator<Item = (DnaString, Exts, D, Strandedness)> + '_> {
2,821✔
451
        match self {
2,821✔
452
            ReadsPaired::Empty => panic!("Error: no reads to process"),
×
453
            ReadsPaired::Unpaired { reads } => Box::new(reads.iter()),
2,819✔
454
            ReadsPaired::Paired { paired1, paired2 } => Box::new(paired1.iter().chain(paired2.iter())),
1✔
455
            ReadsPaired::Combined { paired1, paired2, unpaired } => Box::new(paired1.iter().chain(paired2.iter()).chain(unpaired.iter())),
1✔
456
        }
457
    }
2,821✔
458

459
    pub fn iter_partial(&self, range: Range<usize>) -> Box<dyn Iterator<Item = (DnaString, Exts, D, Strandedness)> + '_> {
42✔
460
        match self {
42✔
461
            Self::Empty => panic!("Error: no reads to process"),
×
462
            Self::Unpaired { reads } => Box::new(reads.partial_iter(range)),
33✔
463
            Self::Paired { paired1, paired2 } => {
3✔
464
                let n_p1 = paired1.n_reads();
3✔
465
                if range.start >= n_p1 {
3✔
466
                    // range is fully in paired2
467
                    Box::new(paired2.partial_iter((range.start - n_p1)..(range.end - n_p1)))
1✔
468
                } else if range.end <= n_p1 {
2✔
469
                    // range is fully in paired1
470
                    Box::new(paired1.partial_iter(range))
1✔
471
                } else {
472
                    // range is both in paired1 and paired2
473
                    Box::new(paired1.partial_iter(range.start..n_p1).chain(paired2.partial_iter(0..(range.end - n_p1))))
1✔
474
                }
475
            },
476
            Self::Combined { paired1, paired2, unpaired } => {
6✔
477
                let n_p1 = paired1.n_reads();
6✔
478
                let n_p2 = paired2.n_reads();
6✔
479
                let n_p12 = n_p1 + paired2.n_reads();
6✔
480
                if range.end <= n_p1 {
6✔
481
                    // range is only in paired1
482
                    Box::new(paired1.partial_iter(range))
1✔
483
                } else if range.end >= n_p1 && range.end <= n_p12 && range.start >= n_p1 && range.start <= n_p12 {
5✔
484
                    // range is only in paired2
485
                    Box::new(paired2.partial_iter((range.start - n_p1)..(range.end - n_p1)))
1✔
486
                } else if range.start >= n_p12 {
4✔
487
                    // range is only in unpaired
488
                    Box::new(unpaired.partial_iter((range.start - n_p12)..(range.end - n_p12)))
1✔
489
                } else if range.start <= n_p1 && range.end >= n_p1 && range.end <= n_p12 {
3✔
490
                    // range is in paired1 and paired2
491
                    Box::new(paired1.partial_iter(range.start..n_p1).chain(paired2.partial_iter(0..(range.end - n_p1))))
1✔
492
                } else if range.start >= n_p1 && range.start <= n_p12 && range.end >= n_p12 {
2✔
493
                    // range is in paired2 and unpaired
494
                    Box::new(paired2.partial_iter((range.start - n_p1)..n_p2).chain(unpaired.partial_iter(0..(range.end - n_p12))))
1✔
495
                } else {
496
                    // range is in paired1, paired2, and in unpaired
497
                    Box::new(paired1.partial_iter(range.start..n_p1).chain(paired2.partial_iter(0..n_p2)).chain(unpaired.partial_iter(0..(range.end - n_p12))))
1✔
498
                }
499
            }
500
        }
501
    }
42✔
502

503
    /// if the `ReadsPaired` is of `Combined` type, remove the unpaired reads
504
    pub fn decombine(&mut self) {
2✔
505
        if let Self::Combined { paired1, paired2, unpaired: _ } = self {
2✔
506
            *self = ReadsPaired::Paired { paired1: take(paired1), paired2: take(paired2) }
1✔
507
        }
1✔
508
    }
2✔
509
}
510

511
impl<D: Clone + Copy + Eq + Hash> ReadsPaired<D> {
512
    pub fn data_kmers(&self, k: usize) -> HashMap<D, usize> {
3✔
513
        match self {
3✔
NEW
514
            Self::Empty => HashMap::new(),
×
515
            Self::Unpaired { reads } => reads.data_kmers(k),
1✔
516
            Self::Paired { paired1, paired2 } => {
1✔
517
                let mut hm_p1 = paired1.data_kmers(k);
1✔
518
                let hm_p2 = paired2.data_kmers(k);
1✔
519

1✔
520
                hm_p2.into_iter().for_each(|(data, kmers)| {
4✔
521
                   if let Some(count) = hm_p1.get_mut(&data) {
4✔
522
                    *count += kmers;
4✔
523
                   } else {
4✔
NEW
524
                    hm_p1.insert(data, kmers);
×
NEW
525
                   }
×
526
                });
4✔
527

1✔
528
                hm_p1
1✔
529
            },
530
            Self::Combined { paired1, paired2, unpaired } => {
1✔
531
                let mut hm_p1 = paired1.data_kmers(k);
1✔
532
                let hm_p2 = paired2.data_kmers(k);
1✔
533
                let hm_up = unpaired.data_kmers(k);
1✔
534

1✔
535
                hm_p2.into_iter().for_each(|(data, kmers)| {
4✔
536
                   if let Some(count) = hm_p1.get_mut(&data) {
4✔
537
                    *count += kmers;
4✔
538
                   } else {
4✔
NEW
539
                    hm_p1.insert(data, kmers);
×
NEW
540
                   }
×
541
                });
4✔
542

1✔
543
                hm_up.into_iter().for_each(|(data, kmers)| {
2✔
544
                    if let Some(count) = hm_p1.get_mut(&data) {
2✔
545
                     *count += kmers;
2✔
546
                    } else {
2✔
NEW
547
                     hm_p1.insert(data, kmers);
×
NEW
548
                    }
×
549
                 });
2✔
550

1✔
551
                hm_p1
1✔
552
            }
553
        }
554
    }
3✔
555
}
556

557
impl ReadsPaired<u8> {
558
    pub fn tag_kmers(&self, k: usize) -> Vec<u64>{
3✔
559
        let hashed_kmer_counts = self.data_kmers(k);
3✔
560

3✔
561
        let n_samples = hashed_kmer_counts.keys().max().expect("Error: no samples");
3✔
562

3✔
563
        let mut kmer_counts = vec![0; *n_samples as usize + 1];
3✔
564
        for (tag, kmer_count) in hashed_kmer_counts {
13✔
565
            kmer_counts[tag as usize] += kmer_count as u64;
10✔
566
        }
10✔
567

568
        kmer_counts
3✔
569
    }
3✔
570
}
571

572
impl<D: Clone + Copy> Display for ReadsPaired<D> {
573
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
4✔
574
        match self {
4✔
575
            Self::Empty => write!(f, "empty ReadsPaired"),
1✔
576
            Self::Unpaired { reads } => write!(f, "unpaired ReadsPaired: \n{}", reads.info()),
1✔
577
            Self::Paired { paired1, paired2 } => write!(f, "paired ReadsPaired: \n{}\n{}", paired1.info(), paired2.info()),
1✔
578
            Self::Combined { paired1, paired2, unpaired } => write!(f, "combined ReadsPaired: \n{}\n{}\n{}", paired1.info(), paired2.info(), unpaired.info()),
1✔
579
        }
580
    }
4✔
581
}
582

583
#[cfg(test)]
584
mod tests {
585
    use std::{collections::HashMap, time};
586

587
    use itertools::enumerate;
588
    use rand::random;
589

590
    use crate::{dna_string::DnaString, reads::Strandedness, Exts};
591
    use super::{Reads, ReadsPaired};
592

593
    #[test]
594
    fn test_add() {
1✔
595

1✔
596
        let fastq = vec![
1✔
597
            (DnaString::from_acgt_bytes(str::as_bytes("ACGATCGT")), Exts::empty(), 6u8),
1✔
598
            (DnaString::from_acgt_bytes(str::as_bytes("GGGGGG")), Exts::empty(), 5u8),
1✔
599
            (DnaString::from_acgt_bytes(str::as_bytes("TTGGTT")), Exts::empty(), 7u8),
1✔
600
            (DnaString::from_acgt_bytes(str::as_bytes("ACCAC")), Exts::empty(), 8u8),
1✔
601
            (DnaString::from_acgt_bytes(str::as_bytes("TCCCT")), Exts::empty(), 9u8),
1✔
602
            (DnaString::from_acgt_bytes(str::as_bytes("ACCAC")), Exts::empty(), 8u8),
1✔
603
            (DnaString::from_acgt_bytes(str::as_bytes("TCCCT")), Exts::empty(), 9u8),
1✔
604

1✔
605
        ];
1✔
606

1✔
607

1✔
608
        let mut reads = Reads::new(Strandedness::Unstranded);
1✔
609
        for (read, ext, data) in fastq.clone() {
7✔
610
            reads.add_read(read, ext, data);
7✔
611
        }
7✔
612

613
        println!("reads: {:#?}", reads);
1✔
614

1✔
615

1✔
616
        /* for no in reads.storage.iter() {
1✔
617
            println!("{:#b}", no)
1✔
618
        } */
1✔
619

1✔
620
         assert_eq!(reads.storage, vec![1791212948343256433, 5140577499666710528]);
1✔
621

622
        for (i, _) in fastq.iter().enumerate() {
7✔
623
            //println!("read {}: {:?}", i, reads.get_read(i))
624
            assert_eq!((fastq[i].0.clone(), fastq[i].1, fastq[i].2, Strandedness::Unstranded), reads.get_read(i).unwrap())
7✔
625
        }
626

627
        for (seq, _, _, _) in reads.iter() {
7✔
628
            println!("{:?}, {}", seq, seq.len())
7✔
629
        }
630
        println!();
1✔
631

632
        for read in reads.partial_iter(5..7) {
2✔
633
            println!("{:?}", read)
2✔
634
        }
635

636
        println!("memory usage: {}", reads.mem())
1✔
637
    }
1✔
638

639
    #[test]
640
    fn test_get_read() {
1✔
641
        let mut reads = Reads::new(Strandedness::Unstranded);
1✔
642
        //reads.add_read(DnaString::from_acgt_bytes("AGCTAGCTAGC".as_bytes()), Exts::empty(), 67u8);
1✔
643
        reads.add_from_bytes("ACGATCGNATGCTAGCTGATCGGCGACGATCGATGCTAGCTGATCGTAGCTGACTGATCGATCG".as_bytes(), Exts::empty(), 67u8);
1✔
644
        let read = reads.get_read(0);
1✔
645
        println!("{:?}", read);
1✔
646
        println!("{:#066b}", reads.storage[0]);
1✔
647
        println!("{:?}", reads)
1✔
648
    }
1✔
649

650
    #[test]
651
    fn test_add_from_bytes() {
1✔
652
        let dna = [
1✔
653
            "AAGCGGAGATTATTCACGAGCATCGCGTAC".as_bytes(),
1✔
654
            "GATCGATGCATGCTAGA".as_bytes(),
1✔
655
            "ACGTAAAAAAAAAATTATATAACGTACGTAAAAAAAAAATTATATAACGTAACGTAAAAAAAAAAATTATAATAACGT".as_bytes(),
1✔
656
            "AGCTAGCTAGCTGACTGAGCGACTGA".as_bytes(),
1✔
657
            "AGCTAGCTAGCTGACTGAGCGACTGACGGATC".as_bytes(),
1✔
658
            "TTTTTTTTTTTTTTTTTTTTTTTT".as_bytes(),
1✔
659
            "ACGATCGAATGCTAGCTGATCGGCGACGATCGATGCTAGCTGATCGTAGCTGACTGATCGATCG".as_bytes(),
1✔
660
            "ACGATCGATGCTAGCTGATCGGCGACGATCGATGCTAGCTGATCGTAGCTGACTGATCGATCGAAGGGCAGTTAGGCCGTAAGCGCGAT".as_bytes(),
1✔
661
        ];
1✔
662

1✔
663
        let mut reads: Reads<u8> = Reads::new(Strandedness::Unstranded);
1✔
664
        for seq in dna {
9✔
665
            reads.add_from_bytes(seq, Exts::empty(), random());
8✔
666

8✔
667
        }
8✔
668

669
        for (i, seq) in enumerate(reads.iter()) {
8✔
670
            let sequence = DnaString::from_acgt_bytes(dna[i]);
8✔
671
            assert_eq!(seq.0, sequence);
8✔
672
        }
673
    }
1✔
674

675
    #[test]
676
    fn test_add_from_bytes_checked() {
1✔
677
        let dna = [
1✔
678
            "AAGCGGAGATTATTCACGAGCATCGCGTAC".as_bytes(),
1✔
679
            "GATCGATGCATGCTAGA".as_bytes(),
1✔
680
            "ACGTAAAAAAAAAATTATATAACGTACGTAAAAAAAAAANTTATATAACGTAACGTAAAAAAAAAAATTATAATAACGT".as_bytes(),
1✔
681
            "AGCTAGCTAGCTGACNGAGCGACTGA".as_bytes(),
1✔
682
            "AGCTAGCTAGCTGACTGAGCGACTGACGGATC".as_bytes(),
1✔
683
            "TTTTTTTTTTTTTTTTTTTTTTTT".as_bytes(),
1✔
684
            "ACGATCGAATGCTAGCTGATCGGCGACGATCGATGCTAGCTGATCGTAGCTGACNNNTGATCGATCG".as_bytes(),
1✔
685
            "ACGATCGATGCTAGCTGATCGGCGACGATCGATGCTAGCTGATCGTAGCTGACTGATCGATCGAAGGGCAGTTAGGCCGTAAGCGCGAT".as_bytes(),
1✔
686
            "A".as_bytes(),
1✔
687
            "AAAAN".as_bytes(),
1✔
688
            "NNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNN".as_bytes(),
1✔
689
        ];
1✔
690

1✔
691
        let mut reads: Reads<u8> = Reads::new(Strandedness::Unstranded);
1✔
692
        let mut corrects = Vec::new();
1✔
693
        for seq in dna {
12✔
694
            corrects.push(reads.add_from_bytes_checked(seq, Exts::empty(), random()));
11✔
695
        }
11✔
696

697
        let mut read_counter = 0;
1✔
698

699
        for (i, correct) in enumerate(corrects) {
11✔
700
            let sequence = DnaString::from_acgt_bytes_checked(dna[i]);
11✔
701
            match correct {
11✔
702
                true => {
703
                    let read = reads.get_read(read_counter).unwrap();
6✔
704
                    assert_eq!(sequence.unwrap(), read.0);
6✔
705
                    read_counter += 1;
6✔
706

707
                },
708
                false => assert!(sequence.is_err()),
5✔
709
            }
710
        }
711
    }
1✔
712

713
    #[test]
714
    fn test_speed_from_bytes() {
1✔
715
        let dnas = [
1✔
716
            "AAGCGGAGATTATTCACGAGCATCGCGTAC".as_bytes(),
1✔
717
            "GATCGATGCATGCTAGA".as_bytes(),
1✔
718
            "ACGTAAAAAAAAAATTATATAACGTACGTAAAAAAAAAANTTATATAACGTAACGTAAAAAAAAAAATTATAATAACGT".as_bytes(),
1✔
719
            "AGCTAGCTAGCTGACNGAGCGACTGA".as_bytes(),
1✔
720
            "AGCTAGCTAGCTGACTGAGCGACTGACGGATC".as_bytes(),
1✔
721
            "TTTTTTTTTTTTTTTTTTTTTTTT".as_bytes(),
1✔
722
            "ACGATCGAATGCTAGCTGATCGGCGACGATCGATGCTAGCTGATCGTAGCTGACNNNTGATCGATCG".as_bytes(),
1✔
723
            "ACGATCGATGCTAGCTGATCGGCGACGATCGATGCTAGCTGATCGTAGCTGACTGATCGATCGAAGGGCAGTTAGGCCGTAAGCGCGAT".as_bytes(),
1✔
724
            "A".as_bytes(),
1✔
725
            "AAAAN".as_bytes(),
1✔
726
            "NNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNN".as_bytes(),
1✔
727
        ];
1✔
728

729
        const REPS: usize = 500000;
730
        /*
731
        test with 5 ml:
732
            through DnaString: 88.09323 s 
733
            direct to Read: 44.41913 s
734
         */
735

736

737
        let ds_start= time::Instant::now();
1✔
738
        let mut reads: Reads<u8> = Reads::new(Strandedness::Unstranded);
1✔
739
        for _i in 0..REPS {
500,001✔
740
            for dna in dnas {
6,000,000✔
741
                reads.add_read(DnaString::from_acgt_bytes(dna), Exts::empty(), random());
5,500,000✔
742
            }
5,500,000✔
743
        }
744
        let ds_finish = ds_start.elapsed();
1✔
745

1✔
746
        let r_start= time::Instant::now();
1✔
747
        let mut reads: Reads<u8> = Reads::new(Strandedness::Unstranded);
1✔
748
        for _i in 0..REPS {
500,001✔
749
            for dna in dnas {
6,000,000✔
750
                reads.add_from_bytes(dna, Exts::empty(), random());
5,500,000✔
751
            }
5,500,000✔
752
        }
753
        let r_finish = r_start.elapsed();
1✔
754

1✔
755
        println!("through DnaString: {} s \n direct to Read: {} s", ds_finish.as_secs_f32(), r_finish.as_secs_f32())
1✔
756

1✔
757

1✔
758
    }
1✔
759

760

761
    #[test]
762
    fn test_reads_stranded() {
1✔
763
        let reads: Reads<u8> = Reads::new(Strandedness::Forward);
1✔
764
        assert_eq!(reads.stranded(), Strandedness::Forward);
1✔
765
    }
1✔
766

767
    #[test]
768
    fn test_reads_data_kmers() {
1✔
769
        let mut reads = Reads::new(Strandedness::Unstranded);
1✔
770
        let seqs = [
1✔
771
            ("ACGATCGTACGTACGTAGCTAGCTGCTAGCTAGCTGACTGACTGA", 0),
1✔
772
            ("CGATGCTATCAGCGAGCGATCGTACGTAGCTACG", 1),
1✔
773
            ("CGATCGACGAGCAGCGTATGCTACGAGCTGACGATCTACGA", 2),
1✔
774
            ("CACACACGGCATCGATCGAGCAGCATCGACTACGTA", 3),
1✔
775
        ];
1✔
776

1✔
777
        seqs.iter().for_each(|(read, tag)| reads.add_from_bytes(read.as_bytes(), Exts::empty(), *tag as u8));
4✔
778
        let data_kmers = reads.data_kmers(16);
1✔
779
       
1✔
780
        let comp_hm: HashMap<u8, usize> = [(0, 30), (1, 19), (2, 26), (3, 21)].into_iter().collect();
1✔
781

1✔
782
        assert_eq!(comp_hm, data_kmers);
1✔
783
    }
1✔
784

785
    #[test]
786
    fn test_reads_info() {
1✔
787
        let mut reads = Reads::new(Strandedness::Unstranded);
1✔
788
        let seqs = [
1✔
789
            ("ACGATCGTACGTACGTAGCTAGCTGCTAGCTAGCTGACTGACTGA", 0),
1✔
790
            ("CGATGCTATCAGCGAGCGATCGTACGTAGCTACG", 1),
1✔
791
            ("CGATCGACGAGCAGCGTATGCTACGAGCTGACGATCTACGA", 2),
1✔
792
            ("CACACACGGCATCGATCGAGCAGCATCGACTACGTA", 3),
1✔
793
        ];
1✔
794

1✔
795
        seqs.iter().for_each(|(read, tag)| reads.add_from_bytes(read.as_bytes(), Exts::empty(), *tag as u8));
4✔
796

1✔
797
        assert_eq!(reads.info(), "Reads { n reads: 4, stranded: Unstranded }".to_string());
1✔
798
    }
1✔
799

800
    #[test]
801
    fn test_reads_paired() {
1✔
802
        let mut p1 = Reads::new(Strandedness::Unstranded);
1✔
803
        let mut p2 = Reads::new(Strandedness::Unstranded);
1✔
804
        let mut up = Reads::new(Strandedness::Unstranded);
1✔
805

1✔
806
        let reads_p1 = [
1✔
807
            "ACGATCGTACGTACGTAGCTAGCTGCTAGCTAGCTGACTGACTGA",
1✔
808
            "CGATGCTATCAGCGAGCGATCGTACGTAGCTACG",
1✔
809
            "CGATCGACGAGCAGCGTATGCTACGAGCTGACGATCTACGA",
1✔
810
            "CACACACGGCATCGATCGAGCAGCATCGACTACGTA",
1✔
811
        ];
1✔
812

1✔
813
        let reads_p2 = [
1✔
814
            "AGCTAGCTAGCTACTGATCGTAGCTAGCTGATCGA",
1✔
815
            "AGCGATCGTACGTAGCTAGCTA",
1✔
816
            "CGATCGATCGACTAGCGTAGCTGACTGAC",
1✔
817
            "CAGATGCTCTGCTGACTGACTGATCGTACTGACTAGCATCTAGC",
1✔
818
        ];
1✔
819

1✔
820
        let reads_up = [
1✔
821
            "CGTACTAGCTGACGTAC",
1✔
822
            "CGATGCTAGCTAGCTAGCGATCG",
1✔
823
        ];
1✔
824

1✔
825
        let tags = (0..4).collect::<Vec<u8>>();
1✔
826

1✔
827
        reads_p1.iter().enumerate().for_each(|(i, read)| p1.add_from_bytes(read.as_bytes(), Exts::empty(), tags[i]));
4✔
828
        reads_p2.iter().enumerate().for_each(|(i, read)| p2.add_from_bytes(read.as_bytes(), Exts::empty(), tags[i]));
4✔
829
        reads_up.iter().enumerate().for_each(|(i, read)| up.add_from_bytes(read.as_bytes(), Exts::empty(), tags[i]));
2✔
830

1✔
831
        let empty: ReadsPaired<u8> = ReadsPaired::from_reads((Reads::new(Strandedness::Unstranded), Reads::new(Strandedness::Unstranded), Reads::new(Strandedness::Unstranded)));
1✔
832
        assert_eq!(empty, ReadsPaired::Empty);
1✔
833
        assert_eq!(empty.mem(), 0);
1✔
834
        assert_eq!(empty.n_reads(), 0);
1✔
835
        assert_eq!(empty.iterable(), Vec::<&Reads<u8>>::new());
1✔
836

837
        let unpaired = ReadsPaired::from_reads((Reads::new(Strandedness::Unstranded), Reads::new(Strandedness::Unstranded), up.clone()));
1✔
838
        assert_eq!(ReadsPaired::Unpaired { reads: up.clone() }, unpaired);
1✔
839
        assert_eq!(unpaired.mem(), 148);
1✔
840
        assert_eq!(unpaired.n_reads(), 2);
1✔
841
        assert_eq!(unpaired.iterable(), vec![&up]);
1✔
842
      
843
        let paired = ReadsPaired::from_reads((p1.clone(), p2.clone(), Reads::new(Strandedness::Unstranded)));
1✔
844
        assert_eq!(ReadsPaired::Paired { paired1: p1.clone(), paired2: p2.clone() }, paired);        
1✔
845
        assert_eq!(paired.mem(), 384);
1✔
846
        assert_eq!(paired.n_reads(), 8);
1✔
847
        assert_eq!(paired.iterable(), vec![&p1, &p2]);
1✔
848

849
        let combined = ReadsPaired::from_reads((p1.clone(), p2.clone(), up.clone()));
1✔
850
        assert_eq!(ReadsPaired::Combined { paired1: p1.clone(), paired2: p2.clone(), unpaired: up.clone() }, combined);
1✔
851
        assert_eq!(combined.mem(), 532);
1✔
852
        assert_eq!(combined.n_reads(), 10);
1✔
853
        assert_eq!(combined.iterable(), vec![&p1, &p2, &up]);
1✔
854

855

856
        // test iter
857

858
        assert_eq!(unpaired.iter().collect::<Vec<_>>(), up.iter().collect::<Vec<_>>());
1✔
859
        assert_eq!(paired.iter().collect::<Vec<_>>(), p1.iter().chain(p2.iter()).collect::<Vec<_>>());
1✔
860
        assert_eq!(combined.iter().collect::<Vec<_>>(), p1.iter().chain(p2.iter()).chain(up.iter()).collect::<Vec<_>>());
1✔
861

862
        // test partial iter
863

864
        assert_eq!(unpaired.iter_partial(0..1).collect::<Vec<_>>(), up.partial_iter(0..1).collect::<Vec<_>>());
1✔
865

866
        assert_eq!(paired.iter_partial(0..1).collect::<Vec<_>>(), p1.partial_iter(0..1).collect::<Vec<_>>());
1✔
867
        assert_eq!(paired.iter_partial(5..7).collect::<Vec<_>>(), p2.partial_iter(1..3).collect::<Vec<_>>());
1✔
868
        assert_eq!(paired.iter_partial(1..8).collect::<Vec<_>>(), p1.partial_iter(1..4).chain(p2.partial_iter(0..4)).collect::<Vec<_>>());
1✔
869

870
        assert_eq!(combined.iter_partial(0..1).collect::<Vec<_>>(), p1.partial_iter(0..1).collect::<Vec<_>>());
1✔
871
        assert_eq!(combined.iter_partial(5..7).collect::<Vec<_>>(), p2.partial_iter(1..3).collect::<Vec<_>>());
1✔
872
        assert_eq!(combined.iter_partial(8..10).collect::<Vec<_>>(), up.partial_iter(0..2).collect::<Vec<_>>());
1✔
873
        assert_eq!(combined.iter_partial(1..8).collect::<Vec<_>>(), p1.partial_iter(1..4).chain(p2.partial_iter(0..4)).collect::<Vec<_>>());
1✔
874
        assert_eq!(combined.iter_partial(6..9).collect::<Vec<_>>(), p2.partial_iter(2..4).chain(up.partial_iter(0..1)).collect::<Vec<_>>());
1✔
875
        assert_eq!(combined.iter_partial(1..9).collect::<Vec<_>>(), p1.partial_iter(1..4).chain(p2.partial_iter(0..4)).chain(up.partial_iter(0..1)).collect::<Vec<_>>());
1✔
876

877
        // test tag kmers (and data_kmers)
878
        assert_eq!(unpaired.tag_kmers(16), vec![2, 8]);
1✔
879
        assert_eq!(paired.tag_kmers(16), vec![50, 26, 40, 50]);
1✔
880
        assert_eq!(combined.tag_kmers(16), vec![52, 34, 40, 50]);
1✔
881

882
        // test decombine
883
        let mut paired_dc = paired.clone();
1✔
884
        paired_dc.decombine();
1✔
885
        let mut combined_dc = combined.clone();
1✔
886
        combined_dc.decombine();
1✔
887
        assert_eq!(paired_dc, paired);
1✔
888
        assert_eq!(combined_dc, paired);
1✔
889

890
        // display
891
        assert_eq!(format!("{}", empty), "empty ReadsPaired".to_string());
1✔
892
        assert_eq!(format!("{}", unpaired), "unpaired ReadsPaired: 
1✔
893
Reads { n reads: 2, stranded: Unstranded }".to_string());
1✔
894
        assert_eq!(format!("{}", paired), "paired ReadsPaired: 
1✔
895
Reads { n reads: 4, stranded: Unstranded }
1✔
896
Reads { n reads: 4, stranded: Unstranded }".to_string());
1✔
897
        assert_eq!(format!("{}", combined), "combined ReadsPaired: 
1✔
898
Reads { n reads: 4, stranded: Unstranded }
1✔
899
Reads { n reads: 4, stranded: Unstranded }
1✔
900
Reads { n reads: 2, stranded: Unstranded }".to_string());
1✔
901

902
    }
1✔
903

904
    #[test]
905
    #[should_panic]
906
    fn test_reads_paired_panic() {
1✔
907
        let mut p1 = Reads::new(Strandedness::Unstranded);
1✔
908

1✔
909
        let reads_p1 = [
1✔
910
            "ACGATCGTACGTACGTAGCTAGCTGCTAGCTAGCTGACTGACTGA",
1✔
911
            "CGATGCTATCAGCGAGCGATCGTACGTAGCTACG",
1✔
912
            "CGATCGACGAGCAGCGTATGCTACGAGCTGACGATCTACGA",
1✔
913
            "CACACACGGCATCGATCGAGCAGCATCGACTACGTA",
1✔
914
        ];
1✔
915

1✔
916
        reads_p1.iter().for_each(|read| p1.add_from_bytes(read.as_bytes(), Exts::empty(), 0u8));
4✔
917

1✔
918
        let _ = ReadsPaired::from_reads((p1, Reads::new(Strandedness::Unstranded), Reads::new(Strandedness::Unstranded)));
1✔
919
    }
1✔
920
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc