use crate::{
data::{
mappings::DNA_COUNT_PROFILE_MAP,
types::nucleotides::{Nucleotides, NucleotidesReadable},
},
math::Uint,
private::Sealed,
};
use std::ops::{Add, AddAssign};
use std::simd::{SimdElement, num::SimdUint, prelude::*};
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
pub struct NucleotideCounts<T: Uint> {
inner: [T; 8],
}
impl<T> NucleotideCounts<T>
where
T: Uint,
{
#[inline]
#[must_use]
pub fn new() -> Self {
NucleotideCounts { inner: [T::ZERO; 8] }
}
#[inline]
#[must_use]
pub fn into_inner(self) -> [T; 8] {
self.inner
}
#[inline]
#[must_use]
pub fn a(&self) -> T {
self.inner[0]
}
#[inline]
#[must_use]
pub fn c(&self) -> T {
self.inner[1]
}
#[inline]
#[must_use]
pub fn g(&self) -> T {
self.inner[2]
}
#[inline]
#[must_use]
pub fn t(&self) -> T {
self.inner[3]
}
#[inline]
#[must_use]
pub fn n(&self) -> T {
self.inner[4]
}
#[inline]
#[must_use]
pub fn gap(&self) -> T {
self.inner[5]
}
#[inline]
#[must_use]
pub fn other(&self) -> T {
self.inner[6]
}
#[inline]
#[must_use]
pub fn invalid(&self) -> T {
self.inner[7]
}
#[inline]
#[must_use]
pub fn total_at(&self) -> T
where
T: Add<Output = T>, {
self.a() + self.t()
}
#[inline]
#[must_use]
pub fn total_gc(&self) -> T
where
T: Add<Output = T>, {
self.g() + self.c()
}
#[inline]
#[must_use]
pub fn total_acgt(&self) -> T
where
T: Add<Output = T>, {
self.inner[0..4].iter().fold(T::ZERO, |acc, &v| acc + v)
}
#[inline]
#[must_use]
pub fn total_acgtn(&self) -> T
where
T: Add<Output = T>, {
self.inner[0..5].iter().fold(T::ZERO, |acc, &v| acc + v)
}
#[inline]
#[must_use]
pub fn total_acgtn_gap(&self) -> T
where
T: Add<Output = T>, {
self.inner[0..6].iter().fold(T::ZERO, |acc, &v| acc + v)
}
#[inline]
#[must_use]
pub fn total_valid(&self) -> T
where
T: Add<Output = T>, {
self.inner[0..7].iter().fold(T::ZERO, |acc, &v| acc + v)
}
#[inline]
#[must_use]
pub fn plurality_acgtn(&self) -> u8 {
let mut max_count = self.inner[0];
let mut max_idx = 0;
for i in 1..5 {
if self.inner[i] > max_count {
max_count = self.inner[i];
max_idx = i;
}
}
b"ACGTN"[max_idx]
}
}
impl<T: Uint> Default for NucleotideCounts<T> {
#[inline]
fn default() -> Self {
Self::new()
}
}
impl<T> NucleotideCounts<T>
where
T: Uint + SimdElement,
Simd<T, 8>: SimdUint<Scalar = T>,
{
#[inline]
#[must_use]
pub fn total_any(&self) -> T {
Simd::from_array(self.inner).reduce_sum()
}
}
impl<T> From<u8> for NucleotideCounts<T>
where
T: Uint,
{
#[inline]
fn from(b: u8) -> Self {
let mut nc = NucleotideCounts { inner: [T::ZERO; 8] };
nc.inner[DNA_COUNT_PROFILE_MAP[b] as usize] = T::ONE;
nc
}
}
impl<T> AddAssign for NucleotideCounts<T>
where
T: Uint + SimdElement,
Simd<T, 8>: Add<Output = Simd<T, 8>>,
{
#[inline]
fn add_assign(&mut self, other: Self) {
let added = Simd::from_array(self.inner) + Simd::from_array(other.inner);
*self = Self { inner: added.to_array() };
}
}
impl<T> Add for NucleotideCounts<T>
where
T: Uint + SimdElement,
Simd<T, 8>: Add<Output = Simd<T, 8>>,
{
type Output = Self;
#[inline]
fn add(self, other: Self) -> Self {
let added: Simd<T, 8> = Simd::from_array(self.inner) + Simd::from_array(other.inner);
Self { inner: added.to_array() }
}
}
impl<T> Add<u8> for NucleotideCounts<T>
where
T: Uint + AddAssign,
{
type Output = Self;
#[inline]
fn add(mut self, other: u8) -> Self {
self.inner[DNA_COUNT_PROFILE_MAP[other] as usize] += T::ONE;
self
}
}
impl<T> AddAssign<u8> for NucleotideCounts<T>
where
T: Uint + AddAssign,
{
#[inline]
fn add_assign(&mut self, other: u8) {
self.inner[DNA_COUNT_PROFILE_MAP[other] as usize] += T::ONE;
}
}
pub trait AlignmentComposition: Sealed {
fn per_site_counts<T>(&mut self) -> Option<Vec<NucleotideCounts<T>>>
where
Self: Sized + Iterator,
Self::Item: IntoIterator<Item = u8> + Sized,
T: Uint, {
if let Some(first) = self.next() {
let mut counts: Vec<NucleotideCounts<T>> = first.into_iter().map(NucleotideCounts::from).collect();
for v in self {
for (c, b) in counts.iter_mut().zip(v) {
*c += b;
}
}
Some(counts)
} else {
None
}
}
}
impl<I: Iterator + Sealed> AlignmentComposition for I {}
pub trait ToBaseCounts: NucleotidesReadable + Sealed {
#[inline]
#[must_use]
fn to_base_counts<T: Uint>(&self) -> NucleotideCounts<T> {
self.nucleotide_bytes()
.iter()
.fold(NucleotideCounts::new(), |acc, &b| acc + b)
}
}
impl<T: NucleotidesReadable + Sealed> ToBaseCounts for T {}
pub trait CreateConsensus: Sealed {
#[must_use]
fn plurality_acgtn(&self) -> Nucleotides;
}
impl<T: Sealed> CreateConsensus for Vec<NucleotideCounts<T>>
where
T: Uint + Ord + Add<Output = T>,
{
#[inline]
fn plurality_acgtn(&self) -> Nucleotides {
self.iter().map(NucleotideCounts::plurality_acgtn).collect()
}
}
#[cfg(test)]
mod test;