use super::location::{CdsPos, GenomePos, ProtPos, RnaPos, TxPos};
use super::uncertainty::Mu;
use serde::{Deserialize, Serialize};
use std::fmt;
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum UncertainBoundary<T> {
Single(Mu<T>),
Range {
start: Mu<T>,
end: Mu<T>,
},
}
impl<T> UncertainBoundary<T> {
pub fn certain(pos: T) -> Self {
Self::Single(Mu::Certain(pos))
}
pub fn uncertain(pos: T) -> Self {
Self::Single(Mu::Uncertain(pos))
}
pub fn unknown() -> Self {
Self::Single(Mu::Unknown)
}
pub fn range(start: Mu<T>, end: Mu<T>) -> Self {
Self::Range { start, end }
}
pub fn is_single(&self) -> bool {
matches!(self, Self::Single(_))
}
pub fn is_range(&self) -> bool {
matches!(self, Self::Range { .. })
}
pub fn as_single(&self) -> Option<&Mu<T>> {
match self {
Self::Single(mu) => Some(mu),
Self::Range { .. } => None,
}
}
pub fn inner(&self) -> Option<&T> {
match self {
Self::Single(mu) => mu.inner(),
Self::Range { .. } => None,
}
}
}
impl<T: Clone> UncertainBoundary<T> {
pub fn from_mu(mu: Mu<T>) -> Self {
Self::Single(mu)
}
}
impl<T: fmt::Display> fmt::Display for UncertainBoundary<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Single(mu) => write!(f, "{}", mu),
Self::Range { start, end } => {
write!(f, "({}_{}", start, end)?;
write!(f, ")")
}
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct ComplexInterval<T> {
pub start: UncertainBoundary<T>,
pub end: UncertainBoundary<T>,
}
impl<T> ComplexInterval<T> {
pub fn new(start: UncertainBoundary<T>, end: UncertainBoundary<T>) -> Self {
Self { start, end }
}
}
impl<T: fmt::Display> fmt::Display for ComplexInterval<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}_{}", self.start, self.end)
}
}
pub type ComplexCdsInterval = ComplexInterval<CdsPos>;
pub type ComplexGenomeInterval = ComplexInterval<GenomePos>;
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct Interval<T> {
pub start: UncertainBoundary<T>,
pub end: UncertainBoundary<T>,
}
impl<T> Interval<T> {
pub fn new(start: T, end: T) -> Self {
Self {
start: UncertainBoundary::certain(start),
end: UncertainBoundary::certain(end),
}
}
pub fn point(pos: T) -> Self
where
T: Clone,
{
Self {
start: UncertainBoundary::certain(pos.clone()),
end: UncertainBoundary::certain(pos),
}
}
pub fn with_uncertainty(start: Mu<T>, end: Mu<T>) -> Self {
Self {
start: UncertainBoundary::Single(start),
end: UncertainBoundary::Single(end),
}
}
pub fn with_complex_boundaries(start: UncertainBoundary<T>, end: UncertainBoundary<T>) -> Self {
Self { start, end }
}
pub fn has_complex_boundaries(&self) -> bool {
self.start.is_range() || self.end.is_range()
}
}
impl<T: fmt::Display + PartialEq> fmt::Display for Interval<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match (&self.start, &self.end) {
(UncertainBoundary::Single(start_mu), UncertainBoundary::Single(end_mu)) => {
if start_mu.is_unknown() && end_mu.is_unknown() {
return write!(f, "{}", self.start);
}
match (start_mu.inner(), end_mu.inner()) {
(Some(start), Some(end))
if start == end && start_mu.is_certain() && end_mu.is_certain() =>
{
write!(f, "{}", self.start)
}
_ => write!(f, "{}_{}", self.start, self.end),
}
}
_ => write!(f, "{}_{}", self.start, self.end),
}
}
}
pub type GenomeInterval = Interval<GenomePos>;
pub type CdsInterval = Interval<CdsPos>;
pub type TxInterval = Interval<TxPos>;
pub type RnaInterval = Interval<RnaPos>;
pub type ProtInterval = Interval<ProtPos>;
impl GenomeInterval {
pub fn len(&self) -> u64 {
match (self.start.inner(), self.end.inner()) {
(Some(start), Some(end)) => {
if end.base >= start.base {
(end.base - start.base).saturating_add(1)
} else {
0
}
}
_ => 0, }
}
pub fn is_empty(&self) -> bool {
self.len() == 0
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_genome_interval_display() {
let interval = GenomeInterval::new(GenomePos::new(100), GenomePos::new(200));
assert_eq!(format!("{}", interval), "100_200");
}
#[test]
fn test_point_interval_display() {
let interval = GenomeInterval::point(GenomePos::new(100));
assert_eq!(format!("{}", interval), "100");
}
#[test]
fn test_cds_interval_display() {
let interval = CdsInterval::new(CdsPos::new(100), CdsPos::with_offset(100, 5));
assert_eq!(format!("{}", interval), "100_100+5");
}
#[test]
fn test_interval_length() {
let interval = GenomeInterval::new(GenomePos::new(100), GenomePos::new(105));
assert_eq!(interval.len(), 6);
}
#[test]
fn test_uncertain_interval() {
let interval = GenomeInterval::with_uncertainty(
Mu::Uncertain(GenomePos::new(100)),
Mu::Uncertain(GenomePos::new(200)),
);
assert_eq!(format!("{}", interval), "(100)_(200)");
}
}