Skip to main content

bio_seq/
seq.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//! Arbitrary length sequences of bit-packed genomic data
7//!
8//! `Seq` and `&SeqSlice` are analogous to `String` and `&str`. A `Seq` owns its data and a `SeqSlice` is a read-only window into a `Seq`.
9pub mod index;
10pub mod iterators;
11
12mod array;
13mod slice;
14
15pub use array::SeqArray;
16pub use slice::SeqSlice;
17
18use crate::codec::{Codec, text};
19use crate::error::ParseBioError;
20use crate::{
21    Complement, ComplementMut, Maskable, MaskableMut, Reverse, ReverseComplement,
22    ReverseComplementMut, ReverseMut,
23};
24
25use crate::{Bs, Bv, Order};
26
27use bitvec::field::BitField;
28use bitvec::view::BitView;
29
30#[cfg(feature = "serde")]
31use serde::{Deserialize, Serialize};
32
33use core::borrow::Borrow;
34use core::hash::{Hash, Hasher};
35use core::marker::PhantomData;
36use core::ops::{Bound, Deref, RangeBounds};
37use core::str::FromStr;
38use core::{fmt, ptr, str};
39
40/// A arbitrary length sequence of bit-packed symbols
41///
42/// Stored on the heap
43#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)]
44#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
45#[repr(transparent)]
46pub struct Seq<A: Codec> {
47    pub(crate) _p: PhantomData<A>,
48    pub(crate) bv: Bv,
49}
50
51impl<A: Codec> From<Seq<A>> for usize {
52    fn from(seq: Seq<A>) -> usize {
53        debug_assert!(seq.bv.len() <= usize::BITS as usize);
54        if seq.bv.is_empty() {
55            0
56        } else {
57            seq.bv.load_le::<usize>() //.wrapping_shr(shift)
58        }
59    }
60}
61
62impl<A: Codec> Hash for Seq<A> {
63    fn hash<H: Hasher>(&self, state: &mut H) {
64        self.as_ref().hash(state);
65        //self.len().hash(state);
66    }
67}
68
69impl<A: Codec> Default for Seq<A> {
70    fn default() -> Self {
71        Self::new()
72    }
73}
74
75impl<A: Codec> Seq<A> {
76    #[must_use]
77    pub fn new() -> Self {
78        Seq {
79            _p: PhantomData,
80            bv: Bv::new(),
81        }
82    }
83
84    fn bit_range<R: RangeBounds<usize>>(&self, range: R) -> (usize, usize) {
85        let s = match range.start_bound() {
86            Bound::Included(&n) => n,
87            Bound::Excluded(&n) => n.checked_add(1).expect("bound overflow"),
88            Bound::Unbounded => 0,
89        };
90
91        let e = match range.end_bound() {
92            Bound::Included(&n) => n.checked_add(1).expect("bound overflow"),
93            Bound::Excluded(&n) => n,
94            Bound::Unbounded => self.len(),
95        };
96
97        assert!(s <= e, "Start of range must be less than or equal to end");
98        assert!(e <= self.len(), "Range out of bounds");
99
100        (s * A::BITS as usize, e * A::BITS as usize)
101    }
102
103    /// Trim leading and trailing characters that don't match bases/symbols
104    /// ```
105    /// # use bio_seq::prelude::*;
106    /// let seq = b"NNNNAGAATGATGGGGGGGGGGGCGNNNNNNNNNNN";
107    /// let trimmed: Seq<Dna> = Seq::trim_u8(seq).unwrap();
108    /// assert_eq!(trimmed, dna!("AGAATGATGGGGGGGGGGGCG"));
109    /// ```
110    ///
111    /// # Errors
112    ///
113    /// Will return an `UnrecognisedBase` error for non-leading or non-trailing end bases, just as
114    /// `TryFrom<&[u8]>` would.
115    pub fn trim_u8(v: &[u8]) -> Result<Self, ParseBioError> {
116        let start = v
117            .iter()
118            .position(|&byte| A::try_from_ascii(byte).is_some())
119            .unwrap_or(v.len());
120
121        let end = v[start..]
122            .iter()
123            .rposition(|&byte| A::try_from_ascii(byte).is_some())
124            .map_or(start, |pos| start + pos + 1);
125
126        v[start..end]
127            .iter()
128            .map(|&byte| A::try_from_ascii(byte).ok_or(ParseBioError::UnrecognisedBase(byte)))
129            .collect()
130    }
131
132    #[must_use]
133    pub fn with_capacity(len: usize) -> Self {
134        Seq {
135            _p: PhantomData,
136            bv: Bv::with_capacity(len * A::BITS as usize),
137        }
138    }
139
140    #[must_use]
141    pub fn bit_and(self, rhs: Seq<A>) -> Seq<A> {
142        Seq::<A> {
143            _p: PhantomData,
144            bv: Bv::from_bitslice(&(self.bv & rhs.bv)),
145        }
146    }
147
148    #[must_use]
149    pub fn bit_or(self, rhs: Seq<A>) -> Seq<A> {
150        Seq::<A> {
151            _p: PhantomData,
152            bv: Bv::from_bitslice(&(self.bv | rhs.bv)),
153        }
154    }
155
156    pub fn push(&mut self, item: A) {
157        let byte: u8 = item.to_bits();
158        self.bv
159            .extend_from_bitslice(&byte.view_bits::<Order>()[..A::BITS as usize]);
160    }
161
162    pub fn clear(&mut self) {
163        self.bv.clear();
164    }
165
166    /// Truncate a sequence
167    /// ```
168    /// # use bio_seq::prelude::*;
169    /// let mut seq: Seq<Dna> = dna!("CCCCC").into();
170    /// seq.truncate(2);
171    /// assert_eq!(&seq, dna!("CC"));
172    /// ```
173    pub fn truncate(&mut self, len: usize) {
174        self.bv.truncate(len * A::BITS as usize);
175    }
176
177    /// Prepend a slice
178    /// ```
179    /// # use bio_seq::prelude::*;
180    /// let mut seq: Seq<Dna> = dna!("CCCCC").into();
181    /// seq.prepend(dna!("TTTT"));
182    /// assert_eq!(&seq, dna!("TTTTCCCCC"));
183    /// ```
184    pub fn prepend(&mut self, other: &SeqSlice<A>) {
185        let mut bv = Bv::with_capacity(self.bv.len() + other.bs.len());
186        bv.extend_from_bitslice(&other.bs);
187        bv.extend_from_bitslice(&self.bv);
188        self.bv = bv;
189    }
190
191    /// Append a slice
192    /// ```
193    /// # use bio_seq::prelude::*;
194    /// let mut seq: Seq<Dna> = dna!("CCCCC").into();
195    /// seq.append(dna!("TTTT"));
196    /// assert_eq!(&seq, dna!("CCCCCTTTT"));
197    /// ```
198    pub fn append(&mut self, other: &SeqSlice<A>) {
199        self.bv.extend_from_bitslice(&other.bs);
200    }
201
202    /// Remove a range and replace it with a slice
203    /// ```
204    /// # use bio_seq::prelude::*;
205    /// let mut seq: Seq<Dna> = dna!("AAAACCAAAA").into();
206    /// seq.splice(4..6, dna!("TTTT"));
207    /// assert_eq!(&seq, dna!("AAAATTTTAAAA"));
208    /// ```
209    pub fn splice<R: RangeBounds<usize>>(&mut self, range: R, other: &SeqSlice<A>) {
210        let (s, e) = self.bit_range(range);
211
212        let mut bv = Bv::with_capacity(self.bv.len() - (e - s) + other.bs.len());
213
214        bv.extend_from_bitslice(&self.bs[..s]);
215        bv.extend_from_bitslice(&other.bs);
216        bv.extend_from_bitslice(&self.bs[e..]);
217
218        self.bv = bv;
219    }
220
221    /// Insert a slice into a sequence
222    /// ```
223    /// # use bio_seq::prelude::*;
224    /// let mut seq: Seq<Dna> = dna!("AAAAA").into();
225    /// seq.insert(3, dna!("TTTT"));
226    /// assert_eq!(&seq, dna!("AAATTTTAA"));
227    /// ```
228    ///
229    /// # Panics
230    /// Panics if index out of bounds
231    pub fn insert(&mut self, index: usize, other: &SeqSlice<A>) {
232        assert!(index <= self.len(), "Index out of bounds");
233
234        let i = index * A::BITS as usize;
235        let mut bv = Bv::with_capacity(self.bv.len() + other.bs.len());
236
237        bv.extend_from_bitslice(&self.bs[..i]);
238        bv.extend_from_bitslice(&other.bs);
239        bv.extend_from_bitslice(&self.bs[i..]);
240
241        self.bv = bv;
242    }
243
244    /// Remove a region of a sequence
245    /// ```
246    /// # use bio_seq::prelude::*;
247    /// let mut seq: Seq<Dna> = dna!("ACGTACGT").into();
248    /// seq.remove(2..5);
249    /// assert_eq!(&seq, dna!("ACCGT"));
250    /// ```
251    pub fn remove<R: RangeBounds<usize>>(&mut self, range: R) {
252        let (s, e) = self.bit_range(range);
253        self.bv.drain(s..e);
254    }
255
256    pub fn extend_from_iter<I: IntoIterator<Item = A>>(&mut self, iter: I) {
257        iter.into_iter().for_each(|base| self.push(base));
258    }
259
260    /// **Experimental** Decode sequence from raw `usize` array. This requires the exact length of the target sequence to be known.
261    /// ```
262    /// # use bio_seq::prelude::*;
263    /// let ints: [usize; 2] = [0b0101010101010101010101010101010111111111111111111111111111111111, 0b11100100];
264    /// let seq: Option<Seq<Dna>> = Seq::from_raw(36, &ints);
265    /// assert_eq!(seq.unwrap(), dna!("TTTTTTTTTTTTTTTTCCCCCCCCCCCCCCCCACGT"));
266    /// ```
267    #[must_use]
268    pub fn from_raw(len: usize, bits: &[usize]) -> Option<Self> {
269        let mut bv: Bv = Bv::from_slice(bits);
270        let bit_len = len.checked_mul(A::BITS as usize)?;
271
272        if bit_len > bv.len() {
273            return None;
274        }
275        bv.truncate(bit_len);
276        Some(Seq {
277            _p: PhantomData,
278            bv,
279        })
280    }
281
282    /// **Experimental** Access raw sequence data as `&[usize]`
283    /// ```
284    /// # use bio_seq::prelude::*;
285    /// let seq: Seq<Dna> = dna!("TTTTTTTTTTTTTTTTCCCCCCCCCCCCCCCCACGT").into();
286    /// let ints: Vec<usize> = seq.into_raw().iter().copied().collect();
287    /// assert_eq!(ints[0], 0b0101010101010101010101010101010111111111111111111111111111111111);
288    /// assert_eq!(ints[1], 0b11100100); // ACGT
289    /// ```
290    #[must_use]
291    pub fn into_raw(&self) -> &[usize] {
292        self.bv.as_raw_slice()
293    }
294}
295
296impl<A: Codec> ReverseMut for Seq<A> {
297    fn rev(&mut self) {
298        self.bv.reverse();
299        for chunk in self.bv.rchunks_exact_mut(A::BITS as usize) {
300            chunk.reverse();
301        }
302    }
303}
304
305impl<A: Codec + ComplementMut> ComplementMut for Seq<A> {
306    fn comp(&mut self) {
307        unsafe {
308            for base in self.bv.chunks_exact_mut(A::BITS as usize).remove_alias() {
309                let mut bc = A::unsafe_from_bits(base.load_le::<u8>());
310                bc.comp();
311                base.store(bc.to_bits() as usize);
312            }
313        }
314    }
315}
316
317impl<A: Codec + MaskableMut> MaskableMut for Seq<A> {
318    fn mask(&mut self) {
319        unsafe {
320            for base in self.bv.chunks_exact_mut(A::BITS as usize).remove_alias() {
321                let mut bc = A::unsafe_from_bits(base.load_le::<u8>());
322                bc.mask();
323                base.store(bc.to_bits() as usize);
324            }
325        }
326    }
327    fn unmask(&mut self) {
328        unsafe {
329            for base in self.bv.chunks_exact_mut(A::BITS as usize).remove_alias() {
330                let mut bc = A::unsafe_from_bits(base.load_le::<u8>());
331                bc.unmask();
332                base.store(bc.to_bits() as usize);
333            }
334        }
335    }
336}
337
338impl<A: Codec + MaskableMut> Maskable for Seq<A> {}
339
340impl<A: Codec + ComplementMut> ReverseComplementMut for Seq<A> where
341    Seq<A>: ComplementMut + ReverseMut
342{
343}
344
345impl<A: Codec> Reverse for Seq<A> {}
346
347impl<A: Codec + ComplementMut> Complement for Seq<A> {}
348
349impl<A: Codec + ComplementMut> ReverseComplement for Seq<A> where Seq<A>: ComplementMut + ReverseMut {}
350
351impl<A: Codec> PartialEq<SeqSlice<A>> for Seq<A> {
352    fn eq(&self, other: &SeqSlice<A>) -> bool {
353        self.as_ref() == other
354    }
355}
356
357impl<A: Codec> PartialEq<&SeqSlice<A>> for Seq<A> {
358    fn eq(&self, other: &&SeqSlice<A>) -> bool {
359        self.as_ref() == *other
360    }
361}
362
363impl<A: Codec> PartialEq<Seq<A>> for &Seq<A> {
364    fn eq(&self, other: &Seq<A>) -> bool {
365        **self == *other
366    }
367}
368
369impl<A: Codec> PartialEq<&Seq<A>> for Seq<A> {
370    fn eq(&self, other: &&Seq<A>) -> bool {
371        *self == **other
372    }
373}
374
375/// Borrow a `Seq<A>` as a `SeqSlice<A>`.
376///
377/// The `Borrow` trait to is used to obtain a reference to a `SeqSlice` from a `Seq`, allowing it to be used wherever a `SeqSlice` is expected.
378/// ```
379/// use std::collections::HashMap;
380/// use bio_seq::prelude::*;
381///
382/// let reference = dna!("ACGTTCGCATGCTACGACGATC");
383///
384/// let mut table: HashMap<Seq<Dna>, usize> = HashMap::new();
385///
386/// // Associate some kind of count with sequences as keys:
387/// table.insert(dna!("ACGTT").into(), 1);
388/// table.insert(dna!("ACACCCCC").into(), 0);
389///
390/// // The query is a short window in the reference `Seq`
391/// let query: &SeqSlice<Dna> = &reference[..5];
392///
393/// if let Some(value) = table.get(query) {
394///        // `SeqSlice` implements `Display`
395///        println!("{query}: {value}");
396/// }
397/// ```
398impl<A: Codec> Borrow<SeqSlice<A>> for Seq<A> {
399    fn borrow(&self) -> &SeqSlice<A> {
400        self.as_ref()
401    }
402}
403
404impl<A: Codec> Borrow<SeqSlice<A>> for &Seq<A> {
405    fn borrow(&self) -> &SeqSlice<A> {
406        self.as_ref()
407    }
408}
409
410/// Automatic dereferencing of `Seq<A>` to `SeqSlice<A>`.
411///
412/// ```
413/// # use bio_seq::prelude::*;
414/// fn count_bases(s: &SeqSlice<Dna>) -> usize {
415///    s.len()
416/// }
417///
418/// let seq: Seq<Dna> = dna!("CATCGATCGATC").into();
419/// let count = count_bases(&seq);
420/// assert_eq!(count, 12);
421/// ```
422///
423impl<A: Codec> Deref for Seq<A> {
424    type Target = SeqSlice<A>;
425
426    fn deref(&self) -> &Self::Target {
427        let bs: *const Bs = ptr::from_ref::<Bs>(&self.bv);
428        unsafe { &*(bs as *const SeqSlice<A>) }
429    }
430}
431
432/// A Seq can be borrowed as a `SeqSlice` through generic constraints.
433///
434/// ```
435/// # use bio_seq::prelude::*;
436/// fn count_bases<S: AsRef<SeqSlice<Dna>>>(s: S) -> usize {
437///    s.as_ref().len()
438/// }
439///
440/// let seq: Seq<Dna> = dna!("CATCGATCGATC").into();
441/// let count = count_bases(seq); // the AsRef implementation allows us to directly pass a Seq
442/// assert_eq!(count, 12);
443/// ```
444///
445impl<A: Codec> AsRef<SeqSlice<A>> for Seq<A> {
446    fn as_ref(&self) -> &SeqSlice<A> {
447        self
448    }
449}
450
451/// Creates a deep copy of the sequence.
452///
453/// ```
454/// #[macro_use]
455/// # use bio_seq::prelude::*;
456/// let mut seq1: Seq<Dna> = dna!("CATCGATCGATC").into();
457/// let seq2: Seq<Dna> = seq1.clone();
458///
459/// seq1.push(Dna::A);
460/// assert_ne!(seq1, seq2);
461/// ```
462///
463impl<A: Codec> Clone for Seq<A> {
464    fn clone(&self) -> Self {
465        Self {
466            _p: PhantomData,
467            bv: self.bv.clone(),
468        }
469    }
470}
471
472impl<A: Codec> FromIterator<A> for Seq<A> {
473    fn from_iter<I: IntoIterator<Item = A>>(iter: I) -> Self {
474        let i = iter.into_iter();
475        let mut seq = Seq::with_capacity(i.size_hint().0);
476        seq.extend(i);
477        seq
478    }
479}
480
481impl<A: Codec> From<&Vec<A>> for Seq<A> {
482    fn from(vec: &Vec<A>) -> Self {
483        // for a general conversion: vec.iter().copied().map(Into::into).collect()
484
485        vec.iter().copied().collect()
486    }
487}
488
489impl<A: Codec + Into<B>, B: Codec> From<&SeqSlice<A>> for Seq<B> {
490    fn from(slice: &SeqSlice<A>) -> Self {
491        slice.iter().map(Into::into).collect()
492    }
493}
494
495impl<A: Codec + Into<B>, B: Codec, const N: usize, const W: usize> From<&SeqArray<A, N, W>>
496    for Seq<B>
497{
498    fn from(slice: &SeqArray<A, N, W>) -> Self {
499        slice.iter().map(Into::into).collect()
500    }
501}
502
503impl<A: Codec + Into<B>, B: Codec, const N: usize, const W: usize> From<SeqArray<A, N, W>>
504    for Seq<B>
505{
506    fn from(slice: SeqArray<A, N, W>) -> Self {
507        slice.iter().map(Into::into).collect()
508    }
509}
510
511impl<A: Codec> TryFrom<&str> for Seq<A> {
512    type Error = ParseBioError;
513
514    fn try_from(s: &str) -> Result<Self, Self::Error> {
515        Seq::<A>::try_from(s.as_bytes())
516    }
517}
518
519impl<A: Codec> TryFrom<String> for Seq<A> {
520    type Error = ParseBioError;
521
522    fn try_from(s: String) -> Result<Self, Self::Error> {
523        Seq::<A>::try_from(s.as_str())
524    }
525}
526
527impl<A: Codec> TryFrom<&String> for Seq<A> {
528    type Error = ParseBioError;
529
530    fn try_from(s: &String) -> Result<Self, Self::Error> {
531        Seq::<A>::try_from(s.as_str())
532    }
533}
534
535impl<A: Codec> FromStr for Seq<A> {
536    type Err = ParseBioError;
537
538    fn from_str(s: &str) -> Result<Self, Self::Err> {
539        Seq::<A>::try_from(s)
540    }
541}
542
543impl<A: Codec> TryFrom<&[u8]> for Seq<A> {
544    type Error = ParseBioError;
545
546    fn try_from(v: &[u8]) -> Result<Self, Self::Error> {
547        v.iter()
548            .copied()
549            .map(|byte| A::try_from_ascii(byte).ok_or(ParseBioError::UnrecognisedBase(byte)))
550            .collect()
551    }
552}
553
554impl<A: Codec> TryFrom<Vec<u8>> for Seq<A> {
555    type Error = ParseBioError;
556
557    fn try_from(v: Vec<u8>) -> Result<Self, Self::Error> {
558        // potentional optimisation: with an extra allocation we could
559        // .collect::<Result<Vec<A>, _>>()
560        // .map(|v| { let mut seq = Self::with_capacity etc.
561        v.into_iter()
562            .map(|byte| A::try_from_ascii(byte).ok_or(ParseBioError::UnrecognisedBase(byte)))
563            .collect()
564    }
565}
566
567impl<A: Codec> From<Seq<A>> for String {
568    fn from(seq: Seq<A>) -> Self {
569        String::from(seq.as_ref())
570    }
571}
572
573impl<A: Codec> From<&Seq<A>> for String {
574    fn from(seq: &Seq<A>) -> Self {
575        String::from(seq.as_ref())
576    }
577}
578
579impl<A: Codec> fmt::Display for Seq<A> {
580    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
581        fmt::Display::fmt(self.as_ref(), f)
582    }
583}
584
585impl<A: Codec> Extend<A> for Seq<A> {
586    fn extend<T: IntoIterator<Item = A>>(&mut self, iter: T) {
587        self.extend_from_iter(iter);
588    }
589}
590
591impl From<Vec<usize>> for Seq<text::Dna> {
592    fn from(vec: Vec<usize>) -> Self {
593        Seq {
594            _p: PhantomData,
595            bv: Bv::from_vec(vec),
596        }
597    }
598}
599
600/// **Unstable** construct a `Seq` from a bitslice. This may change in the future.
601impl<A: Codec> From<&Bs> for Seq<A> {
602    fn from(bs: &Bs) -> Self {
603        Seq {
604            _p: PhantomData,
605            bv: bs.into(),
606        }
607    }
608}
609
610/// **Unstable** construct a `Seq` from a bitvec. This may change in the future.
611impl<A: Codec> From<Bv> for Seq<A> {
612    fn from(bv: Bv) -> Self {
613        Seq {
614            _p: PhantomData,
615            bv,
616        }
617    }
618}
619
620#[cfg(test)]
621mod tests {
622    use crate::codec::text;
623    use crate::prelude::*;
624    use crate::{Bv, Order};
625    use bitvec::prelude::*;
626    use core::borrow::Borrow;
627    use core::hash::{Hash, Hasher};
628    use core::marker::PhantomData;
629    use std::collections::hash_map::DefaultHasher;
630
631    #[test]
632    fn test_revcomp() {
633        let s1: Seq<Dna> = dna!("ATGTGTGCGACTGA").into();
634        let mut s2: Seq<Dna> = dna!("TCAGTCGCACACAT").into();
635        let s3: &SeqSlice<Dna> = &s1;
636
637        assert_eq!(s3.to_revcomp(), s2.to_revcomp().to_revcomp());
638        assert_eq!(s3.to_revcomp(), &s2);
639
640        s2.revcomp();
641
642        assert_eq!(s3.to_revcomp(), s2.to_revcomp());
643        assert_ne!(s3, s2.to_revcomp());
644    }
645
646    #[test]
647    fn test_revcomp_mismatched_sizes() {
648        let s1 = dna!("AAAA");
649        let mut s2: Seq<Dna> = dna!("TTTTT").into();
650        s2.revcomp();
651        assert_ne!(s1, s2);
652    }
653
654    #[test]
655    fn test_revcomp_idempotence() {
656        let mut s = dna!("AAACGCTACGTACGCGCCTTCGGGGCATCAGCACCAC").to_owned();
657        let sc = dna!("AAACGCTACGTACGCGCCTTCGGGGCATCAGCACCAC");
658        s.revcomp();
659        assert_eq!(s.to_revcomp(), sc);
660        s.comp();
661        s.rev();
662        s.rev();
663        s.comp();
664        assert_eq!(s.to_revcomp(), sc);
665    }
666    #[test]
667    fn slice_index_comparisions() {
668        let s1 = dna!("ATGTGTGCGACTGATGATCAAACGTAGCTACG");
669        let s2 = dna!("ACGTGTGTGCTAGCTAATCGATCAAAAAG");
670
671        assert_eq!(&s1[0], &s2[0]);
672        assert_ne!(&s1[1], &s2[1]);
673        assert_eq!(&s1[2..4], &s2[2..4]);
674        assert_eq!(&s1[15..21], &s2[19..25]);
675        assert_ne!(&s1[2..20], &s2[2..20]);
676    }
677
678    #[test]
679    #[expect(clippy::redundant_slicing, reason = "exercise full-range indexing")]
680    fn slice_index_owned() {
681        let seq = dna!("GCTCGATCACT");
682
683        assert_eq!(&seq[..], dna!("GCTCGATCACT"));
684        assert_eq!(&seq[..=4], dna!("GCTCG"));
685        assert_eq!(&seq[1..=4], dna!("CTCG"));
686        assert_eq!(&seq[1..4], dna!("CTC"));
687        assert_eq!(&seq[..4], dna!("GCTC"));
688        assert_eq!(&seq[1..], dna!("CTCGATCACT"));
689    }
690
691    #[test]
692    #[expect(clippy::redundant_slicing, reason = "exercise full-range indexing")]
693    fn slice_indexing() {
694        let seq = dna!("TGCATCGAT");
695
696        assert_ne!(&seq[..], &dna!("AGCATCGAA")[..]);
697        assert_ne!(&seq[3..=6], &dna!("ATC")[..]);
698        assert_ne!(&seq[..=6], &dna!("TGCATC")[..]);
699        assert_ne!(&seq[4..5], &dna!("TC")[..]);
700        assert_ne!(&seq[..6], &dna!("TGCAT")[..]);
701        assert_ne!(&seq[5..], &dna!("TCGAT")[..]);
702
703        assert_eq!(&seq[..], &dna!("TGCATCGAT")[..]);
704        assert_eq!(&seq[3..=6], &dna!("ATCG")[..]);
705        assert_eq!(&seq[..=6], &dna!("TGCATCG")[..]);
706        assert_eq!(&seq[4..5], &dna!("T")[..]);
707        assert_eq!(&seq[..6], &dna!("TGCATC")[..]);
708        assert_eq!(&seq[5..], &dna!("CGAT")[..]);
709    }
710
711    #[test]
712    #[expect(clippy::redundant_slicing, reason = "exercise full-range indexing")]
713    fn slice_index_ranges() {
714        let s1: &'static SeqSlice<Dna> = dna!("ACGACTGATCGA");
715        let s2: &'static SeqSlice<Dna> = dna!("TCGAACGACTGA");
716
717        assert_eq!(&s1[..8], &s2[4..]);
718        assert_eq!(&s1[8..], &s2[..4]);
719        assert_ne!(&s1[8..], &s2[8..]);
720        assert_ne!(&s1[..], &s2[..4]);
721
722        assert_eq!(&s1[..=7], &s2[4..]);
723        assert_eq!(&s1[8..], &s2[..=3]);
724        assert_ne!(&s1[8..11], &s2[8..=11]);
725        assert_ne!(&s1[..], &s2[..=4]);
726    }
727
728    #[test]
729    fn slice_nth() {
730        let s = dna!("ATGTGTGCGACTGATGATCAAACGTAGCTACG");
731
732        assert_eq!(s.nth(0), Dna::A);
733        assert_ne!(s.nth(0), Dna::G);
734
735        assert_eq!(s.nth(1), Dna::T);
736        assert_ne!(s.nth(1), Dna::C);
737
738        assert_eq!(s.nth(s.len() - 1), Dna::G);
739        assert_ne!(s.nth(s.len() - 1), Dna::C);
740    }
741
742    #[test]
743    #[expect(clippy::redundant_slicing, reason = "exercise full-range indexing")]
744    fn slice_rangeto_and_full() {
745        let s1 = dna!("ATCGACTAGCATGCTACG");
746        let s2 = dna!("ATCGACTAG");
747
748        assert_eq!(&s1[..s2.len()], &s2[..]);
749        assert_ne!(&s2[..s2.len()], &s1[..]);
750    }
751
752    #[test]
753    fn from_slice() {
754        let s1 = dna!("ATGTGTGCGACTGATGATCAAACGTAGCTACG");
755        let s: &SeqSlice<Dna> = &s1[15..21];
756        assert_eq!(format!("{s}"), "GATCAA");
757    }
758
759    #[test]
760    fn string_to_seq() {
761        let seq_str = "ACTGACTG";
762        let seq: Result<Seq<Dna>, _> = seq_str.try_into();
763        assert!(seq.is_ok());
764        assert_eq!(seq.unwrap().to_string(), seq_str);
765    }
766
767    #[test]
768    fn invalid_string_to_seq() {
769        let invalid_seq_str = "ACUGACTG";
770        let seq: Result<Seq<Dna>, _> = invalid_seq_str.try_into();
771        assert!(seq.is_err());
772    }
773
774    #[test]
775    fn seq_to_string() {
776        let seq_str = "ACTGACTG";
777        let seq: Seq<Dna> = seq_str.try_into().unwrap();
778        let result_str: String = seq.into();
779        assert_eq!(result_str, seq_str);
780    }
781
782    #[test]
783    fn seqslice_to_string() {
784        let seq_str = "ACTGACTG";
785        let seq: Seq<Dna> = seq_str.try_into().unwrap();
786        let slice = &seq[1..5];
787        let result_str: String = slice.into();
788        assert_eq!(result_str, "CTGA");
789    }
790
791    #[test]
792    #[should_panic(expected = "range 2..18 out of bounds: 16")]
793    fn invalid_seqslice_to_string() {
794        let seq_str = "ACTGACTG";
795        let seq: Seq<Dna> = seq_str.try_into().unwrap();
796        let _ = &seq[1..9];
797    }
798
799    #[test]
800    fn test_push() {
801        let mut seq = Seq::<Dna>::new();
802        seq.push(Dna::A);
803        seq.push(Dna::C);
804        seq.push(Dna::G);
805        seq.push(Dna::T);
806
807        assert_eq!(seq.len(), 4);
808        assert_eq!(String::from(seq), "ACGT");
809    }
810
811    #[test]
812    fn test_extend_amino() {
813        let mut seq = Seq::<Amino>::new();
814        seq.push(Amino::S);
815        seq.push(Amino::L);
816
817        seq.extend(vec![Amino::Y, Amino::M]);
818
819        assert_eq!(seq.len(), 4);
820        assert_eq!(String::from(seq), "SLYM");
821    }
822    #[test]
823    fn test_extend() {
824        let mut seq = Seq::<Dna>::new();
825        seq.push(Dna::A);
826        seq.push(Dna::C);
827
828        seq.extend(vec![Dna::G, Dna::T]);
829
830        assert_eq!(seq.len(), 4);
831        assert_eq!(String::from(seq), "ACGT");
832    }
833
834    #[test]
835    fn test_eqs() {
836        let seq: Seq<Dna> = "ACTAGCATCGA".try_into().unwrap();
837        let seq2: Seq<Dna> = "ACTAGCATCGA".try_into().unwrap();
838        let slice: &SeqSlice<Dna> = &seq;
839        let slice2: &SeqSlice<Dna> = &seq2[..];
840        assert_eq!(seq, slice);
841        assert_eq!(seq2, slice);
842        assert_eq!(seq2, slice2);
843        assert_eq!(slice, slice2);
844        assert_eq!(seq, seq2);
845    }
846
847    #[test]
848    fn test_str_eqs() {
849        let string: String = "ACTAGCATCGA".into();
850        let slice: &str = "GCTGCATCGATC";
851
852        let seq1: Seq<Dna> = Seq::<Dna>::try_from(slice).unwrap();
853        let seq2: Seq<Dna> = Seq::<Dna>::try_from(string.clone()).unwrap();
854
855        assert_eq!(seq2.to_string(), string);
856        assert_eq!(seq1.to_string(), slice);
857
858        assert_ne!(seq2.to_string(), slice);
859        assert_ne!(seq1.to_string(), string);
860    }
861
862    #[test]
863    fn test_from_iter() {
864        let iter = vec![Dna::A, Dna::C, Dna::G, Dna::T].into_iter();
865        let seq: Seq<Dna> = Seq::from_iter(iter);
866
867        assert_eq!(seq.len(), 4);
868        assert_eq!(String::from(seq), "ACGT");
869    }
870
871    #[test]
872    fn test_bit_order() {
873        let raw: usize = 0b10_11_01_11_10_01_00_01;
874        let mut bv: Bv = BitVec::default();
875        bv.extend(&raw.view_bits::<Order>()[..(Dna::BITS as usize * 8)]);
876        let s = Seq::<Dna> {
877            bv,
878            _p: PhantomData,
879        };
880        assert_eq!(dna!("CACGTCTG").to_string(), "CACGTCTG");
881        assert_eq!(String::from(s), "CACGTCTG");
882        //        assert_eq!(raw, Kmer::<Dna, 8>::from(&s[..8]).bs);
883    }
884
885    #[test]
886    fn test_borrow() {
887        let seq: Seq<Dna> = dna!("ACGACCCCCATAGATGGGCTG").into();
888        let slice: &SeqSlice<Dna> = seq.borrow();
889        assert_eq!(slice, &seq[..]);
890        assert_ne!(slice, &seq[1..]);
891    }
892
893    #[test]
894    #[expect(
895        clippy::explicit_auto_deref,
896        reason = "exercise the Deref implementation"
897    )]
898    fn test_deref() {
899        let seq: Seq<Dna> = dna!("AGAATGATCG").into();
900        let slice: &SeqSlice<Dna> = &*seq;
901
902        assert_eq!(slice, &seq[..]);
903        assert_ne!(slice, &seq[1..]);
904    }
905
906    #[test]
907    fn test_asref() {
908        let seq: Seq<Dna> = dna!("AGAATGATCAAAATATATATAAAG").into();
909        let slice: &SeqSlice<Dna> = seq.as_ref();
910        assert_ne!(slice, &seq[2..5]);
911        assert_eq!(slice, &seq[..]);
912    }
913
914    #[test]
915    fn test_to_owned() {
916        let seq: Seq<Dna> = dna!("AGAATGAATCG").into();
917        let slice: &SeqSlice<Dna> = &seq;
918        let owned: Seq<Dna> = slice[2..5].to_owned();
919        assert_eq!(&owned, &seq[2..5]);
920        assert_eq!(owned, seq[2..5].to_owned());
921        assert_ne!(&owned, &seq[..]);
922    }
923
924    #[test]
925    fn test_clone() {
926        let seq: Seq<Dna> = dna!("AGAATGATGGGGGGGGGGGCG").into();
927        let cloned = seq.clone();
928        assert_eq!(seq, cloned);
929    }
930    #[test]
931    fn test_trim() {
932        let seq = b"AGAATGATGGGGGGGGGGGCG";
933        let s: Seq<Dna> = Seq::trim_u8(seq).unwrap();
934        assert_eq!(s, dna!("AGAATGATGGGGGGGGGGGCG"));
935
936        let seq = b"NNNNAGAATGATGGGGGGGGGGGCGNNNNNNNNNNN";
937        let s: Seq<Dna> = Seq::trim_u8(seq).unwrap();
938        assert_eq!(s, dna!("AGAATGATGGGGGGGGGGGCG"));
939
940        let seq = b"NNNNAGAATGATGGGGNGGGGGGGCGNNNNNNNNNNN";
941        let s: Result<Seq<Dna>, ParseBioError> = Seq::trim_u8(seq);
942        assert_eq!(s, Err(ParseBioError::UnrecognisedBase(b'N')));
943
944        let seq = b"AGAATGATGGGGGGGGGGGCG";
945        let s: Seq<Dna> = Seq::trim_u8(seq).unwrap();
946        assert_eq!(s, dna!("AGAATGATGGGGGGGGGGGCG"));
947
948        let seq = b"NNNNAGAATGATGGGGGGGGGGGCGNNNNNNNNNNN";
949        let s: Seq<Dna> = Seq::trim_u8(seq).unwrap();
950        assert_eq!(s, dna!("AGAATGATGGGGGGGGGGGCG"));
951
952        let seq = b"NNNNAGAATGATGGGGNGGGGGGGCGNNNNNNNNNNN";
953        let s: Result<Seq<Dna>, ParseBioError> = Seq::trim_u8(seq);
954        assert_eq!(s, Err(ParseBioError::UnrecognisedBase(b'N')));
955
956        let seq = b"";
957        let s: Result<Seq<Dna>, ParseBioError> = Seq::trim_u8(seq);
958        assert!(s.is_ok());
959        assert_eq!(s.unwrap(), dna!(""));
960
961        let seq = b"XXXX";
962        let s: Result<Seq<Dna>, ParseBioError> = Seq::trim_u8(seq);
963        assert!(s.is_ok());
964        assert_eq!(s.unwrap(), dna!(""));
965
966        let seq = b"XXACGT";
967        let s: Seq<Dna> = Seq::trim_u8(seq).unwrap();
968        assert_eq!(s, dna!("ACGT"));
969
970        let seq = b"ACGTXX";
971        let s: Seq<Dna> = Seq::trim_u8(seq).unwrap();
972        assert_eq!(s, dna!("ACGT"));
973
974        let seq = b"ACGTACGTACGTACGTACGTACGT";
975        let s: Seq<Dna> = Seq::trim_u8(seq).unwrap();
976        assert_eq!(s, dna!("ACGTACGTACGTACGTACGTACGT"));
977
978        let seq = b"XXACGTXXACGTXX";
979        let s: Result<Seq<Dna>, ParseBioError> = Seq::trim_u8(seq);
980        assert_eq!(s, Err(ParseBioError::UnrecognisedBase(b'X')));
981
982        let seq = b"A";
983        let s: Seq<Dna> = Seq::trim_u8(seq).unwrap();
984        assert_eq!(s, dna!("A"));
985
986        let seq = b"X";
987        let s: Result<Seq<Dna>, ParseBioError> = Seq::trim_u8(seq);
988        assert!(s.is_ok());
989        assert_eq!(s.unwrap(), dna!(""));
990
991        /*
992                let seq = b"acgtACGT";
993                let s: Seq<Dna> = Seq::trim_u8(seq).unwrap();
994                assert_eq!(s, dna!("ACGTACGT"));
995
996                let seq = b"AcGtAcGt";
997                let s: Seq<Dna> = Seq::trim_u8(seq).unwrap();
998                assert_eq!(s, dna!("ACGTACGT"));
999
1000                let seq = b"AXCXGXTXaXcXgXt";
1001                let s: Result<Seq<Dna>, ParseBioError> = Seq::trim_u8(seq);
1002                assert_eq!(s, Err(ParseBioError::UnrecognisedBase(b'X')));
1003        */
1004    }
1005
1006    #[test]
1007    fn test_seq_eq_and_hash() {
1008        let seq1: Seq<Dna> = "ACGT".try_into().unwrap();
1009        let seq2: Seq<Dna> = "ACGT".try_into().unwrap();
1010
1011        // Equality checks
1012        assert_eq!(seq1, seq2);
1013
1014        // Hash checks
1015        let mut hasher1 = DefaultHasher::new();
1016        seq1.hash(&mut hasher1);
1017        let hash1 = hasher1.finish();
1018
1019        let mut hasher2 = DefaultHasher::new();
1020        seq2.hash(&mut hasher2);
1021        let hash2 = hasher2.finish();
1022
1023        assert_eq!(hash1, hash2);
1024
1025        assert_eq!(record(&seq1), [4, 0, 0, 0, 0, 0, 0, 0, 0xe4]);
1026    }
1027
1028    #[test]
1029    #[expect(
1030        clippy::similar_names,
1031        reason = "names identify the source sequence and slice variant"
1032    )]
1033    fn test_seq_slice_eq() {
1034        let seq1: Seq<Dna> = "ACGTAAAAAAAAAAAAACGTAAAACCCCGGGGTTTTA".try_into().unwrap();
1035        let seq2: Seq<Dna> = "ACGTAAAAAAAAAAAAACGTAAAACCCCGGGGTTTTAA".try_into().unwrap();
1036
1037        let slice1a: &SeqSlice<Dna> = &seq1[..];
1038        let slice1b: &SeqSlice<Dna> = &seq1[..];
1039        let slice2a: &SeqSlice<Dna> = &seq2[..seq2.len() - 1];
1040        let slice2b: &SeqSlice<Dna> = &seq2[..seq2.len() - 1];
1041
1042        let seq3: Seq<Dna> = slice2b.into();
1043
1044        assert_eq!(slice1a, slice2b);
1045        assert_eq!(&slice2a, &slice2b);
1046        assert_eq!(seq1, slice2a);
1047        assert_eq!(slice1b, seq3);
1048        assert_eq!(slice1a, slice1b);
1049        assert_eq!(seq1, seq3);
1050
1051        assert_ne!(seq1, seq2);
1052        assert_ne!(seq2, seq3);
1053        assert_ne!(seq2, slice2a);
1054        assert_ne!(seq2, slice1b);
1055
1056        assert_eq!(&slice1a, &slice1b);
1057
1058        assert_eq!(seq1, &seq1);
1059
1060        assert_eq!(seq1, &seq3);
1061        assert_eq!(&seq1, seq3);
1062        assert_eq!(&seq1, &seq3);
1063
1064        assert_eq!(slice1a, seq3);
1065        //assert_eq!(&slice1a, seq3);
1066
1067        assert_eq!(seq1, slice2a);
1068        assert_eq!(&seq1, slice2a);
1069        //assert_eq!(seq1, &slice2a);
1070
1071        assert_eq!(&seq1, slice2a);
1072    }
1073    #[test]
1074    fn test_seq_slice_hash() {
1075        let seq1: Seq<Dna> = "ACGTAAAAAAAAAAAAACGTAAAACCCCGGGGAAAAA".try_into().unwrap();
1076        let seq2: Seq<Dna> = "ACGTAAAAAAAAAAAAACGTAAAACCCCGGGGAAAAA".try_into().unwrap();
1077
1078        let seq3: Seq<Dna> = "ACGTAAAAAAAAAAAAACGTAAAACCCCGGGG".try_into().unwrap();
1079
1080        let slice1 = &seq1[..];
1081        let slice2 = &seq2[..];
1082
1083        let slice3 = &seq3[..];
1084
1085        let slice1_32 = &seq1[..32];
1086
1087        let mut hasher1 = DefaultHasher::new();
1088        seq1.hash(&mut hasher1);
1089        let full1 = hasher1.finish();
1090
1091        let mut hasher1a = DefaultHasher::new();
1092        seq1.hash(&mut hasher1a);
1093        let full1_alt = hasher1a.finish();
1094
1095        let mut hasher2 = DefaultHasher::new();
1096        seq2.hash(&mut hasher2);
1097        let full2 = hasher2.finish();
1098
1099        let mut hasher3 = DefaultHasher::new();
1100        slice1.hash(&mut hasher3);
1101        let full1_slice = hasher3.finish();
1102
1103        let mut hasher4 = DefaultHasher::new();
1104        slice2.hash(&mut hasher4);
1105        let full2_slice = hasher4.finish();
1106
1107        let mut hasher5 = DefaultHasher::new();
1108        slice3.hash(&mut hasher5);
1109        let short1_slice = hasher5.finish();
1110
1111        let mut hasher6 = DefaultHasher::new();
1112        slice1_32.hash(&mut hasher6);
1113        let seq1_short = hasher6.finish();
1114
1115        assert_eq!(full1, full1_alt);
1116        assert_eq!(full1, full2);
1117        assert_ne!(full2_slice, short1_slice);
1118        assert_eq!(full2, full1_slice);
1119        assert_eq!(short1_slice, seq1_short);
1120        assert_ne!(seq1_short, full1_slice);
1121
1122        assert_ne!(full1, short1_slice);
1123    }
1124
1125    #[test]
1126    fn test_fromstr() {
1127        let seq: Result<Seq<Dna>, ParseBioError> =
1128            "ACGATGAGTAGTCGCCATCGTATCTTTGACTGCCGATGCTA".parse();
1129        assert!(seq.is_ok());
1130
1131        let seq: Result<Seq<Dna>, ParseBioError> =
1132            "ACGATGAGTAGBCGCCATCGTATCTTTGACTGCCGATGCTA".parse();
1133        assert_eq!(seq, Err(ParseBioError::UnrecognisedBase(b'B')));
1134    }
1135
1136    #[test]
1137    fn test_lens() {
1138        assert_eq!(iupac!("AWANWATNA---SKAGTCAA").len(), 20);
1139    }
1140
1141    #[test]
1142    fn test_unique_bitarray_ident() {
1143        let s1 = dna!(
1144            "ATCGACTACGATCGCTACGATCGATCGATCGATCGAATCTCCCGCGCGATCATCGATCATCGCTACGTACGTCGAAAAATATAATGGG"
1145        );
1146        let s2 = dna!(
1147            "ATCGACTACGATCGCTACGATCGATCGATCGATCGAATCTCCCGCGCGATCATCGATCATCGCTACGTACGTCGAAAAATATAATGGG"
1148        );
1149
1150        // Changed first base
1151        let s3 = dna!(
1152            "CTCGACTACGATCGCTACGATCGATCGATCGATCGAATCTCCCGCGCGATCATCGATCATCGCTACGTACGTCGAAAAATATAATGGG"
1153        );
1154
1155        // Added `00` to end
1156        let s4 = dna!(
1157            "ATCGACTACGATCGCTACGATCGATCGATCGATCGAATCTCCCGCGCGATCATCGATCATCGCTACGTACGTCGAAAAATATAATGGGA"
1158        );
1159
1160        // Changed last base
1161        let s5 = dna!(
1162            "ATCGACTACGATCGCTACGATCGATCGATCGATCGAATCTCCCGCGCGATCATCGATCATCGCTACGTACGTCGAAAAATATAATGGC"
1163        );
1164
1165        assert_eq!(s1, s2);
1166        assert_ne!(s1, s3);
1167
1168        // these two tests won't compile because lengths are different
1169        assert_ne!(s1.len(), s4.len());
1170        assert_ne!(s4.len(), s1.len());
1171
1172        assert_ne!(s5, s1);
1173    }
1174
1175    #[test]
1176    fn test_static() {
1177        use crate::seq::SeqArray;
1178
1179        static B: SeqArray<Dna, 5, 1> = SeqArray {
1180            _p: PhantomData,
1181            ba: bitarr![const usize, Lsb0; 1,1,0,1,0,0,1,0,1,1],
1182        };
1183
1184        let s: &'static SeqSlice<Dna> = &B;
1185
1186        let x: &'static str = "TGACT";
1187
1188        assert_eq!(s.to_string(), x);
1189    }
1190
1191    #[test]
1192    fn test_to_from_raw() {
1193        let s = "TCAGCTAGCTACGACTGATCGATCGACTGATGCCGCGCGCGGCGCCGCGCGCGCGCGCCGCGCGCCCCGCGCGCGGCGCGCGCCGCGCGCGCGCGCGGCGCGCGCGCGCGCGCGCGCGCGCGCGCGCGCGC";
1194
1195        let seq: Seq<Dna> = s.try_into().unwrap();
1196        let raw = seq.into_raw();
1197        let new = Seq::<Dna>::from_raw(s.len(), raw);
1198        assert_eq!(new.unwrap(), seq);
1199
1200        let bad = Seq::<Dna>::from_raw(s.len() + 1, raw);
1201        assert_ne!(bad.unwrap(), seq);
1202
1203        let bad = Seq::<Dna>::from_raw(342, raw);
1204        assert_eq!(bad, None);
1205    }
1206    #[test]
1207    fn test_seq_and_seq_slice_eq_and_hash() {
1208        let seq: Seq<text::Dna> = Seq::try_from("ACGT".to_string()).unwrap();
1209        let slice = &seq[..];
1210
1211        // Equality checks
1212        assert_eq!(seq, slice);
1213        assert_eq!(&seq, slice);
1214        assert_eq!(slice, &seq);
1215
1216        // Hash checks
1217        let mut hasher1 = std::collections::hash_map::DefaultHasher::new();
1218        seq.hash(&mut hasher1);
1219        let hash1 = hasher1.finish();
1220
1221        let mut hasher2 = std::collections::hash_map::DefaultHasher::new();
1222        slice.hash(&mut hasher2);
1223        let hash2 = hasher2.finish();
1224
1225        assert_eq!(hash1, hash2);
1226    }
1227
1228    #[derive(Default)]
1229    struct RecordingHasher(Vec<u8>);
1230
1231    impl Hasher for RecordingHasher {
1232        fn finish(&self) -> u64 {
1233            0
1234        }
1235        fn write(&mut self, bytes: &[u8]) {
1236            self.0.extend_from_slice(bytes);
1237        }
1238    }
1239
1240    fn record<T: Hash>(value: &T) -> Vec<u8> {
1241        let mut h = RecordingHasher::default();
1242        value.hash(&mut h);
1243        h.0
1244    }
1245
1246    #[test]
1247    fn test_hash_byte_stream_invariants() {
1248        // Same logical content must produce the same byte stream regardless of
1249        // how it is spelt (Seq vs &SeqSlice) and across a range of codecs and
1250        // lengths — including ones with partial trailing bytes and ones long
1251        // enough to cross the internal flush buffer in `hash_bits`.
1252        fn check<A: Codec>(s: &str) {
1253            let seq: Seq<A> = s.try_into().unwrap_or_else(|_| panic!("parse {s:?}"));
1254            let bytes = record(&seq);
1255            assert_eq!(record::<&SeqSlice<A>>(&&seq[..]), bytes);
1256
1257            // Structural: 8-byte LE length prefix, then ceil(bits / 8) bytes.
1258            let n = seq.len();
1259            let body = (n * A::BITS as usize).div_ceil(8);
1260            assert_eq!(bytes.len(), 8 + body);
1261            assert_eq!(&bytes[..8], &(n as u64).to_le_bytes());
1262        }
1263        check::<Dna>("A");
1264        check::<Dna>("ACGT");
1265        check::<Dna>("ACGTA");
1266        check::<Dna>(&"ACGT".repeat(80)); // > 64-byte flush in `hash_bits`
1267        check::<Iupac>("AC");
1268        check::<Iupac>("ACG");
1269        check::<Iupac>("NRYKBDHV");
1270        check::<Amino>("MWLLP"); // 6-bit codec, partial trailing byte
1271        check::<text::Dna>("ACGT"); // 8-bit codec, byte-aligned
1272    }
1273
1274    #[test]
1275    fn test_kmer_hash_independent_of_storage_width() {
1276        let seq: Seq<Dna> = "ACGTACGTAC".try_into().unwrap();
1277        let slice: &SeqSlice<Dna> = &seq[..];
1278        let bytes = record(&slice);
1279        let km_usize: Kmer<Dna, 10, usize> = slice.try_into().unwrap();
1280        let km_u64: Kmer<Dna, 10, u64> = slice.try_into().unwrap();
1281        let km_u128: Kmer<Dna, 10, u128> = slice.try_into().unwrap();
1282        assert_eq!(record(&km_usize), bytes);
1283        assert_eq!(record(&km_u64), bytes);
1284        assert_eq!(record(&km_u128), bytes);
1285    }
1286
1287    #[test]
1288    fn test_prepend() {
1289        let mut seq1 =
1290            Seq::<Dna>::from_str("GCTCGATCGATCGATCGACTGACTGACGCGCGCATCCGATAAAAAAAAT").unwrap();
1291        seq1.prepend(dna!("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"));
1292        assert_eq!(
1293            seq1.to_string(),
1294            "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGCTCGATCGATCGATCGACTGACTGACGCGCGCATCCGATAAAAAAAAT"
1295        );
1296
1297        let mut seq2 =
1298            Seq::<Dna>::from_str("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA").unwrap();
1299        seq2.prepend(dna!("GCTCGATCGATCGATCGACTGACTGACGCGCGCATCCGATAAAAAAAAT"));
1300        assert_eq!(
1301            seq2.to_string(),
1302            "GCTCGATCGATCGATCGACTGACTGACGCGCGCATCCGATAAAAAAAATAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
1303        );
1304
1305        assert_ne!(seq1, seq2);
1306    }
1307
1308    #[test]
1309    fn test_append() {
1310        let mut seq1 =
1311            Seq::<Dna>::from_str("GCTCGATCGATCGATCGACTGACTGACGCGCGCATCCGATAAAAAAAAT").unwrap();
1312        seq1.append(dna!("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"));
1313        assert_eq!(
1314            seq1.to_string(),
1315            "GCTCGATCGATCGATCGACTGACTGACGCGCGCATCCGATAAAAAAAATAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
1316        );
1317
1318        let mut seq2 =
1319            Seq::<Dna>::from_str("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA").unwrap();
1320        seq2.append(dna!("GCTCGATCGATCGATCGACTGACTGACGCGCGCATCCGATAAAAAAAAT"));
1321        assert_eq!(
1322            seq2.to_string(),
1323            "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGCTCGATCGATCGATCGACTGACTGACGCGCGCATCCGATAAAAAAAAT"
1324        );
1325
1326        assert_ne!(seq1, seq2);
1327    }
1328
1329    #[test]
1330    fn test_remove() {
1331        let mut seq: Seq<Dna> = dna!("TCAGCATCGATCAATCG").into();
1332        seq.remove(4..6);
1333        assert_eq!(&seq, dna!("TCAGTCGATCAATCG"));
1334        seq.remove(..4);
1335        assert_eq!(&seq, dna!("TCGATCAATCG"));
1336        seq.remove(6..);
1337        assert_eq!(&seq, dna!("TCGATC"));
1338    }
1339
1340    #[test]
1341    fn test_insert() {
1342        let mut seq: Seq<Dna> = dna!("TCAGCATCGATCAATCG").into();
1343        let insertion = dna!("CCCCC");
1344
1345        seq.insert(4, insertion);
1346        assert_eq!(&seq, dna!("TCAGCCCCCCATCGATCAATCG"));
1347
1348        seq.insert(seq.len(), dna!("AAAAA"));
1349        assert_eq!(&seq, dna!("TCAGCCCCCCATCGATCAATCGAAAAA"));
1350    }
1351
1352    #[test]
1353    fn test_truncate() {
1354        let mut seq: Seq<Dna> = dna!("TCAGCATCGATCAATCG").into();
1355
1356        seq.truncate(seq.len());
1357        assert_eq!(&seq, dna!("TCAGCATCGATCAATCG"));
1358
1359        seq.truncate(10);
1360        assert_eq!(&seq, dna!("TCAGCATCGA"));
1361
1362        seq.truncate(0);
1363        assert_eq!(&seq, dna!(""));
1364    }
1365
1366    #[test]
1367    fn test_long_splice() {
1368        let mut seq: Seq<Dna> = dna!("TTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTT").into();
1369        let insertion = dna!("CCCCCCC");
1370
1371        seq.splice(32..38, insertion);
1372        assert_eq!(
1373            &seq,
1374            dna!("TTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTCCCCCCCTTTTTTTTTTT")
1375        );
1376
1377        seq.splice(1..=1, dna!("AAA"));
1378        assert_eq!(
1379            &seq,
1380            dna!("TAAATTTTTTTTTTTTTTTTTTTTTTTTTTTTTTCCCCCCCTTTTTTTTTTT")
1381        );
1382
1383        seq.splice(10.., dna!("GGGG"));
1384        assert_eq!(&seq, dna!("TAAATTTTTTGGGG"));
1385    }
1386
1387    #[test]
1388    fn test_splice() {
1389        let mut seq: Seq<Dna> = dna!("TCAGCATCGATCAATCGT").into();
1390        let insertion = dna!("CCCCC");
1391
1392        seq.splice(4..6, insertion);
1393        assert_eq!(&seq, dna!("TCAGCCCCCTCGATCAATCGT"));
1394
1395        seq.splice(1..=1, dna!("AAA"));
1396        assert_eq!(&seq, dna!("TAAAAGCCCCCTCGATCAATCGT"));
1397
1398        seq.splice(10.., dna!("TTTT"));
1399        assert_eq!(&seq, dna!("TAAAAGCCCCTTTT"));
1400    }
1401}