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

jlab / rust-debruijn / 14265292510

04 Apr 2025 12:25PM UTC coverage: 82.582%. First build
14265292510

push

github

web-flow
Merge pull request #7 from jlab/dev

New `Reads` struct and revamped summarizers

- new `reads::Reads` struct for compact reads storage
- new handling of ambiguous bases
- move summarize methods to `SummaryData`
- add new functionality to `SummaryData` (p-values, edge multiplicities)
- `KmerSummarizer` removed

1890 of 2176 new or added lines in 10 files covered. (86.86%)

6078 of 7360 relevant lines covered (82.58%)

3674640.93 hits per line

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

83.01
/src/dna_string.rs
1
// Copyright 2014 Johannes Köster and 10x Genomics
2
// Licensed under the MIT license (http://opensource.org/licenses/MIT)
3
// This file may not be copied, modified, or distributed
4
// except according to those terms.
5

6
//! A 2-bit encoding of arbitrary length DNA sequences.
7
//!
8
//! Store arbitrary-length DNA strings in a packed 2-bit encoding. Individual base values are encoded
9
//! as the integers 0,1,2,3 corresponding to A,C,G,T.
10
//!
11
//! # Example
12
//! ```
13
//! use debruijn::Kmer;
14
//! use debruijn::dna_string::*;
15
//! use debruijn::kmer::Kmer16;
16
//! use debruijn::Vmer;
17
//!
18
//! // Construct a new DNA string
19
//! let dna_string1 = DnaString::from_dna_string("ACAGCAGCAGCACGTATGACAGATAGTGACAGCAGTTTGTGACCGCAAGAGCAGTAATATGATG");
20
//!
21
//! // Get an immutable view into the sequence
22
//! let slice1 = dna_string1.slice(10, 40);
23
//!
24
//! // Get a kmer from the DNA string
25
//! let first_kmer: Kmer16 = slice1.get_kmer(0);
26
//! assert_eq!(first_kmer, Kmer16::from_ascii(b"CACGTATGACAGATAG"))
27

28
use itertools::Itertools;
29
use serde_derive::{Deserialize, Serialize};
30
use std::borrow::Borrow;
31
use std::cmp::min;
32
use std::collections::hash_map::DefaultHasher;
33
use std::error::Error;
34
use std::fmt::{self, Display};
35
use std::hash::{Hash, Hasher};
36

37
use crate::{base_to_bits, base_to_bits_checked};
38
use crate::bits_to_ascii;
39
use crate::bits_to_base;
40
use crate::dna_only_base_to_bits;
41

42
use crate::Kmer;
43
use crate::Mer;
44
use crate::MerIter;
45
use crate::Vmer;
46

47
const BLOCK_BITS: usize = 64;
48
const WIDTH: usize = 2;
49

50
const MASK: u64 = 0x3;
51

52
/// A container for sequence of DNA bases.
53
/// ```
54
/// use debruijn::dna_string::DnaString;
55
/// use debruijn::kmer::Kmer8;
56
/// use debruijn::{Mer, Vmer};
57
///
58
/// let dna_string = DnaString::from_dna_string("ATCGTACGTACGTAGTC");
59
///
60
/// // Iterate over 8-mers
61
/// for k in dna_string.iter_kmers::<Kmer8>() {
62
///     println!("{:?}", k);
63
/// }
64
///
65
/// // Get a base, encoded as a byte in 0-3 range
66
/// assert_eq!(dna_string.get(0), 0);
67
/// assert_eq!(dna_string.get(1), 3);
68
///
69
/// // Make a read-only 'slice' of a DnaString
70
/// let slc = dna_string.slice(1, 10);
71
///
72
///  assert_eq!(slc.iter_kmers::<Kmer8>().next(), dna_string.iter_kmers::<Kmer8>().skip(1).next());
73
/// ```
74
#[derive(Ord, PartialOrd, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
75
pub struct DnaString {
76
    storage: Vec<u64>,
77
    len: usize,
78
}
79

80
impl Mer for DnaString {
81
    fn len(&self) -> usize {
268,307,342✔
82
        self.len
268,307,342✔
83
    }
268,307,342✔
84

85
    fn is_empty(&self) -> bool {
×
86
        self.len == 0
×
87
    }
×
88

89
    /// Get the value at position `i`.
90
    #[inline(always)]
91
    fn get(&self, i: usize) -> u8 {
249,224,400✔
92
        let (block, bit) = self.addr(i);
249,224,400✔
93
        self.get_by_addr(block, bit)
249,224,400✔
94
    }
249,224,400✔
95

96
    /// Set the value as position `i`.
97
    fn set_mut(&mut self, i: usize, value: u8) {
3,548,967✔
98
        let (block, bit) = self.addr(i);
3,548,967✔
99
        self.set_by_addr(block, bit, value);
3,548,967✔
100
    }
3,548,967✔
101

102
    fn set_slice_mut(&mut self, _: usize, _: usize, _: u64) {
×
103
        unimplemented!()
×
104
    }
105

106
    fn rc(&self) -> DnaString {
484✔
107
        let mut dna_string = DnaString::new();
484✔
108
        let rc = (0..self.len()).rev().map(|i| 3 - self.get(i));
225,948✔
109

484✔
110
        dna_string.extend(rc);
484✔
111
        dna_string
484✔
112
    }
484✔
113
}
114

115
impl Vmer for DnaString {
116
    fn new(len: usize) -> Self {
75,396✔
117
        Self::blank(len)
75,396✔
118
    }
75,396✔
119

120
    fn max_len() -> usize {
253✔
121
        <usize>::MAX
253✔
122
    }
253✔
123

124
    /// Get the kmer starting at position pos
125
    fn get_kmer<K: Kmer>(&self, pos: usize) -> K {
5,315,817✔
126
        assert!(self.len() - pos >= K::k());
5,315,817✔
127

128
        // Which block has the first base
129
        let (mut block, _) = self.addr(pos);
5,315,817✔
130

5,315,817✔
131
        // Where we are in the kmer
5,315,817✔
132
        let mut kmer_pos = 0;
5,315,817✔
133

5,315,817✔
134
        // Where are in the block
5,315,817✔
135
        let mut block_pos = pos % 32;
5,315,817✔
136

5,315,817✔
137
        let mut kmer = K::empty();
5,315,817✔
138

139
        while kmer_pos < K::k() {
15,009,608✔
140
            // get relevent bases for current block
9,693,791✔
141
            let nb = min(K::k() - kmer_pos, 32 - block_pos);
9,693,791✔
142

9,693,791✔
143
            let v = self.storage[block];
9,693,791✔
144
            let val = v << (2 * block_pos);
9,693,791✔
145
            kmer.set_slice_mut(kmer_pos, nb, val);
9,693,791✔
146

9,693,791✔
147
            // move to next block, move ahead in kmer.
9,693,791✔
148
            block += 1;
9,693,791✔
149
            kmer_pos += nb;
9,693,791✔
150
            // alway start a beginning of next block
9,693,791✔
151
            block_pos = 0;
9,693,791✔
152
        }
9,693,791✔
153

154
        kmer
5,315,817✔
155
    }
5,315,817✔
156
}
157

158
impl DnaString {
159
    /// Create an empty DNA string
160
    pub fn new() -> DnaString {
507,100✔
161
        DnaString {
507,100✔
162
            storage: Vec::new(),
507,100✔
163
            len: 0,
507,100✔
164
        }
507,100✔
165
    }
507,100✔
166

167
    /// Length of the sequence
168
    pub fn len(&self) -> usize {
7,362,389✔
169
        self.len
7,362,389✔
170
    }
7,362,389✔
171

172
    /// Create a new instance with a given capacity.
173
    pub fn with_capacity(n: usize) -> Self {
5,500,065✔
174
        let blocks = ((n * WIDTH) >> 6) + (if (n * WIDTH) & 0x3F > 0 { 1 } else { 0 });
5,500,065✔
175
        let storage = Vec::with_capacity(blocks);
5,500,065✔
176

5,500,065✔
177
        DnaString { storage, len: 0 }
5,500,065✔
178
    }
5,500,065✔
179

180
    /// Create a DnaString of length n initialized to all A's
181
    pub fn blank(n: usize) -> Self {
75,396✔
182
        let blocks = ((n * WIDTH) >> 6) + (if (n * WIDTH) & 0x3F > 0 { 1 } else { 0 });
75,396✔
183
        let storage = vec![0; blocks];
75,396✔
184

75,396✔
185
        DnaString { storage, len: n }
75,396✔
186
    }
75,396✔
187

188
    /// Create a DnaString corresponding to an ACGT-encoded str.
189
    pub fn from_dna_string(dna: &str) -> DnaString {
32✔
190
        let mut dna_string = DnaString {
32✔
191
            storage: Vec::new(),
32✔
192
            len: 0,
32✔
193
        };
32✔
194

32✔
195
        dna_string.extend(dna.chars().map(|c| base_to_bits(c as u8)));
827✔
196
        dna_string
32✔
197
    }
32✔
198

199
    /// Create a DnaString corresponding to an ACGT-encoded str.
200
    pub fn from_dna_only_string(dna: &str) -> Vec<DnaString> {
×
201
        let mut dna_vector: Vec<DnaString> = Vec::new();
×
202
        let mut dna_string = DnaString::new();
×
203

204
        for c in dna.chars() {
×
205
            match dna_only_base_to_bits(c as u8) {
×
206
                Some(bit) => {
×
207
                    dna_string.push(bit);
×
208
                }
×
209
                None => {
210
                    if !dna_string.is_empty() {
×
211
                        dna_vector.push(dna_string);
×
212
                        dna_string = DnaString::new();
×
213
                    }
×
214
                }
215
            }
216
        }
217
        if !dna_string.is_empty() {
×
218
            dna_vector.push(dna_string);
×
219
        }
×
220

221
        dna_vector
×
222
    }
×
223

224
    /// Create a DnaString from an ASCII ACGT-encoded byte slice.
225
    /// Non ACGT positions will be converted to 'A'
226
    pub fn from_acgt_bytes(bytes: &[u8]) -> DnaString {
5,500,034✔
227
        let mut dna_string = DnaString::with_capacity(bytes.len());
5,500,034✔
228

5,500,034✔
229
        // Accelerated avx2 mode. Should run on most machines made since 2013.
5,500,034✔
230
        #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
5,500,034✔
231
        {
5,500,034✔
232
            if is_x86_feature_detected!("avx2") {
5,500,034✔
233
                for chunk in bytes.chunks(32) {
8,500,044✔
234
                    if chunk.len() == 32 {
8,500,044✔
235
                        let (conv_chunk, _) = unsafe { crate::bitops_avx2::convert_bases(chunk) };
4,000,015✔
236
                        let packed = unsafe { crate::bitops_avx2::pack_32_bases(conv_chunk) };
4,000,015✔
237
                        dna_string.storage.push(packed);
4,000,015✔
238
                    } else {
4,500,029✔
239
                        let b = chunk.iter().map(|c| base_to_bits(*c));
73,000,381✔
240
                        dna_string.extend(b);
4,500,029✔
241
                    }
4,500,029✔
242
                }
243

244
                dna_string.len = bytes.len();
5,500,034✔
245
                return dna_string;
5,500,034✔
246
            }
×
247
        }
×
248

×
249
        let b = bytes.iter().map(|c| base_to_bits(*c));
×
250
        dna_string.extend(b);
×
251
        dna_string
×
252
    }
5,500,034✔
253

254
    /// Create a DnaString from an ASCII ACGT-encoded byte slice.
255
    /// Will return `None` if there are ambiguous bases in the DnaString
256
    pub fn from_acgt_bytes_checked(bytes: &[u8]) -> Result<DnaString, AmbiguousBasesError> {
20✔
257
        let mut dna_string = DnaString::with_capacity(bytes.len());
20✔
258

20✔
259
        // Accelerated avx2 mode. Should run on most machines made since 2013.
20✔
260
        #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
20✔
261
        {
20✔
262
            if is_x86_feature_detected!("avx2") {
20✔
263
                for chunk in bytes.chunks(32) {
27✔
264
                    if chunk.len() == 32 {
27✔
265
                        let (conv_chunk, correct) = unsafe { crate::bitops_avx2::convert_bases(chunk) };
15✔
266
                        if !correct { return Err(AmbiguousBasesError {  }) }
15✔
267
                        let packed = unsafe { crate::bitops_avx2::pack_32_bases(conv_chunk) };
9✔
268
                        dna_string.storage.push(packed);
9✔
269
                    } else {
270
                        let (b, corrects): (Vec<u8>, Vec<bool>) = chunk.iter().map(|c| base_to_bits_checked(*c)).collect();
237✔
271
                        let correct = !corrects.iter().contains(&false);
12✔
272
                        if !correct { return Err(AmbiguousBasesError {  }) }
12✔
273
                        dna_string.extend(b.into_iter());
8✔
274
                    }
275
                }
276

277
                dna_string.len = bytes.len();
10✔
278
                return Ok(dna_string);
10✔
NEW
279
            }
×
NEW
280
        }
×
NEW
281
        
×
NEW
282
        let (b, corrects): (Vec<u8>, Vec<bool>) = bytes.iter().map(|c| base_to_bits_checked(*c)).collect();
×
NEW
283
        let correct = corrects.iter().contains(&false);
×
NEW
284
        if !correct { return Err(AmbiguousBasesError {  }) }
×
NEW
285
        dna_string.extend(b.into_iter());
×
NEW
286

×
NEW
287
        Ok(dna_string)
×
288
    }
20✔
289

290
    /// Create a DnaString from an ACGT-encoded byte slice,
291
    /// Non ACGT positions will be converted to repeatable random base determined
292
    /// by a hash of the read name and the position within the string.
293
    pub fn from_acgt_bytes_hashn(bytes: &[u8], read_name: &[u8]) -> DnaString {
×
294
        let mut hasher = DefaultHasher::new();
×
295
        read_name.hash(&mut hasher);
×
296

×
297
        let mut dna_string = DnaString::with_capacity(bytes.len());
×
298

299
        for (pos, c) in bytes.iter().enumerate() {
×
300
            let v = match c {
×
301
                b'A' | b'a' => 0u8,
×
302
                b'C' | b'c' => 1u8,
×
303
                b'G' | b'g' => 2u8,
×
304
                b'T' | b't' => 3u8,
×
305
                _ => {
306
                    let mut hasher_clone = hasher.clone();
×
307
                    pos.hash(&mut hasher_clone);
×
308
                    (hasher_clone.finish() % 4) as u8
×
309
                }
310
            };
311

312
            dna_string.push(v);
×
313
        }
314

315
        dna_string
×
316
    }
×
317

318
    /// Create a DnaString from a 0-4 encoded byte slice
319
    pub fn from_bytes(bytes: &[u8]) -> DnaString {
5,014✔
320
        let mut dna_string = DnaString {
5,014✔
321
            storage: Vec::new(),
5,014✔
322
            len: 0,
5,014✔
323
        };
5,014✔
324

5,014✔
325
        dna_string.extend(bytes.iter().cloned());
5,014✔
326
        dna_string
5,014✔
327
    }
5,014✔
328

329
    /// Convert sequence to a Vector of 0-4 encoded bytes
330
    pub fn to_bytes(&self) -> Vec<u8> {
×
331
        self.iter().collect()
×
332
    }
×
333

334
    /// Convert sequence to a Vector of ascii-encoded bytes
335
    pub fn to_ascii_vec(&self) -> Vec<u8> {
×
336
        self.iter().map(bits_to_ascii).collect()
×
337
    }
×
338

339
    /// Append a 0-4 encoded base.
340
    #[inline]
341
    pub fn push(&mut self, value: u8) {
68,934,864✔
342
        let (block, bit) = self.addr(self.len);
68,934,864✔
343
        if bit == 0 && block >= self.storage.len() {
68,934,864✔
344
            self.storage.push(0);
2,375,431✔
345
        }
66,559,433✔
346
        self.set_by_addr(block, bit, value);
68,934,864✔
347
        self.len += 1;
68,934,864✔
348
    }
68,934,864✔
349

350
    pub fn extend(&mut self, mut bytes: impl Iterator<Item = u8>) {
4,505,567✔
351
        // fill the last incomplete u64 block
352
        while self.len % 32 != 0 {
4,505,567✔
353
            match bytes.next() {
×
354
                Some(b) => self.push(b),
×
355
                None => return,
×
356
            }
357
        }
358

359
        let mut bytes = bytes.peekable();
4,505,567✔
360

361
        // chunk the remaining items into groups of at most 32 and handle them together
362
        while bytes.peek().is_some() {
9,093,461✔
363
            let mut val: u64 = 0;
4,587,894✔
364
            let mut offset = 62;
4,587,894✔
365
            let mut n_added = 0;
4,587,894✔
366

367
            for _ in 0..32 {
80,313,929✔
368
                if let Some(b) = bytes.next() {
80,231,423✔
369
                    assert!(b < 4);
75,726,035✔
370
                    val |= (b as u64) << offset;
75,726,035✔
371
                    offset -= 2;
75,726,035✔
372
                    n_added += 1;
75,726,035✔
373
                } else {
374
                    break;
4,505,388✔
375
                }
376
            }
377

378
            self.storage.push(val);
4,587,894✔
379
            self.len += n_added;
4,587,894✔
380
        }
381
    }
4,505,567✔
382

383
    /// Push 0-4 encoded bases from a byte array.
384
    ///
385
    /// # Arguments
386
    /// `bytes`: byte array to read values from
387
    /// `seq_length`: how many values to read from the byte array. Note that this
388
    /// is number of values not number of elements of the byte array.
389
    pub fn push_bytes(&mut self, bytes: &[u8], seq_length: usize) {
9✔
390
        assert!(
9✔
391
            seq_length <= bytes.len() * 8 / WIDTH,
9✔
392
            "Number of elements to push exceeds array length"
×
393
        );
394

395
        for i in 0..seq_length {
66✔
396
            let byte_index = (i * WIDTH) / 8;
66✔
397
            let byte_slot = (i * WIDTH) % 8;
66✔
398

66✔
399
            let v = bytes[byte_index];
66✔
400
            let bits = (v >> byte_slot) & (MASK as u8);
66✔
401

66✔
402
            self.push(bits);
66✔
403
        }
66✔
404
    }
9✔
405

406
    /// Iterate over stored values (values will be unpacked into bytes).
407
    pub fn iter(&self) -> DnaStringIter<'_> {
17,046✔
408
        DnaStringIter {
17,046✔
409
            dna_string: self,
17,046✔
410
            i: 0,
17,046✔
411
        }
17,046✔
412
    }
17,046✔
413

414
    /// Clear the sequence.
415
    pub fn clear(&mut self) {
×
416
        self.storage.clear();
×
417
        self.len = 0;
×
418
    }
×
419

420
    #[inline(always)]
421
    fn get_by_addr(&self, block: usize, bit: usize) -> u8 {
249,224,400✔
422
        ((self.storage[block] >> (62 - bit)) & MASK) as u8
249,224,400✔
423
    }
249,224,400✔
424

425
    #[inline(always)]
426
    fn set_by_addr(&mut self, block: usize, bit: usize, value: u8) {
72,483,831✔
427
        let mask = MASK << (62 - bit);
72,483,831✔
428
        self.storage[block] |= mask;
72,483,831✔
429
        self.storage[block] ^= mask;
72,483,831✔
430
        self.storage[block] |= (value as u64 & MASK) << (62 - bit);
72,483,831✔
431
    }
72,483,831✔
432

433
    #[inline(always)]
434
    fn addr(&self, i: usize) -> (usize, usize) {
327,024,048✔
435
        let k = i * WIDTH;
327,024,048✔
436
        (k / BLOCK_BITS, k % BLOCK_BITS)
327,024,048✔
437
    }
327,024,048✔
438

439
    pub fn is_empty(&self) -> bool {
×
440
        self.len == 0
×
441
    }
×
442

443
    /// Get the length `k` prefix of the DnaString
444
    pub fn prefix(&self, k: usize) -> DnaStringSlice<'_> {
5✔
445
        assert!(k <= self.len, "Prefix size exceeds number of elements.");
5✔
446
        DnaStringSlice {
5✔
447
            dna_string: self,
5✔
448
            start: 0,
5✔
449
            length: k,
5✔
450
            is_rc: false,
5✔
451
        }
5✔
452
    }
5✔
453

454
    /// Get the length `k` suffix of the DnaString
455
    pub fn suffix(&self, k: usize) -> DnaStringSlice<'_> {
5✔
456
        assert!(k <= self.len, "Suffix size exceeds number of elements.");
5✔
457

458
        DnaStringSlice {
5✔
459
            dna_string: self,
5✔
460
            start: self.len() - k,
5✔
461
            length: k,
5✔
462
            is_rc: false,
5✔
463
        }
5✔
464
    }
5✔
465

466
    /// Get slice containing the interval [`start`, `end`) of `self`
467
    pub fn slice(&self, start: usize, end: usize) -> DnaStringSlice<'_> {
10,008✔
468
        assert!(start <= self.len, "coordinate exceeds number of elements.");
10,008✔
469
        assert!(end <= self.len, "coordinate exceeds number of elements.");
10,008✔
470

471
        DnaStringSlice {
10,008✔
472
            dna_string: self,
10,008✔
473
            start,
10,008✔
474
            length: end - start,
10,008✔
475
            is_rc: false,
10,008✔
476
        }
10,008✔
477
    }
10,008✔
478

479
    /// Create a fresh DnaString containing the reverse of `self`
480
    pub fn reverse(&self) -> DnaString {
2✔
481
        let values: Vec<u8> = self.iter().collect();
2✔
482
        let mut dna_string = DnaString::new();
2✔
483
        for v in values.iter().rev() {
8✔
484
            dna_string.push(*v);
8✔
485
        }
8✔
486
        dna_string
2✔
487
    }
2✔
488

489
    /// Compute Hamming distance between this DnaString and another DnaString. The two strings must have the same length.
490
    pub fn hamming_distance(&self, other: &DnaString) -> usize {
×
491
        ndiffs(self, other)
×
492
    }
×
493

494
    // pub fn complement(&self) -> DnaString {
495
    //    assert!(self.width == 2, "Complement only supported for 2bit encodings.");
496
    //    let values: Vec<u32> = Vec::with_capacity(self.len());
497
    //    for i, v in self.storage.iter() {
498
    //        values[i] = v;
499
    //    }
500
    //    values[values.len() - 1] =
501
    // }
502
}
503

504
impl fmt::Display for DnaString {
505
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
10✔
506
        for v in self.iter() {
68✔
507
            write!(f, "{}", bits_to_base(v))?;
68✔
508
        }
509
        Ok(())
10✔
510
    }
10✔
511
}
512

513
impl fmt::Debug for DnaString {
514
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
30✔
515
        let mut s = String::new();
30✔
516
        for pos in 0..self.len() {
1,702✔
517
            s.push(bits_to_base(self.get(pos)))
1,702✔
518
        }
519

520
        write!(f, "{}", s)
30✔
521
    }
30✔
522
}
523

524
impl Default for DnaString {
525
    fn default() -> Self {
×
526
        Self::new()
×
527
    }
×
528
}
529

530
/// Iterator over values of a DnaStringoded sequence (values will be unpacked into bytes).
531
pub struct DnaStringIter<'a> {
532
    dna_string: &'a DnaString,
533
    i: usize,
534
}
535

536
impl Iterator for DnaStringIter<'_> {
537
    type Item = u8;
538

539
    fn next(&mut self) -> Option<u8> {
1,205,209✔
540
        if self.i < self.dna_string.len() {
1,205,209✔
541
            let value = self.dna_string.get(self.i);
1,188,163✔
542
            self.i += 1;
1,188,163✔
543
            Some(value)
1,188,163✔
544
        } else {
545
            None
17,046✔
546
        }
547
    }
1,205,209✔
548
}
549

550
impl<'a> IntoIterator for &'a DnaString {
551
    type Item = u8;
552
    type IntoIter = DnaStringIter<'a>;
553

554
    fn into_iter(self) -> DnaStringIter<'a> {
17,013✔
555
        self.iter()
17,013✔
556
    }
17,013✔
557
}
558

559
/// count Hamming distance between 2 2-bit DNA packed u64s
560
#[inline]
561
fn count_diff_2_bit_packed(a: u64, b: u64) -> u32 {
3,863✔
562
    let bit_diffs = a ^ b;
3,863✔
563
    let two_bit_diffs = (bit_diffs | bit_diffs >> 1) & 0x5555555555555555;
3,863✔
564
    two_bit_diffs.count_ones()
3,863✔
565
}
3,863✔
566

567
/// Compute the number of base positions at which two DnaStrings differ, assuming
568
/// that they have the same length.
569
pub fn ndiffs(b1: &DnaString, b2: &DnaString) -> usize {
4✔
570
    assert_eq!(b1.len(), b2.len());
4✔
571
    let mut diffs = 0;
4✔
572
    let (s1, s2) = (&b1.storage, &b2.storage);
4✔
573
    for i in 0..s1.len() {
8✔
574
        diffs += count_diff_2_bit_packed(s1[i], s2[i])
8✔
575
    }
576
    diffs as usize
4✔
577
}
4✔
578

579
/// An immutable slice into a DnaString
580
#[derive(Clone)]
581
pub struct DnaStringSlice<'a> {
582
    pub dna_string: &'a DnaString,
583
    pub start: usize,
584
    pub length: usize,
585
    pub is_rc: bool,
586
}
587

588
impl PartialEq for DnaStringSlice<'_> {
589
    fn eq(&self, other: &DnaStringSlice) -> bool {
1✔
590
        if other.length != self.length {
1✔
591
            return false;
×
592
        }
1✔
593
        for i in 0..self.length {
3✔
594
            if self.get(i) != other.get(i) {
3✔
595
                return false;
×
596
            }
3✔
597
        }
598
        true
1✔
599
    }
1✔
600
}
601
impl Eq for DnaStringSlice<'_> {}
602

603
impl<'a> Mer for DnaStringSlice<'a> {
604
    #[inline(always)]
605
    fn len(&self) -> usize {
5,918,458✔
606
        self.length
5,918,458✔
607
    }
5,918,458✔
608

609
    fn is_empty(&self) -> bool {
×
610
        self.length == 0
×
611
    }
×
612

613
    /// Get the base at position `i`.
614
    #[inline(always)]
615
    fn get(&self, i: usize) -> u8 {
8,506,869✔
616
        if !self.is_rc {
8,506,869✔
617
            self.dna_string.get(i + self.start)
8,034,442✔
618
        } else {
619
            crate::complement(self.dna_string.get(self.start + self.length - 1 - i))
472,427✔
620
        }
621
    }
8,506,869✔
622

623
    /// Set the base as position `i`.
624
    fn set_mut(&mut self, _: usize, _: u8) {
×
625
        unimplemented!()
×
626
        //debug_assert!(i < self.length);
627
        //self.dna_string.set(i + self.start, value);
628
    }
629

630
    fn set_slice_mut(&mut self, _: usize, _: usize, _: u64) {
×
631
        unimplemented!();
×
632
    }
633

634
    fn rc(&self) -> DnaStringSlice<'a> {
305,578✔
635
        DnaStringSlice {
305,578✔
636
            dna_string: self.dna_string,
305,578✔
637
            start: self.start,
305,578✔
638
            length: self.length,
305,578✔
639
            is_rc: !self.is_rc,
305,578✔
640
        }
305,578✔
641
    }
305,578✔
642
}
643

644
impl Vmer for DnaStringSlice<'_> {
645
    fn new(_: usize) -> Self {
×
646
        unimplemented!()
×
647
    }
648

649
    fn max_len() -> usize {
×
NEW
650
        <usize>::MAX
×
651
    }
×
652

653
    /// Get the kmer starting at position pos
654
    fn get_kmer<K: Kmer>(&self, pos: usize) -> K {
4,894,412✔
655
        debug_assert!(pos + K::k() <= self.length);
4,894,412✔
656
        if !self.is_rc {
4,894,412✔
657
            self.dna_string.get_kmer(self.start + pos)
4,894,408✔
658
        } else {
659
            let k = self
4✔
660
                .dna_string
4✔
661
                .get_kmer(self.start + self.length - K::k() - pos);
4✔
662
            K::rc(&k)
4✔
663
        }
664
    }
4,894,412✔
665
}
666

667
impl DnaStringSlice<'_> {
668
    pub fn is_palindrome(&self) -> bool {
×
669
        unimplemented!();
×
670
    }
671

672
    pub fn bytes(&self) -> Vec<u8> {
×
673
        let mut v = Vec::with_capacity(self.length);
×
674
        for pos in 0..self.length {
×
675
            v.push(self.get(pos));
×
676
        }
×
677
        v
×
678
    }
×
679

680
    pub fn ascii(&self) -> Vec<u8> {
×
681
        let mut v = Vec::with_capacity(self.length);
×
682
        for pos in 0..self.length {
×
683
            v.push(bits_to_ascii(self.get(pos)));
×
684
        }
×
685
        v
×
686
    }
×
687

688
    pub fn to_dna_string(&self) -> String {
12✔
689
        let mut dna: String = String::with_capacity(self.length);
12✔
690
        for pos in 0..self.length {
666✔
691
            dna.push(bits_to_base(self.get(pos)));
666✔
692
        }
666✔
693
        dna
12✔
694
    }
12✔
695

696
    pub fn to_owned(&self) -> DnaString {
10✔
697
        // FIXME make this faster
10✔
698
        let mut be = DnaString::with_capacity(self.length);
10✔
699
        for pos in 0..self.length {
70✔
700
            be.push(self.get(pos));
70✔
701
        }
70✔
702

703
        be
10✔
704
    }
10✔
705
    /// Get slice containing the interval [`start`, `end`) of `self`
706
    pub fn slice(&self, start: usize, end: usize) -> DnaStringSlice<'_> {
2✔
707
        assert!(
2✔
708
            start <= self.length,
2✔
709
            "coordinate exceeds number of elements."
×
710
        );
711
        assert!(end <= self.length, "coordinate exceeds number of elements.");
2✔
712
        assert!(end >= start, "invalid interval");
2✔
713

714
        if !self.is_rc {
2✔
715
            DnaStringSlice {
1✔
716
                dna_string: self.dna_string,
1✔
717
                start: self.start + start,
1✔
718
                length: end - start,
1✔
719
                is_rc: self.is_rc,
1✔
720
            }
1✔
721
        } else {
722
            // remap coords for RC
723
            let new_start = self.start + self.length - end;
1✔
724
            let new_length = end - start;
1✔
725

1✔
726
            DnaStringSlice {
1✔
727
                dna_string: self.dna_string,
1✔
728
                start: new_start,
1✔
729
                length: new_length,
1✔
730
                is_rc: self.is_rc,
1✔
731
            }
1✔
732
        }
733
    }
2✔
734

735
    /// Compute the Hamming distance between this DNA string and another
736
    pub fn hamming_dist(&self, other: &DnaStringSlice) -> u32 {
5,000✔
737
        use crate::kmer::Kmer32;
738
        assert_eq!(self.len(), other.len());
5,000✔
739

740
        let mut ndiffs = 0;
5,000✔
741

5,000✔
742
        let whole_blocks = self.len() >> 5;
5,000✔
743

744
        // iterate over the whole K=32 blocks
745
        for block in (0..whole_blocks).step_by(32) {
5,000✔
746
            let b1: Kmer32 = self.get_kmer(block);
3,855✔
747
            let b2: Kmer32 = self.get_kmer(block);
3,855✔
748
            ndiffs += count_diff_2_bit_packed(b1.to_u64(), b2.to_u64());
3,855✔
749
        }
3,855✔
750

751
        // iterate over trailing bases
752
        for pos in (whole_blocks >> 5)..self.len() {
831,037✔
753
            if self.get(pos) != other.get(pos) {
831,037✔
754
                ndiffs += 1;
110,831✔
755
            }
720,206✔
756
        }
757

758
        ndiffs
5,000✔
759
    }
5,000✔
760
}
761

762
impl fmt::Display for DnaStringSlice<'_> {
763
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
×
764
        for pos in 0..self.length {
×
765
            write!(f, "{}", bits_to_base(self.get(pos)))?;
×
766
        }
767
        Ok(())
×
768
    }
×
769
}
770

771
impl fmt::Debug for DnaStringSlice<'_> {
772
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
4✔
773
        let mut s = String::new();
4✔
774
        if self.length < 256 {
4✔
775
            for pos in self.start..(self.start + self.length) {
200✔
776
                s.push(bits_to_base(self.dna_string.get(pos)))
200✔
777
            }
778
            write!(f, "{}", s)
4✔
779
        } else {
780
            write!(
×
781
                f,
×
782
                "start: {}, len: {}, is_rc: {}",
×
783
                self.start, self.length, self.is_rc
×
784
            )
×
785
        }
786
    }
4✔
787
}
788

789
impl<'a> IntoIterator for &'a DnaStringSlice<'a> {
790
    type Item = u8;
791
    type IntoIter = MerIter<'a, DnaStringSlice<'a>>;
792

793
    fn into_iter(self) -> Self::IntoIter {
4,508✔
794
        self.iter()
4,508✔
795
    }
4,508✔
796
}
797

798
/// Container for many distinct sequences, concatenated into a single DnaString.  Each
799
/// sequence is accessible by index as a DnaStringSlice.
800
#[derive(Debug, Default, Clone, Serialize, Deserialize)]
801
pub struct PackedDnaStringSet {
802
    pub sequence: DnaString,
803
    pub start: Vec<usize>,
804
    pub length: Vec<u32>,
805
}
806

807
impl PackedDnaStringSet {
808
    /// Create an empty `PackedDnaStringSet`
809
    pub fn new() -> Self {
2,263✔
810
        PackedDnaStringSet {
2,263✔
811
            sequence: DnaString::new(),
2,263✔
812
            start: Vec::new(),
2,263✔
813
            length: Vec::new(),
2,263✔
814
        }
2,263✔
815
    }
2,263✔
816

817
    /// Get a `DnaStringSlice` containing `i`th sequence in the set
818
    pub fn get(&self, i: usize) -> DnaStringSlice {
5,725,354✔
819
        DnaStringSlice {
5,725,354✔
820
            dna_string: &self.sequence,
5,725,354✔
821
            start: self.start[i],
5,725,354✔
822
            length: self.length[i] as usize,
5,725,354✔
823
            is_rc: false,
5,725,354✔
824
        }
5,725,354✔
825
    }
5,725,354✔
826

827
    /// Get a `DnaStringSlice` containing `i`th sequence in the set
828
    pub fn slice(&self, i: usize, start: usize, end: usize) -> DnaStringSlice {
×
829
        assert!(start <= self.length[i] as usize);
×
830
        assert!(end <= self.length[i] as usize);
×
831

832
        DnaStringSlice {
×
833
            dna_string: &self.sequence,
×
834
            start: self.start[i] + start,
×
835
            length: end - start,
×
836
            is_rc: false,
×
837
        }
×
838
    }
×
839

840
    /// Number of sequences in the set
841
    pub fn len(&self) -> usize {
242,042✔
842
        self.start.len()
242,042✔
843
    }
242,042✔
844

845
    pub fn is_empty(&self) -> bool {
1✔
846
        self.start.is_empty()
1✔
847
    }
1✔
848

849
    pub fn add<R: Borrow<u8>, S: IntoIterator<Item = R>>(&mut self, sequence: S) {
673,235✔
850
        let start = self.sequence.len();
673,235✔
851
        self.start.push(start);
673,235✔
852

673,235✔
853
        let mut length = 0;
673,235✔
854
        for b in sequence {
23,035,448✔
855
            self.sequence.push(*b.borrow());
22,362,213✔
856
            length += 1;
22,362,213✔
857
        }
22,362,213✔
858
        self.length.push(length as u32);
673,235✔
859
        //debug!("add to sequence for loop {:?} iterations (pr seq len)", length);
673,235✔
860
    }
673,235✔
861
}
862

863
#[derive(Debug, Clone, PartialEq)]
864
pub struct AmbiguousBasesError {}
865

866
impl Error for AmbiguousBasesError {}
867

868
impl Display for AmbiguousBasesError {
NEW
869
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
×
NEW
870
        write!(f, "ambiguous base found in read")
×
NEW
871
    }
×
872
}
873

874
#[cfg(test)]
875
mod tests {
876
    use super::*;
877
    use crate::kmer::IntKmer;
878
    use crate::kmer::Kmer4;
879
    use crate::test;
880
    use rand::{self, Rng};
881

882
    fn hamming_dist_slow(s1: &DnaStringSlice, s2: &DnaStringSlice) -> u32 {
5,000✔
883
        assert_eq!(s1.len(), s2.len());
5,000✔
884

885
        let mut ndiff = 0;
5,000✔
886
        for pos in 0..s1.len() {
831,037✔
887
            if s1.get(pos) != s2.get(pos) {
831,037✔
888
                ndiff += 1;
110,831✔
889
            }
720,206✔
890
        }
891

892
        ndiff
5,000✔
893
    }
5,000✔
894

895
    /// Randomly mutate each base with probability `p`
896
    pub fn edit_dna_string(string: &mut DnaString, p: f64, r: &mut impl Rng) {
5,000✔
897
        for pos in 0..string.len() {
2,497,500✔
898
            if r.gen_range(0.0, 1.0) < p {
2,497,500✔
899
                let base = test::random_base(r);
249,445✔
900
                string.set_mut(pos, base)
249,445✔
901
            }
2,248,055✔
902
        }
903
    }
5,000✔
904

905
    fn random_dna_string(len: usize) -> DnaString {
5,000✔
906
        let bytes = test::random_dna(len);
5,000✔
907
        DnaString::from_bytes(&bytes)
5,000✔
908
    }
5,000✔
909

910
    fn random_dna_string_pair(len: usize) -> (DnaString, DnaString) {
5,000✔
911
        let s1 = random_dna_string(len);
5,000✔
912
        let mut s2 = s1.clone();
5,000✔
913

5,000✔
914
        if rand::thread_rng().gen_bool(0.1) {
5,000✔
915
            s2 = s2.rc();
464✔
916
        }
4,536✔
917

918
        edit_dna_string(&mut s2, 0.1, &mut rand::thread_rng());
5,000✔
919
        (s1, s2)
5,000✔
920
    }
5,000✔
921

922
    fn random_slice(len: usize) -> (usize, usize) {
5,000✔
923
        if len == 0 {
5,000✔
924
            return (0, 0);
5✔
925
        }
4,995✔
926
        let mut r = rand::thread_rng();
4,995✔
927
        let a = (rand::RngCore::next_u64(&mut r) as usize) % len;
4,995✔
928
        let b = (rand::RngCore::next_u64(&mut r) as usize) % len;
4,995✔
929
        (std::cmp::min(a, b), std::cmp::max(a, b))
4,995✔
930
    }
5,000✔
931

932
    #[test]
933
    fn dnastringslice_get_kmer() {
1✔
934
        let seq = DnaString::from_dna_string("ACGGTAC");
1✔
935
        let seqrc = DnaString::from_dna_string("GTACCGT");
1✔
936
        let rcslice = seq.slice(0, 7).rc();
1✔
937
        let slice = seqrc.slice(0, 7);
1✔
938
        for i in 0..=3 {
5✔
939
            // The kmer in a slice should be the kmer of the sequencing represented
940
            // by that slice, regardless of whether the backing DnaString is RC or not.
941
            assert_eq!(slice.get_kmer::<Kmer4>(i), rcslice.get_kmer::<Kmer4>(i));
4✔
942
        }
943
    }
1✔
944
    #[test]
945
    fn dnastringslice_slice() {
1✔
946
        let seq = DnaString::from_dna_string("ACGGTAC");
1✔
947
        let seqrc = DnaString::from_dna_string("GTACCGT");
1✔
948
        let rcslice = seq.slice(0, 7).rc();
1✔
949
        let slice = seqrc.slice(0, 7);
1✔
950
        // The fact that a slice is backed by a DnaStringSlice that is
1✔
951
        // the rc of the slice sequence shouldn't matter.
1✔
952
        assert_eq!(rcslice.slice(1, 4), slice.slice(1, 4));
1✔
953
    }
1✔
954

955
    #[test]
956
    fn test_slice_hamming_dist() {
1✔
957
        for len in 0..1000 {
1,001✔
958
            for _ in 0..5 {
6,000✔
959
                let (s1, s2) = random_dna_string_pair(len);
5,000✔
960
                let (start, end) = random_slice(len);
5,000✔
961
                let slc1 = s1.slice(start, end);
5,000✔
962
                let slc2 = s2.slice(start, end);
5,000✔
963

5,000✔
964
                let validation_dist = hamming_dist_slow(&slc1, &slc2);
5,000✔
965
                let test_dist = slc1.hamming_dist(&slc2);
5,000✔
966
                assert_eq!(validation_dist, test_dist);
5,000✔
967
            }
968
        }
969
    }
1✔
970

971
    #[test]
972
    fn test_dna_string() {
1✔
973
        let mut dna_string = DnaString::with_capacity(1000);
1✔
974
        assert_eq!(dna_string.len(), 0);
1✔
975
        dna_string.push(0);
1✔
976
        dna_string.push(2);
1✔
977
        dna_string.push(1);
1✔
978
        println!("{:?}", dna_string);
1✔
979
        let mut values: Vec<u8> = dna_string.iter().collect();
1✔
980
        assert_eq!(values, [0, 2, 1]);
1✔
981
        dna_string.set_mut(1, 3);
1✔
982
        values = dna_string.iter().collect();
1✔
983
        assert_eq!(values, [0, 3, 1]);
1✔
984
    }
1✔
985

986
    #[test]
987
    fn test_push_bytes() {
1✔
988
        let in_values: Vec<u8> = vec![2, 20];
1✔
989

1✔
990
        let mut dna_string = DnaString::new();
1✔
991
        dna_string.push_bytes(&in_values, 8);
1✔
992
        // Contents should be [01000000, 00101000]
1✔
993
        let values: Vec<u8> = dna_string.iter().collect();
1✔
994
        assert_eq!(values, [2, 0, 0, 0, 0, 1, 1, 0]);
1✔
995

996
        let mut dna_string = DnaString::new();
1✔
997
        dna_string.push_bytes(&in_values, 2);
1✔
998
        // Contents should be 01000000
1✔
999
        let values: Vec<u8> = dna_string.iter().collect();
1✔
1000
        assert_eq!(values, [2, 0]);
1✔
1001
    }
1✔
1002

1003
    #[test]
1004
    fn test_from_dna_string() {
1✔
1005
        dna_string_test("");
1✔
1006
        dna_string_test("A");
1✔
1007
        dna_string_test("C");
1✔
1008
        dna_string_test("G");
1✔
1009
        dna_string_test("T");
1✔
1010

1✔
1011
        dna_string_test("GC");
1✔
1012
        dna_string_test("ATA");
1✔
1013

1✔
1014
        dna_string_test("ACGTACGT");
1✔
1015
        dna_string_test("ACGTAAAAAAAAAATTATATAACGT");
1✔
1016
        dna_string_test("AACGTAAAAAAAAAATTATATAACGT");
1✔
1017
    }
1✔
1018

1019
    fn dna_string_test(dna: &str) {
10✔
1020
        let dna_string_a = DnaString::from_dna_string(dna);
10✔
1021
        let dna_string = DnaString::from_acgt_bytes(dna.as_bytes());
10✔
1022
        assert_eq!(dna_string_a, dna_string);
10✔
1023

1024
        let rc = dna_string_a.rc();
10✔
1025
        let rc2 = rc.rc();
10✔
1026
        assert_eq!(dna_string_a, rc2);
10✔
1027

1028
        assert_eq!(dna_string.iter().count(), dna.len());
10✔
1029

1030
        assert_eq!(dna_string.len, dna.len());
10✔
1031

1032
        let dna_cp = dna_string.to_string();
10✔
1033
        assert_eq!(dna, dna_cp);
10✔
1034
    }
10✔
1035

1036
    #[test]
1037
    fn test_dna_string_ambig() {
1✔
1038
        let dna = [
1✔
1039
            "TTTTTTTTTTTTTTTTTTTTTTTT",
1✔
1040
            "NAGCGGAGATTATTCACGAGCATCGCGTAC",
1✔
1041
            "GATCGATGCATGCTAGN",
1✔
1042
            "ACGTAAAAAAAAAATTATATAACGTACGTAAAAAAAAAATTATATAACGTAACGTAAAAANAAAAATTATANTAACGT",
1✔
1043
            "AGCTAGCTAGCTGACTGAGCGACTGA",
1✔
1044
            "AGCTAGCTAGCTGACTGAGCGACTGACGGATC",
1✔
1045
            "GCATCGAGCATGCTACGATGCGACGATCGTACGATCGTACGATC",
1✔
1046
            "ACGATCGNATGCTAGCTGATCGGCGACGATCGATGCTAGCTGATCGTAGCTGACTGATCGATCG",
1✔
1047
            "ACGATCGATGCTAGCTGATCGGCGACGATCGATGCTAGCTGATCGTAGCTGACTGATCGATCGJHSJDSDHKAJSHDK",
1✔
1048
        ];
1✔
1049

1✔
1050
        let comp_unchecked = [
1✔
1051
            "TTTTTTTTTTTTTTTTTTTTTTTT",
1✔
1052
            "AAGCGGAGATTATTCACGAGCATCGCGTAC",
1✔
1053
            "GATCGATGCATGCTAGA",
1✔
1054
            "ACGTAAAAAAAAAATTATATAACGTACGTAAAAAAAAAATTATATAACGTAACGTAAAAAAAAAAATTATAATAACGT",
1✔
1055
            "AGCTAGCTAGCTGACTGAGCGACTGA",
1✔
1056
            "AGCTAGCTAGCTGACTGAGCGACTGACGGATC",
1✔
1057
            "GCATCGAGCATGCTACGATGCGACGATCGTACGATCGTACGATC",
1✔
1058
            "ACGATCGAATGCTAGCTGATCGGCGACGATCGATGCTAGCTGATCGTAGCTGACTGATCGATCG",
1✔
1059
            "ACGATCGATGCTAGCTGATCGGCGACGATCGATGCTAGCTGATCGTAGCTGACTGATCGATCGAAAAAAAAAAAAAAA",
1✔
1060
        ];
1✔
1061

1✔
1062
        let comp_checked = [
1✔
1063
            Ok("TTTTTTTTTTTTTTTTTTTTTTTT".to_string()),
1✔
1064
            Err(AmbiguousBasesError {}),
1✔
1065
            Err(AmbiguousBasesError {}),
1✔
1066
            Err(AmbiguousBasesError {}),
1✔
1067
            Ok("AGCTAGCTAGCTGACTGAGCGACTGA".to_string()),
1✔
1068
            Ok("AGCTAGCTAGCTGACTGAGCGACTGACGGATC".to_string()),
1✔
1069
            Ok("GCATCGAGCATGCTACGATGCGACGATCGTACGATCGTACGATC".to_string()),
1✔
1070
            Err(AmbiguousBasesError {}),
1✔
1071
            Err(AmbiguousBasesError {}),
1✔
1072
        ];
1✔
1073

1074
        for (i, seq) in dna.iter().enumerate() {
9✔
1075
            assert_eq!(format!("{:?}", DnaString::from_acgt_bytes(seq.as_bytes())), comp_unchecked[i]);
9✔
1076
            assert_eq!(DnaString::from_acgt_bytes_checked(seq.as_bytes()).map(|d| format!("{:?}", d)), comp_checked[i]);
9✔
1077
        }
1078
    }
1✔
1079

1080
    #[test]
1081
    fn test_prefix() {
1✔
1082
        let in_values: Vec<u8> = vec![2, 20];
1✔
1083
        let mut dna_string = DnaString::new();
1✔
1084
        dna_string.push_bytes(&in_values, 8);
1✔
1085
        // Contents should be [01000000, 00101000]
1✔
1086

1✔
1087
        let pref_dna_string = dna_string.prefix(0).to_owned();
1✔
1088
        assert_eq!(pref_dna_string.len(), 0);
1✔
1089

1090
        let pref_dna_string = dna_string.prefix(8).to_owned();
1✔
1091
        assert_eq!(pref_dna_string, dna_string);
1✔
1092

1093
        let pref_dna_string = dna_string.prefix(4).to_owned();
1✔
1094
        let values: Vec<u8> = pref_dna_string.iter().collect();
1✔
1095
        assert_eq!(values, [2, 0, 0, 0]);
1✔
1096

1097
        let pref_dna_string = dna_string.prefix(6).to_owned();
1✔
1098
        let values: Vec<u8> = pref_dna_string.iter().collect();
1✔
1099
        assert_eq!(values, [2, 0, 0, 0, 0, 1]);
1✔
1100

1101
        dna_string.push_bytes(&in_values, 8);
1✔
1102
        dna_string.push_bytes(&in_values, 8);
1✔
1103

1✔
1104
        let pref_dna_string = dna_string.prefix(17).to_owned();
1✔
1105
        let values: Vec<u8> = pref_dna_string.iter().collect();
1✔
1106
        assert_eq!(values, [2, 0, 0, 0, 0, 1, 1, 0, 2, 0, 0, 0, 0, 1, 1, 0, 2]);
1✔
1107
    }
1✔
1108

1109
    #[test]
1110
    fn test_suffix() {
1✔
1111
        let in_values: Vec<u8> = vec![2, 20];
1✔
1112
        let mut dna_string = DnaString::new();
1✔
1113
        dna_string.push_bytes(&in_values, 8);
1✔
1114
        // Contents should be [01000000, 00101000]
1✔
1115

1✔
1116
        let suf_dna_string = dna_string.suffix(0).to_owned();
1✔
1117
        assert_eq!(suf_dna_string.len(), 0);
1✔
1118

1119
        let suf_dna_string = dna_string.suffix(8).to_owned();
1✔
1120
        assert_eq!(suf_dna_string, dna_string);
1✔
1121

1122
        let suf_dna_string = dna_string.suffix(4).to_owned();
1✔
1123
        let values: Vec<u8> = suf_dna_string.iter().collect();
1✔
1124
        assert_eq!(values, [0, 1, 1, 0]);
1✔
1125

1126
        // 000101000000 64+256
1127
        let suf_dna_string = dna_string.suffix(6).to_owned();
1✔
1128
        let values: Vec<u8> = suf_dna_string.iter().collect();
1✔
1129
        assert_eq!(values, [0, 0, 0, 1, 1, 0]);
1✔
1130

1131
        dna_string.push_bytes(&in_values, 8);
1✔
1132
        dna_string.push_bytes(&in_values, 8);
1✔
1133

1✔
1134
        let suf_dna_string = dna_string.suffix(17).to_owned();
1✔
1135
        let values: Vec<u8> = suf_dna_string.iter().collect();
1✔
1136
        assert_eq!(values, [0, 2, 0, 0, 0, 0, 1, 1, 0, 2, 0, 0, 0, 0, 1, 1, 0]);
1✔
1137
    }
1✔
1138

1139
    #[test]
1140
    fn test_reverse() {
1✔
1141
        let in_values: Vec<u8> = vec![2, 20];
1✔
1142
        let mut dna_string = DnaString::new();
1✔
1143
        let rev_dna_string = dna_string.reverse();
1✔
1144
        assert_eq!(dna_string, rev_dna_string);
1✔
1145

1146
        dna_string.push_bytes(&in_values, 8);
1✔
1147
        // Contents should be 00010100 00000010
1✔
1148

1✔
1149
        let rev_dna_string = dna_string.reverse();
1✔
1150
        let values: Vec<u8> = rev_dna_string.iter().collect();
1✔
1151
        assert_eq!(values, [0, 1, 1, 0, 0, 0, 0, 2]);
1✔
1152
    }
1✔
1153

1154
    #[test]
1155
    fn test_kmers() {
1✔
1156
        const DNA: &str = "TGCATTAGAAAACTCCTTGCCTGTCAGCCCGACAGGTAGAAACTCATTAATCCACACATTGA\
1157
            CTCTATTTCAGGTAAATATGACGTCAACTCCTGCATGTTGAAGGCAGTGAGTGGCTGAAACAGCATCAAGGCGTGAAGGC";
1158
        let dna_string = DnaString::from_dna_string(DNA);
1✔
1159

1✔
1160
        let kmers: Vec<IntKmer<u64>> = dna_string.iter_kmers().collect();
1✔
1161
        kmer_test::<IntKmer<u64>>(&kmers, DNA, &dna_string);
1✔
1162
    }
1✔
1163

1164
    #[test]
1165
    fn test_ndiffs() {
1✔
1166
        let x1 = DnaString::from_dna_string("TGCATTAGAAAACTCCTTGCCTGTCTAGAAACTCATTAATCCACACATTGA");
1✔
1167
        let x2 = DnaString::from_dna_string("TGCATTAGTAAACTCCTTCGCTGTCTAGAAAATCATTAAGCCACACATTGA");
1✔
1168
        assert_eq!(ndiffs(&x1, &x2), 5);
1✔
1169

1170
        let x1 = DnaString::from_dna_string("TGCATT");
1✔
1171
        let x2 = DnaString::from_dna_string("TGCATT");
1✔
1172
        assert_eq!(ndiffs(&x1, &x2), 0);
1✔
1173

1174
        let x1 = DnaString::from_dna_string("");
1✔
1175
        let x2 = DnaString::from_dna_string("");
1✔
1176
        assert_eq!(ndiffs(&x1, &x2), 0);
1✔
1177

1178
        let x1 = DnaString::from_dna_string("TGCATTAGAAAACTCCTTGCCTGTCTAGAAACTCATTAATCCACACATTGA\
1✔
1179
            TGCATTAGAAAACTCCTTGCCTGTCTAGAAACTCATTAATCCACACATTGATGCATTAGAAAACTCCTTGCCTGTCTAGAAACTCATTAATCCACACATTGA");
1✔
1180
        let x2 = DnaString::from_dna_string("TGCATTAGTAAACTCCTTCGCTGTCTAGAAAATCATTAAGCCACACATTGA\
1✔
1181
            TGCATTAGTAAACTCCTTCGCTGTCTAGAAAATCATTAAGCCACACATTGATGCATTAGTAAACTCCTTCGCTGTCTAGAAAATCATTAAGCCACACATTGA");
1✔
1182
        assert_eq!(ndiffs(&x1, &x2), 15);
1✔
1183
    }
1✔
1184

1185
    #[test]
1186
    fn test_kmers_too_short() {
1✔
1187
        const DNA: &str = "TGCATTAGAA";
1188
        let dna_string = DnaString::from_dna_string(DNA);
1✔
1189

1✔
1190
        let kmers: Vec<IntKmer<u64>> = dna_string.iter_kmers().collect();
1✔
1191
        assert_eq!(kmers, Vec::default());
1✔
1192
    }
1✔
1193

1194
    fn kmer_test<K: Kmer>(kmers: &[K], dna: &str, dna_string: &DnaString) {
1✔
1195
        for i in 0..(dna.len() - K::k() + 1) {
111✔
1196
            assert_eq!(kmers[i].to_string(), &dna[i..(i + K::k())]);
111✔
1197
        }
1198

1199
        let last_kmer: K = dna_string.last_kmer();
1✔
1200
        assert_eq!(last_kmer.to_string(), &dna[(dna.len() - K::k())..]);
1✔
1201

1202
        for (idx, &k) in kmers.iter().enumerate() {
111✔
1203
            assert_eq!(k, dna_string.get_kmer(idx));
111✔
1204
        }
1205
    }
1✔
1206
}
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

© 2026 Coveralls, Inc