Skip to main content

bio_seq/
kmer.rs

1// Copyright 2021-2024 Jeff Knaggs
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//! Encoded sequences of static length
7//!
8//! Generally, the underlying storage type of `Kmer` should lend itself to optimisation. The default `Kmer` instance is packed into a `usize`, which can be efficiently `Copy`ed on the stack.
9//!
10//! `k * A::BITS` must fit in the storage type, e.g. `usize` (64 bits).
11//!
12//! ```
13//! use bio_seq::prelude::*;
14//!
15//! for (amino_kmer, amino_string) in Seq::<Amino>::try_from("SSLMNHKKL").unwrap()
16//!         .kmers::<3>()
17//!         .zip(["SSL", "SLM", "LMN", "MNH", "NHK", "HKK", "KKL"])
18//!     {
19//!         assert_eq!(amino_kmer, amino_string);
20//!     }
21//! ```
22//!
23//! Kmers can be copied from other sequence types:
24//!
25//! ```
26//! # use bio_seq::prelude::*;
27//! let kmer: Kmer<Dna, 8> = dna!("AGTTGGCA").try_into().unwrap();
28//! ```
29
30// storage types wider than usize are deliberately narrowed
31#![allow(clippy::cast_possible_truncation)]
32
33use crate::Bs;
34use crate::codec::{self, Codec};
35use crate::prelude::ParseBioError;
36use crate::seq::{Seq, SeqArray, SeqSlice};
37use crate::{
38    Complement, ComplementMut, Reverse, ReverseComplement, ReverseComplementMut, ReverseMut,
39};
40use bitvec::field::BitField;
41use bitvec::view::BitView;
42use core::fmt;
43use core::hash::{Hash, Hasher};
44use core::marker::PhantomData;
45use core::ops::Deref;
46use core::ptr;
47use core::str::FromStr;
48
49//#[cfg(target_feature(enable = "avx2,bmi2"))]
50//pub mod avx2;
51
52//#[cfg(target_arch = "wasm32")]
53//pub mod wasm;
54
55#[cfg(target_pointer_width = "64")]
56pub(crate) mod integral64;
57
58#[cfg(target_pointer_width = "32")]
59pub(crate) mod integral32;
60
61#[cfg(feature = "serde")]
62use serde_derive::{Deserialize, Serialize};
63
64const fn make_2bit_table() -> [u8; 256] {
65    let mut table = [0u8; 256];
66    let mut i: usize = 0;
67    while i < 256 {
68        let b0: u8 = (i as u8 & 0b11_00_00_00) >> 6;
69        let b1: u8 = (i as u8 & 0b00_11_00_00) >> 2;
70        let b2: u8 = (i as u8 & 0b00_00_11_00) << 2;
71        let b3: u8 = (i as u8 & 0b00_00_00_11) << 6;
72
73        table[i] = b3 | b2 | b1 | b0;
74        i += 1;
75    }
76    table
77}
78
79const REV_2BIT: [u8; 256] = make_2bit_table();
80
81pub(crate) mod sealed {
82    use crate::Bs;
83
84    pub trait KmerStorage: Copy + Clone + PartialEq + std::fmt::Debug {
85        const BITS: usize;
86        type BaN: AsRef<Bs> + AsMut<Bs>;
87
88        fn to_bitarray(self) -> Self::BaN;
89        fn from_bitslice(bs: &Bs) -> Self;
90
91        //        fn rotate_left(self, n: u32) -> Self;
92        //        fn rotate_right(self, n: u32) -> Self;
93
94        fn shiftr(&mut self, n: u32);
95
96        fn shiftl(&mut self, n: u32);
97
98        fn mask(&mut self, bits: usize);
99
100        fn complement(&mut self, mask: usize);
101        fn rev_blocks_2(&mut self);
102    }
103}
104
105pub trait KmerStorage: sealed::KmerStorage {}
106
107impl KmerStorage for usize {}
108
109impl KmerStorage for u64 {}
110
111impl KmerStorage for u128 {}
112
113/// By default k-mers are backed by `usize` and `Codec::BITS` * `K` must be <= 64 on 64-bit platforms
114///
115/// ```compile_fail
116/// # use bio_seq::prelude::*;
117/// // 40 * 2 bits does not fit in a usize
118/// let kmer: Kmer<Dna, 40> = "ACGTACGTACGTACGTACGTACGTACGTACGTACGTACGT".parse().unwrap();
119/// ```
120///
121/// ```
122/// # use bio_seq::prelude::*;
123/// // ..but 80 bits does fit into a u128
124/// let kmer: Kmer<Dna, 40, u128> = "ACGTACGTACGTACGTACGTACGTACGTACGTACGTACGT".parse().unwrap();
125/// assert_eq!(kmer.len(), 40);
126/// ```
127#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Copy, Clone)]
128#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
129#[repr(transparent)]
130pub struct Kmer<C: Codec, const K: usize, S: KmerStorage = usize> {
131    pub _p: PhantomData<C>,
132    pub bs: S,
133}
134
135impl<A: Codec, const K: usize, S: KmerStorage> Kmer<A, K, S> {
136    // This error message can be formatted with constants in nightly (const_format)
137    const _ASSERT_K: () = assert!(
138        K * A::BITS as usize <= S::BITS,
139        "`KmerStorage` not large enough for `Kmer`",
140    );
141
142    const _ASSERT_K_NONZERO: () = assert!(K > 0, "`K` must be greater than 0");
143
144    const fn assert_k() {
145        let () = Self::_ASSERT_K;
146        let () = Self::_ASSERT_K_NONZERO;
147    }
148
149    const BITS: usize = K * A::BITS as usize;
150
151    pub fn len(&self) -> usize {
152        K
153    }
154
155    pub fn is_empty(&self) -> bool {
156        // This is recommended by clippy since we have `len`
157        // Kmers are never empty if K > 0
158        false
159    }
160
161    pub fn rotated_left(&self, n: u32) -> Self {
162        let n: usize = (n as usize % K) * A::BITS as usize;
163        let mut ba = self.bs.to_bitarray();
164        let bs: &mut Bs = ba.as_mut();
165        bs[..Self::BITS].rotate_left(n);
166
167        Kmer {
168            _p: PhantomData,
169            bs: S::from_bitslice(&bs[..Self::BITS]),
170        }
171    }
172
173    pub fn rotated_right(&self, n: u32) -> Self {
174        let n: usize = (n as usize % K) * A::BITS as usize;
175        let mut ba = self.bs.to_bitarray();
176        let bs: &mut Bs = ba.as_mut();
177        bs[..Self::BITS].rotate_right(n);
178
179        Kmer {
180            _p: PhantomData,
181            bs: S::from_bitslice(&bs[..Self::BITS]),
182        }
183    }
184
185    /// Shift bases to the right and push a base onto the end.
186    ///
187    /// ```
188    /// use bio_seq::prelude::*;
189    /// use bio_seq::codec::dna::Dna;
190    ///
191    /// let k = kmer!("ACGAT");
192    /// assert_eq!(k.pushr(Dna::T).to_string(), "CGATT");
193    /// ```
194    pub fn pushr(self, base: A) -> Self {
195        let mut ba = self.rotated_left(1).bs.to_bitarray();
196        let bs: &mut Bs = ba.as_mut();
197
198        let start = Self::BITS - A::BITS as usize;
199        let end = start + A::BITS as usize;
200
201        bs[start..end].store(base.to_bits());
202
203        Kmer {
204            _p: PhantomData,
205            bs: S::from_bitslice(bs),
206        }
207    }
208
209    /// Push a base from the left
210    pub fn pushl(self, base: A) -> Self {
211        let mut ba = self.rotated_right(1).bs.to_bitarray();
212        let bs: &mut Bs = ba.as_mut();
213
214        bs[..A::BITS as usize].store(base.to_bits());
215
216        Kmer {
217            _p: PhantomData,
218            bs: S::from_bitslice(bs),
219        }
220    }
221
222    /// Create Kmer from sequence without checking length
223    pub fn unsafe_from_seqslice(seq: &SeqSlice<A>) -> Self {
224        Self::assert_k();
225        debug_assert!(K == seq.len(), "K != seq.len()");
226        Kmer {
227            _p: PhantomData,
228            bs: S::from_bitslice(&seq.bs),
229        }
230    }
231
232    fn complement(&mut self) {
233        self.bs.complement(K * A::BITS as usize);
234    }
235
236    fn rev_blocks_2(&mut self) {
237        // TODO: assert K == 2
238        self.bs.rev_blocks_2();
239        self.bs.shiftr((S::BITS - (A::BITS as usize * K)) as u32);
240    }
241}
242
243impl<A: Codec, const K: usize> From<usize> for Kmer<A, K, usize> {
244    fn from(i: usize) -> Kmer<A, K, usize> {
245        Self::assert_k();
246        Kmer {
247            _p: PhantomData,
248            bs: i,
249        }
250    }
251}
252
253impl<A: Codec, const K: usize> From<u64> for Kmer<A, K, u64> {
254    fn from(i: u64) -> Kmer<A, K, u64> {
255        Self::assert_k();
256        Kmer {
257            _p: PhantomData,
258            bs: i,
259        }
260    }
261}
262
263impl<A: Codec, const K: usize> From<usize> for Kmer<A, K, u64> {
264    fn from(i: usize) -> Kmer<A, K, u64> {
265        Self::assert_k();
266        Kmer {
267            _p: PhantomData,
268            bs: i as u64,
269        }
270    }
271}
272
273/*
274impl<A: Codec, const K: usize, S: KmerStorage> From<&SeqSlice<A>> for Kmer<A, K, S> {
275    fn from(seq: &SeqSlice<A>) -> Self {
276        Kmer {
277            _p: PhantomData,
278            bs: S::from_bitslice(&seq.bs),
279        }
280    }
281}
282*/
283
284impl<S: KmerStorage + Into<usize>, A: Codec, const K: usize> From<&Kmer<A, K, S>> for usize {
285    fn from(kmer: &Kmer<A, K, S>) -> usize {
286        kmer.bs.into()
287    }
288}
289
290impl<A: Codec, const K: usize> Deref for Kmer<A, K, usize> {
291    type Target = SeqSlice<A>;
292
293    fn deref(&self) -> &Self::Target {
294        let bs: &Bs = &self.bs.view_bits()[0..(K * A::BITS as usize)];
295        let bs: *const Bs = ptr::from_ref::<Bs>(bs);
296        unsafe { &*(bs as *const SeqSlice<A>) }
297    }
298}
299
300impl<A: Codec, const K: usize> AsRef<SeqSlice<A>> for Kmer<A, K, usize> {
301    fn as_ref(&self) -> &SeqSlice<A> {
302        self
303    }
304}
305
306impl<A: Codec, const K: usize, S: KmerStorage> fmt::Display for Kmer<A, K, S> {
307    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
308        let mut s = String::new();
309        let ba = self.bs.to_bitarray();
310        let bs: &Bs = &ba.as_ref()[0..(K * A::BITS as usize)];
311
312        bs.chunks(A::BITS as usize).for_each(|chunk| {
313            s.push(A::unsafe_from_bits(chunk.load_le::<u8>()).to_char());
314        });
315        write!(f, "{s}")
316    }
317}
318
319/// An iterator over all kmers of a sequence with a specified length
320pub struct KmerIter<'a, A: Codec, const K: usize> {
321    pub slice: &'a SeqSlice<A>,
322    pub index: usize,
323    pub len: usize,
324    pub _p: PhantomData<A>,
325}
326
327impl<A: Codec, const K: usize, S: KmerStorage> Kmer<A, K, S> {
328    fn unsafe_from(seq: &SeqSlice<A>) -> Self {
329        Self::assert_k();
330        debug_assert!(K == seq.len(), "K != seq.len()");
331        Kmer {
332            _p: PhantomData,
333            bs: S::from_bitslice(&seq.bs),
334        }
335    }
336}
337
338impl<A: Codec, const K: usize> Iterator for KmerIter<'_, A, K> {
339    type Item = Kmer<A, K>;
340    fn next(&mut self) -> Option<Kmer<A, K>> {
341        let i = self.index;
342        if self.index + K > self.len {
343            return None;
344        }
345        self.index += 1;
346        Some(Kmer::<A, K>::unsafe_from(&self.slice[i..i + K]))
347    }
348}
349
350/// ```
351/// use bio_seq::prelude::*;
352/// use std::hash::{Hash, Hasher, DefaultHasher};
353///
354/// let mut hasher1 = DefaultHasher::new();
355/// kmer!("AAA").hash(&mut hasher1);
356/// let hash1 = hasher1.finish();
357///
358/// let mut hasher2 = DefaultHasher::new();
359/// kmer!("AAAA").hash(&mut hasher2);
360/// let hash2 = hasher2.finish();
361///
362/// assert_ne!(hash1, hash2);
363/// ```
364impl<A: Codec, const K: usize, S: KmerStorage> Hash for Kmer<A, K, S> {
365    fn hash<H: Hasher>(&self, state: &mut H) {
366        // Length prefix as 8 little-endian bytes (see `SeqSlice` for rationale).
367        state.write(&(K as u64).to_le_bytes());
368        let ba = self.bs.to_bitarray();
369        let bs: &Bs = ba.as_ref();
370        // Only the meaningful bits are hashed; the unused high bits of the
371        // storage word/array are padding and must not influence the digest.
372        let n_bits = K * A::BITS as usize;
373        crate::hash::hash_bits(&bs[..n_bits], state);
374    }
375}
376
377impl<A: Codec, const K: usize, S: KmerStorage> TryFrom<&SeqSlice<A>> for Kmer<A, K, S> {
378    type Error = ParseBioError;
379
380    fn try_from(seq: &SeqSlice<A>) -> Result<Self, Self::Error> {
381        if seq.len() == K {
382            Ok(Kmer::<A, K, S>::unsafe_from(&seq[0..K]))
383        } else {
384            Err(ParseBioError::MismatchedLength(K, seq.len()))
385        }
386    }
387}
388
389impl<A: Codec, const K: usize> TryFrom<Seq<A>> for Kmer<A, K> {
390    type Error = ParseBioError;
391
392    fn try_from(seq: Seq<A>) -> Result<Self, Self::Error> {
393        Self::try_from(seq.as_ref())
394    }
395}
396
397impl<A: Codec, const K: usize, S: KmerStorage> PartialEq<SeqArray<A, K, 1>> for Kmer<A, K, S> {
398    fn eq(&self, seq: &SeqArray<A, K, 1>) -> bool {
399        if seq.len() != K {
400            return false;
401        }
402        &Kmer::<A, K, S>::unsafe_from(seq.as_ref()) == self
403    }
404}
405
406impl<A: Codec, const K: usize, S: KmerStorage> PartialEq<&SeqArray<A, K, 1>> for Kmer<A, K, S> {
407    fn eq(&self, seq: &&SeqArray<A, K, 1>) -> bool {
408        if seq.len() != K {
409            return false;
410        }
411        &Kmer::<A, K, S>::unsafe_from(seq.as_ref()) == self
412    }
413}
414
415impl<A: Codec, const K: usize> PartialEq<Seq<A>> for Kmer<A, K> {
416    fn eq(&self, seq: &Seq<A>) -> bool {
417        if seq.len() != K {
418            return false;
419        }
420        &Kmer::<A, K>::unsafe_from(seq.as_ref()) == self
421    }
422}
423
424impl<A: Codec, const K: usize, S: KmerStorage> PartialEq<SeqSlice<A>> for Kmer<A, K, S> {
425    fn eq(&self, seq: &SeqSlice<A>) -> bool {
426        if seq.len() != K {
427            return false;
428        }
429        &Kmer::<A, K, S>::unsafe_from(seq) == self
430    }
431}
432
433impl<A: Codec, const K: usize, S: KmerStorage> PartialEq<&SeqSlice<A>> for Kmer<A, K, S> {
434    fn eq(&self, seq: &&SeqSlice<A>) -> bool {
435        if seq.len() != K {
436            return false;
437        }
438        &Kmer::<A, K, S>::unsafe_from(seq) == self
439    }
440}
441
442impl<A: Codec, const K: usize> PartialEq<&str> for Kmer<A, K> {
443    fn eq(&self, seq: &&str) -> bool {
444        &self.to_string() == seq
445    }
446}
447
448impl<A: Codec, const K: usize, S: KmerStorage> FromStr for Kmer<A, K, S> {
449    type Err = ParseBioError;
450
451    fn from_str(s: &str) -> Result<Self, Self::Err> {
452        if s.len() != K {
453            return Err(ParseBioError::MismatchedLength(K, s.len()));
454        }
455        let seq: Seq<A> = Seq::from_str(s)?;
456        Kmer::<A, K, S>::try_from(seq.as_ref())
457    }
458}
459
460impl<A: Codec, const K: usize> From<Kmer<A, K, usize>> for Seq<A> {
461    fn from(kmer: Kmer<A, K, usize>) -> Self {
462        let mut seq: Seq<A> = Seq::with_capacity(K);
463        seq.extend(kmer.iter());
464        seq
465    }
466}
467
468impl<const K: usize> ComplementMut for Kmer<codec::dna::Dna, K, usize> {
469    fn comp(&mut self) {
470        self.complement();
471    }
472}
473
474impl<const K: usize> Complement for Kmer<codec::dna::Dna, K, usize> {}
475
476impl<A: Codec, const K: usize> ReverseMut for Kmer<A, K, usize> {
477    fn rev(&mut self) {
478        self.rev_blocks_2();
479    }
480}
481
482impl<A: Codec, const K: usize> Reverse for Kmer<A, K, usize> {}
483
484impl<const K: usize> ReverseComplementMut for Kmer<codec::dna::Dna, K, usize> {}
485
486impl<const K: usize> ReverseComplement for Kmer<codec::dna::Dna, K, usize> {}
487
488/// Convenient compile time kmer constructor
489///
490/// This is a wrapper for the `dna!` macro that returns a `Kmer`:
491/// ```
492/// # use bio_seq::prelude::*;
493/// let kmer: Kmer<Dna, 8> = kmer!("ACGTACGT");
494/// ```
495#[macro_export]
496macro_rules! kmer {
497    ($seq:expr) => {
498        Kmer::<Dna, { $seq.len() }>::unsafe_from_seqslice(dna!($seq))
499    };
500    ($seq:expr, $storage:ty) => {
501        Kmer::<Dna, { $seq.len() }, $storage>::unsafe_from_seqslice(dna!($seq))
502    };
503}
504
505#[cfg(test)]
506mod tests {
507    use crate::prelude::*;
508    use crate::seq::SeqArray;
509
510    #[test]
511    #[allow(clippy::cast_sign_loss)]
512    fn kmer_to_usize() {
513        let s: &'static SeqSlice<Dna> = dna!("AACTT");
514        println!("{s}");
515
516        for (kmer, index) in s.kmers::<2>().zip([0b00_00, 0b01_00, 0b11_01, 0b11_11]) {
517            println!("{kmer}");
518            assert_eq!(index as usize, (&kmer).into());
519        }
520    }
521    #[test]
522    fn pushl_test() {
523        let k = kmer!("ACGT");
524        let k1 = k.pushl(Dna::G);
525        let k2 = k1.pushl(Dna::A);
526        let k3 = k2.pushl(Dna::T);
527        let k4 = k3.pushl(Dna::C);
528        let k5 = k4.pushl(Dna::C);
529
530        assert_eq!(k1, kmer!("GACG"));
531        assert_eq!(k2, kmer!("AGAC"));
532        assert_eq!(k3, kmer!("TAGA"));
533        assert_eq!(k4, kmer!("CTAG"));
534        assert_eq!(k5, kmer!("CCTA"));
535    }
536    #[test]
537    fn pushr_test() {
538        let k = kmer!("ACGT");
539        let k1 = k.pushr(Dna::G);
540        let k2 = k1.pushr(Dna::A);
541        let k3 = k2.pushr(Dna::T);
542        let k4 = k3.pushr(Dna::C);
543        let k5 = k4.pushr(Dna::C);
544
545        println!("{k1}");
546        assert_eq!(k1, kmer!("CGTG"));
547        assert_eq!(k2, kmer!("GTGA"));
548        assert_eq!(k3, kmer!("TGAT"));
549        assert_eq!(k4, kmer!("GATC"));
550        assert_eq!(k5, kmer!("ATCC"));
551    }
552
553    #[test]
554    #[allow(clippy::cast_sign_loss)]
555    fn amino_kmer_to_usize() {
556        for (kmer, index) in Seq::<Amino>::try_from("SRY")
557            .unwrap()
558            .kmers::<2>()
559            .zip([0b0010_0001_1000, 0b0100_1100_1000])
560        {
561            assert_eq!(index as usize, usize::from(&kmer));
562        }
563    }
564    #[test]
565    fn big_kmer_shiftr() {
566        let mut kmer: Kmer<Dna, 32, u64> = kmer!("AATTTGTGGGTTCGTCTGCGGCTCCGCCCTTA", u64);
567        for base in dna!("TACTATGAGGACGATCAGCACCATAAGAACAAA") {
568            kmer = kmer.pushr(base);
569        }
570        assert_eq!(kmer!("ACTATGAGGACGATCAGCACCATAAGAACAAA", u64), kmer);
571    }
572
573    #[test]
574    fn big_kmer_shiftl() {
575        let mut kmer: Kmer<Dna, 32, u64> = kmer!("AATTTGTGGGTTCGTCTGCGGCTCCGCCCTTA", u64);
576        for base in dna!("GTACTATGAGGACGATCAGCACCATAAGAACAAA") {
577            kmer = kmer.pushl(base);
578        }
579        assert_eq!(kmer!("AAACAAGAATACCACGACTAGCAGGAGTATCA", u64), kmer);
580    }
581
582    #[test]
583    fn amino_kmer_iter() {
584        for (kmer, target) in Seq::<Amino>::try_from("SSLMNHKKL")
585            .unwrap()
586            .kmers::<3>()
587            .zip(["SSL", "SLM", "LMN", "MNH", "NHK", "HKK", "KKL"])
588        {
589            assert_eq!(kmer, target);
590        }
591    }
592
593    #[test]
594    fn test_rotations() {
595        let kmer: Kmer<Dna, 9> = Kmer::try_from(dna!("ACTGCGATG")).unwrap();
596
597        for (shift, rotation) in vec![
598            "ACTGCGATG",
599            "CTGCGATGA",
600            "TGCGATGAC",
601            "GCGATGACT",
602            "CGATGACTG",
603            "GATGACTGC",
604            "ATGACTGCG",
605            "TGACTGCGA",
606            "GACTGCGAT",
607            "ACTGCGATG",
608            "CTGCGATGA",
609            "TGCGATGAC",
610        ]
611        .into_iter()
612        .enumerate()
613        {
614            //            println!("{} {} {}", shift, kmer.rotated_left(shift as u32), rotation);
615            assert_eq!(kmer.rotated_left(shift as u32), rotation);
616        }
617
618        for (shift, rotation) in vec![
619            "ACTGCGATG",
620            "GACTGCGAT",
621            "TGACTGCGA",
622            "ATGACTGCG",
623            "GATGACTGC",
624            "CGATGACTG",
625            "GCGATGACT",
626            "TGCGATGAC",
627            "CTGCGATGA",
628            "ACTGCGATG",
629            "GACTGCGAT",
630        ]
631        .into_iter()
632        .enumerate()
633        {
634            //            println!("{} {} {}", shift, kmer.rotated_right(shift as u32), rotation);
635            assert_eq!(kmer.rotated_right(shift as u32), rotation);
636        }
637
638        let kmer: Kmer<Dna, 8> = Kmer::try_from(dna!("ACTGCGAT")).unwrap().rotated_left(1);
639
640        assert_ne!(kmer.to_string(), "ACTGCGAT");
641        assert_eq!(kmer.to_string(), "CTGCGATA");
642
643        let kmer: Kmer<Dna, 8> = kmer!("ACTGCGAT").rotated_right(1);
644
645        assert_ne!(kmer.to_string(), "ACTGCGAT");
646        assert_eq!(kmer.to_string(), "TACTGCGA");
647
648        let kmer: Kmer<Dna, 9> = Kmer::from_str("ACTGCGATG").unwrap().rotated_left(0);
649
650        assert_eq!(kmer.to_string(), "ACTGCGATG");
651        assert_ne!(kmer.to_string(), "ACTGCGATGA");
652
653        let kmer: Kmer<Dna, 9> = Kmer::try_from(dna!("ACTGCGATG")).unwrap().rotated_right(0);
654
655        assert_eq!(kmer.to_string(), "ACTGCGATG");
656        assert_ne!(kmer.to_string(), "ACTGCGATGA");
657
658        let kmer: Kmer<Dna, 9> = Kmer::from_str("ACTGCGATG").unwrap().rotated_left(9 * 3307);
659
660        assert_eq!(kmer.to_string(), "ACTGCGATG");
661        assert_ne!(kmer.to_string(), "ACTGCGATGA");
662
663        let kmer: Kmer<Dna, 9> = kmer!("ACTGCGATG").rotated_right(9 * 3307);
664
665        assert_eq!(kmer.to_string(), "ACTGCGATG");
666        assert_ne!(kmer.to_string(), "ACTGCGATGA");
667    }
668
669    #[test]
670    fn eq_functions() {
671        assert_eq!(kmer!("ACGT"), dna!("ACGT"));
672
673        // this should be a compiler error:
674        // assert_ne!(kmer!("ACGT"), dna!("ACGTA"));
675
676        let kmer: Kmer<Iupac, 4> = Kmer::from_str("ACGT").unwrap();
677        assert_eq!(kmer, iupac!("ACGT"));
678        assert_ne!(kmer, iupac!("NCGT"));
679    }
680
681    #[test]
682    fn kmer_iter() {
683        //let seq = dna!("ACTGA");
684        let cs: Vec<Kmer<Dna, 3>> = dna!("ACTGA").kmers().collect();
685        assert_eq!(cs[0], "ACT");
686        assert_eq!(cs[1], "CTG");
687        assert_eq!(cs[2], "TGA");
688        assert_eq!(cs.len(), 3);
689    }
690
691    #[test]
692    fn k_check() {
693        let _kmer = Kmer::<Dna, 32>::from(0);
694        let _kmer = Kmer::<Amino, 10>::from(0);
695        let _kmer = Kmer::<Iupac, 14>::from(0);
696    }
697
698    #[test]
699    fn kmer_revcomp() {
700        assert_eq!(kmer!("ACGT"), kmer!("ACGT").to_revcomp());
701        assert_ne!(kmer!("GTCGTA"), kmer!("TACGAC"));
702
703        let rc = kmer!("GTCGTA").to_revcomp();
704
705        assert_eq!(rc, kmer!("TACGAC"));
706
707        assert_eq!(
708            kmer!("GCTATCGATCTGATCG"),
709            kmer!("CGATCAGATCGATAGC").to_revcomp()
710        );
711    }
712
713    #[test]
714    fn kmer_deref() {
715        let kmer: Kmer<Dna, 3> = kmer!("ACG");
716        let seq: &SeqSlice<Dna> = &kmer;
717
718        assert_eq!(*seq, *kmer);
719        assert_eq!(seq.to_string(), "ACG");
720
721        let kmer: Kmer<Dna, 8> = kmer!("AAAAAAAA");
722        let seq: &SeqSlice<Dna> = &kmer;
723
724        assert_eq!(seq.to_string(), "AAAAAAAA");
725
726        let kmer: Kmer<Dna, 8> = kmer!("TTTTTTTT");
727        let seq: &SeqSlice<Dna> = &kmer;
728
729        assert_eq!(seq.to_string(), "TTTTTTTT");
730
731        let kmer: Kmer<Dna, 16> = kmer!("AGCTAGCTAGCTAGCT");
732        let seq: &SeqSlice<Dna> = &kmer;
733
734        assert_eq!(seq.to_string(), "AGCTAGCTAGCTAGCT");
735    }
736
737    #[test]
738    fn kmer_as_ref() {
739        let kmer: Kmer<Dna, 4> = kmer!("ACGT");
740        let seq: &SeqSlice<Dna> = kmer.as_ref();
741
742        assert_eq!(seq.to_string(), "ACGT");
743
744        let kmer: Kmer<Dna, 16> = kmer!("AGCTAGCTAGCTAGCT");
745        let seq: &SeqSlice<Dna> = kmer.as_ref();
746
747        assert_eq!(seq.to_string(), "AGCTAGCTAGCTAGCT");
748    }
749
750    #[test]
751    fn kmer_rev() {
752        let mut kmer: Kmer<Dna, 4> = kmer!("ACGT");
753
754        kmer.rev();
755
756        assert_eq!(kmer.to_string(), "TGCA");
757
758        kmer.rev();
759
760        assert_eq!(kmer.to_string(), "ACGT");
761
762        kmer.rev();
763
764        assert_eq!(kmer.to_string(), "TGCA");
765    }
766
767    #[test]
768    #[allow(clippy::needless_borrow)]
769    fn kmer_storage_types() {
770        let s1 = "AACGTAGCCGCGAACTTACGTAGCCGCGAAAA";
771        let s2 = "AACGTAGCCGCGAACTTACGTAGCCGCGAAA";
772        let s3 = "ACGTAGCCGCGAACTTACGTAGCCGCGAAAA";
773
774        let s4 = "AACGTAGCCGCGAACTTACGTAGCCGCGAAAAAACGTAGCCGCGAACTTACGTAGCCGCGAAAA";
775        let s5 = "AACGTAGCCGCGAACTTACGTAGCCGCGAAAAAACGTAGCCGCGAACTTACGTAGCCGCGAAAAA";
776
777        assert_eq!(s1.len(), 32);
778        assert_eq!(s2.len(), 31);
779        assert_eq!(s3.len(), 31);
780        assert_eq!(s4.len(), 64);
781        assert_eq!(s5.len(), 65);
782
783        let kmer1_64 = Kmer::<Dna, 32, u64>::from_str(&s1).unwrap();
784        let kmer2_64 = Kmer::<Dna, 31, u64>::from_str(&s2).unwrap();
785        let kmer3_64 = Kmer::<Dna, 31, u64>::from_str(&s3).unwrap();
786
787        let kmer1 = Kmer::<Dna, 32, u64>::from_str(&s1).unwrap();
788        let kmer2 = Kmer::<Dna, 31, u64>::from_str(&s2).unwrap();
789        let kmer3 = Kmer::<Dna, 31, u64>::from_str(&s3).unwrap();
790
791        let kmer4_128 = Kmer::<Dna, 64, u128>::from_str(&s4).unwrap();
792
793        let seq5: Seq<Dna> = s5.try_into().unwrap();
794
795        assert_eq!(kmer4_128, &seq5[..64]);
796        assert_ne!(kmer4_128, &seq5[1..]);
797
798        assert_eq!(kmer1, &seq5[..32]);
799        assert_eq!(kmer1, &seq5[32..64]);
800
801        assert_eq!(kmer1_64, &seq5[..32]);
802        assert_eq!(kmer1_64, &seq5[32..64]);
803
804        assert_ne!(kmer1, &seq5[..31]);
805        assert_ne!(kmer1, &seq5[32..]);
806
807        assert_ne!(kmer1_64, &seq5[1..33]);
808        assert_ne!(kmer1_64, &seq5[33..]);
809
810        assert_eq!(kmer4_128, kmer4_128);
811        assert_eq!(kmer1_64, kmer1_64);
812        assert_eq!(kmer2, kmer2);
813
814        assert_ne!(kmer2, kmer3);
815        assert_ne!(kmer2_64, kmer3_64);
816        // PartialEq is not implemented for different storgage types
817        /*
818                assert_ne!(kmer2, kmer3_64);
819                assert_eq!(kmer2, kmer2_64);
820                assert_eq!(kmer1, kmer1_64);
821        */
822    }
823
824    #[test]
825    fn try_from_seq() {
826        let seq: Seq<Dna> = Seq::try_from("ACACACACACACGT").unwrap();
827        assert_eq!(
828            Kmer::<Dna, 8>::try_from(&seq[..8]).unwrap().to_string(),
829            "ACACACAC"
830        );
831        assert_eq!(
832            Kmer::<Dna, 8>::try_from(&seq[1..9]).unwrap().to_string(),
833            "CACACACA"
834        );
835
836        let err: Result<Kmer<Dna, 8>, ParseBioError> = Kmer::try_from(&seq[2..9]);
837        assert_eq!(err, Err(ParseBioError::MismatchedLength(8, 7)));
838
839        let err: Result<Kmer<Dna, 8>, ParseBioError> = Kmer::try_from(seq);
840        assert_eq!(err, Err(ParseBioError::MismatchedLength(8, 14)));
841
842        let seq: Seq<Dna> = Seq::try_from("ACACACACACACGT").unwrap();
843
844        assert_eq!(
845            Kmer::<Dna, 14>::try_from(seq).unwrap().to_string(),
846            "ACACACACACACGT"
847        );
848    }
849}