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> UncertainBoundary<T> {
pub fn map_ref<U, F: Fn(&T) -> U>(&self, f: F) -> UncertainBoundary<U> {
match self {
Self::Single(mu) => UncertainBoundary::Single(mu.map_ref(&f)),
Self::Range { start, end } => UncertainBoundary::Range {
start: start.map_ref(&f),
end: end.map_ref(&f),
},
}
}
}
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),
}
}
(UncertainBoundary::Range { .. }, UncertainBoundary::Range { .. })
if self.start == self.end =>
{
write!(f, "{}", self.start)
}
_ => 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>;
pub(crate) fn interval_is_wraparound(loc: &GenomeInterval) -> bool {
match (loc.start.inner(), loc.end.inner()) {
(Some(start), Some(end)) => start.base > end.base,
_ => false,
}
}
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)
}
_ => 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)");
}
}
#[cfg(test)]
mod map_tests {
use super::*;
#[test]
fn test_uncertain_boundary_map_ref() {
let single = UncertainBoundary::certain(5u32);
assert_eq!(single.map_ref(|n| n * 2), UncertainBoundary::certain(10u32));
let range = UncertainBoundary::range(Mu::Certain(1u32), Mu::Unknown);
match range.map_ref(|n| n + 100) {
UncertainBoundary::Range { start, end } => {
assert_eq!(start, Mu::Certain(101));
assert_eq!(end, Mu::Unknown);
}
_ => panic!("expected range"),
}
let uncertain_range = UncertainBoundary::range(Mu::Uncertain(7u32), Mu::Certain(9u32));
match uncertain_range.map_ref(|n| n + 1) {
UncertainBoundary::Range { start, end } => {
assert_eq!(start, Mu::Uncertain(8));
assert_eq!(end, Mu::Certain(10));
}
_ => panic!("expected range"),
}
let single_uncertain = UncertainBoundary::Single(Mu::Uncertain(3u32));
assert_eq!(
single_uncertain.map_ref(|n| n * 10),
UncertainBoundary::Single(Mu::Uncertain(30)),
);
}
}