use std::{
error::Error,
fmt, io, iter, num,
ops::{Deref, DerefMut},
str::FromStr,
};
use super::{index::Index, version::Version};
const SEP: &str = "\t";
pub type Id = usize;
#[derive(Clone, Debug, PartialEq)]
pub struct Likelihoods(Box<[f32]>);
impl AsRef<[f32]> for Likelihoods {
fn as_ref(&self) -> &[f32] {
&self.0
}
}
impl AsMut<[f32]> for Likelihoods {
fn as_mut(&mut self) -> &mut [f32] {
&mut self.0
}
}
impl Deref for Likelihoods {
type Target = [f32];
#[inline]
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl DerefMut for Likelihoods {
#[inline]
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}
impl From<Box<[f32]>> for Likelihoods {
fn from(likelihoods: Box<[f32]>) -> Self {
Self(likelihoods)
}
}
impl From<Likelihoods> for Box<[f32]> {
fn from(likelihoods: Likelihoods) -> Self {
likelihoods.0
}
}
impl From<Vec<f32>> for Likelihoods {
fn from(likelihoods: Vec<f32>) -> Self {
likelihoods.into_boxed_slice().into()
}
}
#[derive(Clone, Debug, PartialEq)]
pub struct Band {
start: usize,
likelihoods: Vec<f32>,
}
impl Band {
pub fn into_full(self, alleles: usize, fill: f32) -> Likelihoods {
let mut v = self.likelihoods;
v.splice(0..0, iter::repeat(fill).take(self.start));
v.extend(iter::repeat(fill).take(alleles + 1 - v.len()));
v.into()
}
pub fn into_likelihoods(self) -> Vec<f32> {
self.likelihoods
}
#[allow(clippy::len_without_is_empty)]
pub fn len(&self) -> usize {
self.likelihoods.len()
}
pub fn likelihoods(&self) -> &[f32] {
&self.likelihoods
}
pub fn likelihoods_mut(&mut self) -> &mut Vec<f32> {
&mut self.likelihoods
}
pub fn new(start: usize, likelihoods: Vec<f32>) -> Self {
Self { start, likelihoods }
}
pub fn start(&self) -> usize {
self.start
}
pub fn start_mut(&mut self) -> &mut usize {
&mut self.start
}
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct Record<I, T> {
contig_id: I,
position: u32,
item: T,
}
impl<I, T> Record<I, T> {
pub fn contig_id(&self) -> &I {
&self.contig_id
}
pub fn contig_id_mut(&mut self) -> &mut I {
&mut self.contig_id
}
pub fn into_item(self) -> T {
self.item
}
pub fn item(&self) -> &T {
&self.item
}
pub fn item_mut(&mut self) -> &mut T {
&mut self.item
}
pub fn new(contig_id: I, position: u32, item: T) -> Self {
Self {
contig_id,
position,
item,
}
}
pub fn position(&self) -> u32 {
self.position
}
pub fn position_mut(&mut self) -> &mut u32 {
&mut self.position
}
}
impl<I> Record<I, Likelihoods> {
pub fn alleles(&self) -> usize {
self.item.len() - 1
}
pub fn from_alleles(contig_id: I, position: u32, alleles: usize) -> Self {
let item = vec![0.0; alleles + 1].into();
Self::new(contig_id, position, item)
}
}
impl<I> Record<I, Band> {
pub fn into_full(self, alleles: usize, fill: f32) -> Record<I, Likelihoods> {
Record::new(
self.contig_id,
self.position,
self.item.into_full(alleles, fill),
)
}
}
impl<T> Record<Id, T> {
pub fn to_named<V>(self, index: &Index<V>) -> Record<&str, T>
where
V: Version,
{
let name = index.records()[self.contig_id].name();
Record::new(name, self.position, self.item)
}
}
impl<I> fmt::Display for Record<I, Likelihoods>
where
I: fmt::Display,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.contig_id)?;
write!(f, "{SEP}{}", self.position)?;
for v in self.item().iter() {
f.write_str(SEP)?;
v.fmt(f)?;
}
Ok(())
}
}
impl<I> fmt::Display for Record<I, Band>
where
I: fmt::Display,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.contig_id)?;
write!(f, "{SEP}{}", self.position)?;
for _ in 0..self.item.start {
f.write_str(SEP)?;
f.write_str(".")?;
}
for v in self.item.likelihoods.iter() {
f.write_str(SEP)?;
v.fmt(f)?;
}
Ok(())
}
}
impl FromStr for Record<String, Likelihoods> {
type Err = ParseRecordError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let mut iter = s.split_whitespace();
let contig_id = iter
.next()
.ok_or(ParseRecordError::MissingContigId)?
.to_string();
let position = iter
.next()
.ok_or(ParseRecordError::MissingPosition)?
.parse()
.map_err(ParseRecordError::InvalidPosition)?;
let item = iter
.map(|v| v.parse())
.collect::<Result<Vec<f32>, _>>()
.map_err(ParseRecordError::InvalidLikelihoods)?;
if !item.is_empty() {
Ok(Self::new(contig_id, position, item.into()))
} else {
Err(ParseRecordError::MissingLikelihoods)
}
}
}
#[derive(Debug)]
pub enum ParseRecordError {
MissingContigId,
MissingPosition,
InvalidPosition(num::ParseIntError),
MissingLikelihoods,
InvalidLikelihoods(num::ParseFloatError),
}
impl fmt::Display for ParseRecordError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
ParseRecordError::MissingContigId => f.write_str("missing record contig ID"),
ParseRecordError::MissingPosition => f.write_str("missing record position"),
ParseRecordError::InvalidPosition(e) => {
write!(f, "failed to parse record position: '{e}'")
}
ParseRecordError::MissingLikelihoods => f.write_str("missing record likelihoods"),
ParseRecordError::InvalidLikelihoods(e) => {
write!(f, "failed to parse record likelihoods: '{e}'")
}
}
}
}
impl Error for ParseRecordError {}
impl From<ParseRecordError> for io::Error {
fn from(error: ParseRecordError) -> Self {
io::Error::new(io::ErrorKind::InvalidData, error)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_into_full_basic() {
assert_eq!(
Record::new("1", 1, Band::new(2, vec![1.; 2])).into_full(6, 0.),
Record::new("1", 1, Likelihoods::from(vec![0., 0., 1., 1., 0., 0., 0.]))
);
}
#[test]
fn test_into_full_no_start() {
assert_eq!(
Record::new("1", 10, Band::new(0, vec![2.; 3])).into_full(4, -1.),
Record::new("1", 10, Likelihoods::from(vec![2., 2., 2., -1., -1.]))
);
}
#[test]
fn test_into_full_no_tail() {
assert_eq!(
Record::new("10", 1, Band::new(2, vec![2.; 3])).into_full(4, -1.),
Record::new("10", 1, Likelihoods::from(vec![-1., -1., 2., 2., 2.]))
);
}
#[test]
fn test_into_full_no_fill() {
assert_eq!(
Record::new("2", 2, Band::new(0, vec![0., 1., 2.])).into_full(2, 0.),
Record::new("2", 2, Likelihoods::from(vec![0., 1., 2.]))
);
}
}