use crate::{
alignment::{Alignment, MaybeAligned, ProfileError, ScoreAndRanges, SeqSrc, StripedProfile, validate_profile_args},
data::matrices::WeightMatrix,
};
use std::{borrow::Cow, cell::OnceCell, sync::OnceLock};
pub trait ProfileSets<'a, const M: usize, const N: usize, const O: usize, const S: usize>: Sized {
fn get_i8(&self) -> &StripedProfile<'a, i8, M, S>;
fn get_i16(&self) -> &StripedProfile<'a, i16, N, S>;
fn get_i32(&self) -> &StripedProfile<'a, i32, O, S>;
fn sequence(&self) -> &[u8];
fn gap_open(&self) -> i8;
fn gap_extend(&self) -> i8;
fn matrix(&self) -> &WeightMatrix<'a, i8, S>;
#[inline]
#[must_use]
fn sw_score_from_i8<T>(&self, seq: &T) -> MaybeAligned<u32>
where
T: AsRef<[u8]> + ?Sized, {
self.get_i8()
.sw_score(seq)
.or_else_overflowed(|| self.get_i16().sw_score(seq))
.or_else_overflowed(|| self.get_i32().sw_score(seq))
}
#[inline]
#[must_use]
fn sw_score_from_i16<T>(&self, seq: &T) -> MaybeAligned<u32>
where
T: AsRef<[u8]> + ?Sized, {
self.get_i16()
.sw_score(seq)
.or_else_overflowed(|| self.get_i32().sw_score(seq))
}
#[inline]
#[must_use]
fn sw_score_from_i32<T>(&self, seq: &T) -> MaybeAligned<u32>
where
T: AsRef<[u8]> + ?Sized, {
self.get_i32().sw_score(seq)
}
#[inline]
#[must_use]
fn sw_align_from_i8<T>(&self, seq: SeqSrc<&T>) -> MaybeAligned<Alignment<u32>>
where
T: AsRef<[u8]> + ?Sized, {
let seq = seq.map(AsRef::as_ref);
self.get_i8()
.sw_align(seq)
.or_else_overflowed(|| self.get_i16().sw_align(seq))
.or_else_overflowed(|| self.get_i32().sw_align(seq))
}
#[inline]
#[must_use]
fn sw_align_from_i16<T>(&self, seq: SeqSrc<&T>) -> MaybeAligned<Alignment<u32>>
where
T: AsRef<[u8]> + ?Sized, {
let seq = seq.map(AsRef::as_ref);
self.get_i16()
.sw_align(seq)
.or_else_overflowed(|| self.get_i32().sw_align(seq))
}
#[inline]
#[must_use]
fn sw_align_from_i32<T>(&self, seq: SeqSrc<&T>) -> MaybeAligned<Alignment<u32>>
where
T: AsRef<[u8]> + ?Sized, {
let seq = seq.map(AsRef::as_ref);
self.get_i32().sw_align(seq)
}
#[inline]
#[must_use]
fn sw_align_from_i8_3pass<T>(&self, seq: SeqSrc<&T>) -> MaybeAligned<Alignment<u32>>
where
T: AsRef<[u8]> + ?Sized, {
let seq = seq.map(AsRef::as_ref);
self.get_i8()
.sw_align_3pass(seq, self.sequence(), self.matrix(), self.gap_open(), self.gap_extend())
.or_else_overflowed(|| {
self.get_i16()
.sw_align_3pass(seq, self.sequence(), self.matrix(), self.gap_open(), self.gap_extend())
})
.or_else_overflowed(|| {
self.get_i32()
.sw_align_3pass(seq, self.sequence(), self.matrix(), self.gap_open(), self.gap_extend())
})
}
#[inline]
#[must_use]
fn sw_align_from_i16_3pass<T>(&self, seq: SeqSrc<&T>) -> MaybeAligned<Alignment<u32>>
where
T: AsRef<[u8]> + ?Sized, {
let seq = seq.map(AsRef::as_ref);
self.get_i16()
.sw_align_3pass(seq, self.sequence(), self.matrix(), self.gap_open(), self.gap_extend())
.or_else_overflowed(|| {
self.get_i32()
.sw_align_3pass(seq, self.sequence(), self.matrix(), self.gap_open(), self.gap_extend())
})
}
#[inline]
#[must_use]
fn sw_align_from_i32_3pass<T>(&self, seq: SeqSrc<&T>) -> MaybeAligned<Alignment<u32>>
where
T: AsRef<[u8]> + ?Sized, {
let seq = seq.map(AsRef::as_ref);
self.get_i32()
.sw_align_3pass(seq, self.sequence(), self.matrix(), self.gap_open(), self.gap_extend())
}
#[inline]
#[must_use]
fn sw_score_ranges_from_i8<T>(&self, seq: SeqSrc<&T>) -> MaybeAligned<ScoreAndRanges<u32>>
where
T: AsRef<[u8]> + ?Sized, {
let seq = seq.map(AsRef::as_ref);
self.get_i8()
.sw_score_ranges(seq)
.or_else_overflowed(|| self.get_i16().sw_score_ranges(seq))
.or_else_overflowed(|| self.get_i32().sw_score_ranges(seq))
}
#[inline]
#[must_use]
fn sw_score_ranges_from_i16<T>(&self, seq: SeqSrc<&T>) -> MaybeAligned<ScoreAndRanges<u32>>
where
T: AsRef<[u8]> + ?Sized, {
let seq = seq.map(AsRef::as_ref);
self.get_i16()
.sw_score_ranges(seq)
.or_else_overflowed(|| self.get_i32().sw_score_ranges(seq))
}
#[inline]
#[must_use]
fn sw_score_ranges_from_i32<T>(&self, seq: SeqSrc<&T>) -> MaybeAligned<ScoreAndRanges<u32>>
where
T: AsRef<[u8]> + ?Sized, {
let seq = seq.map(AsRef::as_ref);
self.get_i32().sw_score_ranges(seq)
}
}
#[derive(Debug, Clone)]
pub struct LocalProfiles<'a, const M: usize, const N: usize, const O: usize, const S: usize> {
pub(crate) seq: Cow<'a, [u8]>,
pub(crate) matrix: &'a WeightMatrix<'a, i8, S>,
pub(crate) gap_open: i8,
pub(crate) gap_extend: i8,
pub(crate) profile_i8: OnceCell<StripedProfile<'a, i8, M, S>>,
pub(crate) profile_i16: OnceCell<StripedProfile<'a, i16, N, S>>,
pub(crate) profile_i32: OnceCell<StripedProfile<'a, i32, O, S>>,
}
impl<'a, const M: usize, const N: usize, const O: usize, const S: usize> LocalProfiles<'a, M, N, O, S> {
#[inline]
pub fn new(
seq: impl Into<Cow<'a, [u8]>>, matrix: &'a WeightMatrix<'a, i8, S>, gap_open: i8, gap_extend: i8,
) -> Result<Self, ProfileError> {
let seq = seq.into();
validate_profile_args(&seq, gap_open, gap_extend)?;
Ok(LocalProfiles {
seq,
matrix,
gap_open,
gap_extend,
profile_i8: OnceCell::new(),
profile_i16: OnceCell::new(),
profile_i32: OnceCell::new(),
})
}
}
impl<'a, const S: usize> LocalProfiles<'a, 16, 8, 4, S> {
#[inline]
pub fn new_with_w128(
seq: impl Into<Cow<'a, [u8]>>, matrix: &'a WeightMatrix<i8, S>, gap_open: i8, gap_extend: i8,
) -> Result<Self, ProfileError> {
Self::new(seq, matrix, gap_open, gap_extend)
}
}
impl<'a, const S: usize> LocalProfiles<'a, 32, 16, 8, S> {
#[inline]
pub fn new_with_w256(
seq: impl Into<Cow<'a, [u8]>>, matrix: &'a WeightMatrix<i8, S>, gap_open: i8, gap_extend: i8,
) -> Result<Self, ProfileError> {
Self::new(seq, matrix, gap_open, gap_extend)
}
}
impl<'a, const S: usize> LocalProfiles<'a, 64, 32, 16, S> {
#[inline]
pub fn new_with_w512(
seq: impl Into<Cow<'a, [u8]>>, matrix: &'a WeightMatrix<i8, S>, gap_open: i8, gap_extend: i8,
) -> Result<Self, ProfileError> {
Self::new(seq, matrix, gap_open, gap_extend)
}
}
impl<'a, const M: usize, const N: usize, const O: usize, const S: usize> ProfileSets<'a, M, N, O, S>
for LocalProfiles<'a, M, N, O, S>
{
#[inline]
fn get_i8(&self) -> &StripedProfile<'a, i8, M, S> {
self.profile_i8
.get_or_init(|| StripedProfile::new_unchecked(&self.seq, self.matrix, self.gap_open, self.gap_extend))
}
#[inline]
fn get_i16(&self) -> &StripedProfile<'a, i16, N, S> {
self.profile_i16
.get_or_init(|| StripedProfile::new_unchecked(&self.seq, self.matrix, self.gap_open, self.gap_extend))
}
#[inline]
fn get_i32(&self) -> &StripedProfile<'a, i32, O, S> {
self.profile_i32
.get_or_init(|| StripedProfile::new_unchecked(&self.seq, self.matrix, self.gap_open, self.gap_extend))
}
#[inline]
fn sequence(&self) -> &[u8] {
&self.seq
}
#[inline]
fn gap_open(&self) -> i8 {
self.gap_open
}
#[inline]
fn gap_extend(&self) -> i8 {
self.gap_extend
}
#[inline]
fn matrix(&self) -> &WeightMatrix<'a, i8, S> {
self.matrix
}
}
#[derive(Debug, Clone)]
pub struct SharedProfiles<'a, const M: usize, const N: usize, const O: usize, const S: usize> {
pub(crate) seq: Cow<'a, [u8]>,
pub(crate) matrix: &'a WeightMatrix<'a, i8, S>,
pub(crate) gap_open: i8,
pub(crate) gap_extend: i8,
pub(crate) profile_i8: OnceLock<StripedProfile<'a, i8, M, S>>,
pub(crate) profile_i16: OnceLock<StripedProfile<'a, i16, N, S>>,
pub(crate) profile_i32: OnceLock<StripedProfile<'a, i32, O, S>>,
}
impl<'a, const M: usize, const N: usize, const O: usize, const S: usize> SharedProfiles<'a, M, N, O, S> {
#[inline]
pub fn new(
seq: impl Into<Cow<'a, [u8]>>, matrix: &'a WeightMatrix<'a, i8, S>, gap_open: i8, gap_extend: i8,
) -> Result<Self, ProfileError> {
let seq = seq.into();
validate_profile_args(&seq, gap_open, gap_extend)?;
Ok(SharedProfiles {
seq,
matrix,
gap_open,
gap_extend,
profile_i8: OnceLock::new(),
profile_i16: OnceLock::new(),
profile_i32: OnceLock::new(),
})
}
}
impl<'a, const S: usize> SharedProfiles<'a, 16, 8, 4, S> {
#[inline]
pub fn new_with_w128(
seq: impl Into<Cow<'a, [u8]>>, matrix: &'a WeightMatrix<i8, S>, gap_open: i8, gap_extend: i8,
) -> Result<SharedProfiles<'a, 16, 8, 4, S>, ProfileError> {
Self::new(seq, matrix, gap_open, gap_extend)
}
}
impl<'a, const S: usize> SharedProfiles<'a, 32, 16, 8, S> {
#[inline]
pub fn new_with_w256(
seq: impl Into<Cow<'a, [u8]>>, matrix: &'a WeightMatrix<i8, S>, gap_open: i8, gap_extend: i8,
) -> Result<SharedProfiles<'a, 32, 16, 8, S>, ProfileError> {
Self::new(seq, matrix, gap_open, gap_extend)
}
}
impl<'a, const S: usize> SharedProfiles<'a, 64, 32, 16, S> {
#[inline]
pub fn new_with_w512(
seq: impl Into<Cow<'a, [u8]>>, matrix: &'a WeightMatrix<i8, S>, gap_open: i8, gap_extend: i8,
) -> Result<SharedProfiles<'a, 64, 32, 16, S>, ProfileError> {
Self::new(seq, matrix, gap_open, gap_extend)
}
}
impl<'a, const M: usize, const N: usize, const O: usize, const S: usize> ProfileSets<'a, M, N, O, S>
for SharedProfiles<'a, M, N, O, S>
{
#[inline]
fn get_i8(&self) -> &StripedProfile<'a, i8, M, S> {
self.profile_i8
.get_or_init(|| StripedProfile::new_unchecked(&self.seq, self.matrix, self.gap_open, self.gap_extend))
}
#[inline]
fn get_i16(&self) -> &StripedProfile<'a, i16, N, S> {
self.profile_i16
.get_or_init(|| StripedProfile::new_unchecked(&self.seq, self.matrix, self.gap_open, self.gap_extend))
}
#[inline]
fn get_i32(&self) -> &StripedProfile<'a, i32, O, S> {
self.profile_i32
.get_or_init(|| StripedProfile::new_unchecked(&self.seq, self.matrix, self.gap_open, self.gap_extend))
}
#[inline]
fn sequence(&self) -> &[u8] {
&self.seq
}
#[inline]
fn gap_open(&self) -> i8 {
self.gap_open
}
#[inline]
fn gap_extend(&self) -> i8 {
self.gap_extend
}
#[inline]
fn matrix(&self) -> &WeightMatrix<'a, i8, S> {
self.matrix
}
}
#[cfg(test)]
mod test {
use super::*;
use crate::alignment::sw::test_data::{GAP_EXTEND, GAP_OPEN, WEIGHTS};
#[allow(clippy::cast_possible_wrap)]
#[test]
fn sw_simd_profile_set() {
let v: &[u8] = include_bytes!(concat!(env!("CARGO_MANIFEST_DIR"), "/tests/data/CY137594.txt"));
let profiles = LocalProfiles::new_with_w256(v, &WEIGHTS, GAP_OPEN, GAP_EXTEND).unwrap();
let profile1 = profiles.get_i8();
let profile2 = StripedProfile::<i8, 32, 5>::new(v, &WEIGHTS, GAP_OPEN, GAP_EXTEND).unwrap();
assert_eq!(profile1, &profile2);
}
}