use std::ops::{Deref, Range};
use crate::parser::error::Error;
use log::warn;
#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize)]
pub enum Mu<T> {
Certain(T),
Uncertain(T),
}
impl<T> Mu<T> {
pub fn from(value: T, is_certain: bool) -> Self {
if is_certain {
Mu::Certain(value)
} else {
Mu::Uncertain(value)
}
}
pub fn is_certain(&self) -> bool {
match &self {
Mu::Certain(_) => true,
Mu::Uncertain(_) => false,
}
}
pub fn unwrap(self) -> T {
match self {
Mu::Certain(value) => value,
Mu::Uncertain(value) => value,
}
}
pub fn inner(&self) -> &T {
match self {
Mu::Certain(value) => value,
Mu::Uncertain(value) => value,
}
}
pub fn inner_mut(&mut self) -> &mut T {
match self {
Mu::Certain(value) => value,
Mu::Uncertain(value) => value,
}
}
}
#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize)]
pub struct GeneSymbol {
pub value: String,
}
impl GeneSymbol {
pub fn new(value: &str) -> Self {
Self {
value: value.to_string(),
}
}
pub fn from(value: String) -> Self {
Self { value }
}
}
impl Deref for GeneSymbol {
type Target = String;
fn deref(&self) -> &Self::Target {
&self.value
}
}
#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize)]
pub enum NaEdit {
RefAlt {
reference: String,
alternative: String,
},
NumAlt { count: i32, alternative: String },
DelRef { reference: String },
DelNum { count: i32 },
Ins { alternative: String },
Dup { reference: String },
InvRef { reference: String },
InvNum { count: i32 },
}
impl NaEdit {
pub fn is_na_edit_num(&self) -> bool {
match self {
NaEdit::RefAlt { .. }
| NaEdit::DelRef { .. }
| NaEdit::Ins { .. }
| NaEdit::Dup { .. }
| NaEdit::InvRef { .. } => false,
NaEdit::NumAlt { .. } | NaEdit::DelNum { .. } | NaEdit::InvNum { .. } => true,
}
}
pub fn is_ins(&self) -> bool {
matches!(self, NaEdit::Ins { .. })
}
pub fn is_dup(&self) -> bool {
matches!(self, NaEdit::Dup { .. })
}
pub fn with_num(&self) -> Self {
match self {
NaEdit::DelRef { reference } => NaEdit::DelNum {
count: reference.len() as i32,
},
NaEdit::InvRef { reference } => NaEdit::InvNum {
count: reference.len() as i32,
},
NaEdit::RefAlt {
reference,
alternative,
} => NaEdit::NumAlt {
count: reference.len() as i32,
alternative: alternative.clone(),
},
NaEdit::NumAlt { .. }
| NaEdit::DelNum { .. }
| NaEdit::InvNum { .. }
| NaEdit::Ins { .. }
| NaEdit::Dup { .. } => self.clone(),
}
}
pub fn reference_equals(&self, value: &str) -> bool {
match self {
NaEdit::RefAlt { reference, .. }
| NaEdit::DelRef { reference }
| NaEdit::Dup { reference }
| NaEdit::InvRef { reference } => reference == value,
_ => false,
}
}
pub fn with_reference(self, reference: String) -> Self {
match self {
NaEdit::RefAlt {
alternative,
reference: old_reference,
} => {
if old_reference.is_empty() && alternative.is_empty() {
NaEdit::RefAlt {
alternative: reference.clone(), reference, }
} else {
NaEdit::RefAlt {
reference,
alternative,
}
}
}
NaEdit::NumAlt { alternative, .. } => NaEdit::RefAlt {
reference,
alternative,
},
NaEdit::DelRef { .. } => NaEdit::DelRef { reference },
NaEdit::DelNum { .. } => NaEdit::DelRef { reference },
NaEdit::Ins { alternative } => {
warn!("Calling with_reference() on NaEdit::Ins");
NaEdit::Ins { alternative }
}
NaEdit::Dup { .. } => NaEdit::Dup { reference },
NaEdit::InvRef { .. } => NaEdit::InvRef { reference },
NaEdit::InvNum { .. } => NaEdit::InvRef { reference },
}
}
}
#[derive(Clone, Debug, PartialEq, Default, serde::Serialize, serde::Deserialize)]
pub enum UncertainLengthChange {
#[default]
None,
Unknown,
Known(i32),
}
#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize)]
pub struct Accession {
pub value: String,
}
impl Deref for Accession {
type Target = String;
fn deref(&self) -> &Self::Target {
&self.value
}
}
impl Accession {
pub fn new(value: &str) -> Self {
Self {
value: value.to_string(),
}
}
pub fn from(value: String) -> Self {
Self { value }
}
}
#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize)]
pub enum ProteinEdit {
Fs {
alternative: Option<String>,
terminal: Option<String>,
length: UncertainLengthChange,
},
Ext {
aa_ext: Option<String>,
ext_aa: Option<String>,
change: UncertainLengthChange,
},
Subst {
alternative: String,
},
DelIns {
alternative: String,
},
Ins {
alternative: String,
},
Del,
Dup,
Ident,
}
#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize)]
pub enum HgvsVariant {
CdsVariant {
accession: Accession,
gene_symbol: Option<GeneSymbol>,
loc_edit: CdsLocEdit,
},
GenomeVariant {
accession: Accession,
gene_symbol: Option<GeneSymbol>,
loc_edit: GenomeLocEdit,
},
MtVariant {
accession: Accession,
gene_symbol: Option<GeneSymbol>,
loc_edit: MtLocEdit,
},
TxVariant {
accession: Accession,
gene_symbol: Option<GeneSymbol>,
loc_edit: TxLocEdit,
},
ProtVariant {
accession: Accession,
gene_symbol: Option<GeneSymbol>,
loc_edit: ProtLocEdit,
},
RnaVariant {
accession: Accession,
gene_symbol: Option<GeneSymbol>,
loc_edit: RnaLocEdit,
},
}
impl HgvsVariant {
pub fn is_na_edit_num(&self) -> bool {
match self {
HgvsVariant::CdsVariant { loc_edit, .. } => loc_edit.edit.inner().is_na_edit_num(),
HgvsVariant::GenomeVariant { loc_edit, .. } => loc_edit.edit.inner().is_na_edit_num(),
HgvsVariant::MtVariant { loc_edit, .. } => loc_edit.edit.inner().is_na_edit_num(),
HgvsVariant::TxVariant { loc_edit, .. } => loc_edit.edit.inner().is_na_edit_num(),
HgvsVariant::RnaVariant { loc_edit, .. } => loc_edit.edit.inner().is_na_edit_num(),
HgvsVariant::ProtVariant { .. } => false,
}
}
pub fn with_na_ref_num(self) -> Self {
match self {
HgvsVariant::CdsVariant {
accession,
gene_symbol,
loc_edit,
} => HgvsVariant::CdsVariant {
accession,
gene_symbol,
loc_edit: loc_edit.with_num(),
},
HgvsVariant::GenomeVariant {
accession,
gene_symbol,
loc_edit,
} => HgvsVariant::GenomeVariant {
accession,
gene_symbol,
loc_edit: loc_edit.with_num(),
},
HgvsVariant::MtVariant {
accession,
gene_symbol,
loc_edit,
} => HgvsVariant::MtVariant {
accession,
gene_symbol,
loc_edit: loc_edit.with_num(),
},
HgvsVariant::TxVariant {
accession,
gene_symbol,
loc_edit,
} => HgvsVariant::TxVariant {
accession,
gene_symbol,
loc_edit: loc_edit.with_num(),
},
HgvsVariant::ProtVariant {
accession,
gene_symbol,
loc_edit,
} => HgvsVariant::ProtVariant {
accession,
gene_symbol,
loc_edit,
},
HgvsVariant::RnaVariant {
accession,
gene_symbol,
loc_edit,
} => HgvsVariant::RnaVariant {
accession,
gene_symbol,
loc_edit: loc_edit.with_num(),
},
}
}
pub fn with_reference(self, value: String) -> Self {
match self {
HgvsVariant::CdsVariant {
accession,
gene_symbol,
loc_edit,
} => HgvsVariant::CdsVariant {
accession,
gene_symbol,
loc_edit: loc_edit.with_reference(value),
},
HgvsVariant::GenomeVariant {
accession,
gene_symbol,
loc_edit,
} => HgvsVariant::GenomeVariant {
accession,
gene_symbol,
loc_edit: loc_edit.with_reference(value),
},
HgvsVariant::MtVariant {
accession,
gene_symbol,
loc_edit,
} => HgvsVariant::MtVariant {
accession,
gene_symbol,
loc_edit: loc_edit.with_reference(value),
},
HgvsVariant::TxVariant {
accession,
gene_symbol,
loc_edit,
} => HgvsVariant::TxVariant {
accession,
gene_symbol,
loc_edit: loc_edit.with_reference(value),
},
HgvsVariant::ProtVariant {
accession,
gene_symbol,
loc_edit,
} => {
warn!("Calling with_reference on ProtVariant");
HgvsVariant::ProtVariant {
accession,
gene_symbol,
loc_edit,
}
}
HgvsVariant::RnaVariant {
accession,
gene_symbol,
loc_edit,
} => HgvsVariant::RnaVariant {
accession,
gene_symbol,
loc_edit: loc_edit.with_reference(value),
},
}
}
pub fn gene_symbol(&self) -> &Option<GeneSymbol> {
match self {
HgvsVariant::CdsVariant { gene_symbol, .. } => gene_symbol,
HgvsVariant::GenomeVariant { gene_symbol, .. } => gene_symbol,
HgvsVariant::MtVariant { gene_symbol, .. } => gene_symbol,
HgvsVariant::TxVariant { gene_symbol, .. } => gene_symbol,
HgvsVariant::ProtVariant { gene_symbol, .. } => gene_symbol,
HgvsVariant::RnaVariant { gene_symbol, .. } => gene_symbol,
}
}
pub fn accession(&self) -> &Accession {
match self {
HgvsVariant::CdsVariant { accession, .. } => accession,
HgvsVariant::GenomeVariant { accession, .. } => accession,
HgvsVariant::MtVariant { accession, .. } => accession,
HgvsVariant::TxVariant { accession, .. } => accession,
HgvsVariant::ProtVariant { accession, .. } => accession,
HgvsVariant::RnaVariant { accession, .. } => accession,
}
}
pub fn mu_loc_range(&self) -> Option<Mu<Range<i32>>> {
match self {
HgvsVariant::CdsVariant { loc_edit, .. } => loc_edit
.loc
.inner()
.clone()
.try_into()
.ok()
.map(|l| Mu::from(l, loc_edit.loc.is_certain())),
HgvsVariant::GenomeVariant { loc_edit, .. } => loc_edit
.loc
.inner()
.clone()
.try_into()
.ok()
.map(|l| Mu::from(l, loc_edit.loc.is_certain())),
HgvsVariant::MtVariant { loc_edit, .. } => loc_edit
.loc
.inner()
.clone()
.try_into()
.ok()
.map(|l| Mu::from(l, loc_edit.loc.is_certain())),
HgvsVariant::TxVariant { loc_edit, .. } => {
Some(From::from(loc_edit.loc.inner().clone()))
.map(|l| Mu::from(l, loc_edit.loc.is_certain()))
}
HgvsVariant::RnaVariant { loc_edit, .. } => {
Some(From::from(loc_edit.loc.inner().clone()))
.map(|l| Mu::from(l, loc_edit.loc.is_certain()))
}
HgvsVariant::ProtVariant {
loc_edit: ProtLocEdit::Ordinary { loc, .. },
..
} => Some(Mu::from(loc.inner().clone().into(), loc.is_certain())),
_ => None,
}
}
pub fn loc_range(&self) -> Option<Range<i32>> {
self.mu_loc_range().map(|l| l.inner().clone())
}
pub fn mu_na_edit(&self) -> Option<&Mu<NaEdit>> {
match self {
HgvsVariant::CdsVariant { loc_edit, .. } => Some(&loc_edit.edit),
HgvsVariant::GenomeVariant { loc_edit, .. } => Some(&loc_edit.edit),
HgvsVariant::MtVariant { loc_edit, .. } => Some(&loc_edit.edit),
HgvsVariant::TxVariant { loc_edit, .. } => Some(&loc_edit.edit),
HgvsVariant::RnaVariant { loc_edit, .. } => Some(&loc_edit.edit),
_ => None,
}
}
pub fn na_edit(&self) -> Option<&NaEdit> {
self.mu_na_edit().map(|e| e.inner())
}
pub fn mu_prot_edit(&self) -> Option<&Mu<ProteinEdit>> {
match self {
HgvsVariant::ProtVariant {
loc_edit: ProtLocEdit::Ordinary { edit, .. },
..
} => Some(edit),
_ => None,
}
}
pub fn prot_edit(&self) -> Option<&ProteinEdit> {
self.mu_prot_edit().map(|e| e.inner())
}
pub fn spans_intron(&self) -> bool {
match self {
HgvsVariant::CdsVariant { loc_edit, .. } => {
loc_edit.loc.inner().start.offset.is_some()
|| loc_edit.loc.inner().end.offset.is_some()
}
HgvsVariant::TxVariant { loc_edit, .. } => {
loc_edit.loc.inner().start.offset.is_some()
|| loc_edit.loc.inner().end.offset.is_some()
}
HgvsVariant::RnaVariant { loc_edit, .. } => {
loc_edit.loc.inner().start.offset.is_some()
|| loc_edit.loc.inner().end.offset.is_some()
}
_ => false,
}
}
}
#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize)]
pub struct CdsLocEdit {
pub loc: Mu<CdsInterval>,
pub edit: Mu<NaEdit>,
}
impl CdsLocEdit {
fn with_reference(self, reference: String) -> Self {
CdsLocEdit {
loc: self.loc,
edit: match self.edit {
Mu::Certain(edit) => Mu::Certain(edit.with_reference(reference)),
Mu::Uncertain(edit) => Mu::Uncertain(edit.with_reference(reference)),
},
}
}
fn with_num(self) -> Self {
CdsLocEdit {
loc: self.loc,
edit: match self.edit {
Mu::Certain(edit) => Mu::Certain(edit.with_num()),
Mu::Uncertain(edit) => Mu::Uncertain(edit.with_num()),
},
}
}
}
#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize)]
pub struct CdsInterval {
pub start: CdsPos,
pub end: CdsPos,
}
impl TryFrom<CdsInterval> for Range<i32> {
type Error = Error;
fn try_from(value: CdsInterval) -> Result<Self, Self::Error> {
if value.start.offset.is_some() || value.end.offset.is_some() {
warn!("Converting interval {:?} with offset to range!", &value);
}
if value.start.cds_from != value.end.cds_from {
Err(Error::IllDefinedConversion(format!("{:?}", value)))
} else {
Ok(if value.start.base > 0 {
(value.start.base - 1)..value.end.base
} else {
value.start.base..(value.end.base + 1)
})
}
}
}
#[derive(Clone, Copy, Debug, PartialEq, serde::Serialize, serde::Deserialize)]
pub enum CdsFrom {
Start,
End,
}
#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize)]
pub struct CdsPos {
pub base: i32,
pub offset: Option<i32>,
pub cds_from: CdsFrom,
}
#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize)]
pub struct GenomeLocEdit {
pub loc: Mu<GenomeInterval>,
pub edit: Mu<NaEdit>,
}
impl GenomeLocEdit {
fn with_reference(self, reference: String) -> Self {
GenomeLocEdit {
loc: self.loc,
edit: match self.edit {
Mu::Certain(edit) => Mu::Certain(edit.with_reference(reference)),
Mu::Uncertain(edit) => Mu::Uncertain(edit.with_reference(reference)),
},
}
}
fn with_num(self) -> Self {
GenomeLocEdit {
loc: self.loc,
edit: match self.edit {
Mu::Certain(edit) => Mu::Certain(edit.with_num()),
Mu::Uncertain(edit) => Mu::Uncertain(edit.with_num()),
},
}
}
}
#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize)]
pub struct GenomeInterval {
pub start: Option<i32>,
pub end: Option<i32>,
}
impl TryInto<Range<i32>> for GenomeInterval {
type Error = Error;
fn try_into(self) -> Result<Range<i32>, Self::Error> {
if let (Some(start), Some(end)) = (self.start, self.end) {
Ok((start - 1)..end)
} else {
Err(Error::CannotNonePositionIntoRange(format!("{:?}", self)))
}
}
}
impl From<Range<i32>> for GenomeInterval {
fn from(value: Range<i32>) -> Self {
Self {
start: Some(value.start + 1),
end: Some(value.end + 1),
}
}
}
#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize)]
pub struct MtLocEdit {
pub loc: Mu<MtInterval>,
pub edit: Mu<NaEdit>,
}
impl MtLocEdit {
fn with_reference(self, reference: String) -> Self {
MtLocEdit {
loc: self.loc,
edit: match self.edit {
Mu::Certain(edit) => Mu::Certain(edit.with_reference(reference)),
Mu::Uncertain(edit) => Mu::Uncertain(edit.with_reference(reference)),
},
}
}
fn with_num(self) -> Self {
MtLocEdit {
loc: self.loc,
edit: match self.edit {
Mu::Certain(edit) => Mu::Certain(edit.with_num()),
Mu::Uncertain(edit) => Mu::Uncertain(edit.with_num()),
},
}
}
}
#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize)]
pub struct MtInterval {
pub start: Option<i32>,
pub end: Option<i32>,
}
impl TryInto<Range<i32>> for MtInterval {
type Error = Error;
fn try_into(self) -> Result<Range<i32>, Self::Error> {
if let (Some(start), Some(end)) = (self.start, self.end) {
Ok((start - 1)..end)
} else {
Err(Error::CannotNonePositionIntoRange(format!("{:?}", self)))
}
}
}
#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize)]
pub struct TxLocEdit {
pub loc: Mu<TxInterval>,
pub edit: Mu<NaEdit>,
}
impl TxLocEdit {
fn with_reference(self, reference: String) -> Self {
TxLocEdit {
loc: self.loc,
edit: match self.edit {
Mu::Certain(edit) => Mu::Certain(edit.with_reference(reference)),
Mu::Uncertain(edit) => Mu::Uncertain(edit.with_reference(reference)),
},
}
}
fn with_num(self) -> Self {
TxLocEdit {
loc: self.loc,
edit: match self.edit {
Mu::Certain(edit) => Mu::Certain(edit.with_num()),
Mu::Uncertain(edit) => Mu::Uncertain(edit.with_num()),
},
}
}
}
#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize)]
pub struct TxInterval {
pub start: TxPos,
pub end: TxPos,
}
impl From<TxInterval> for Range<i32> {
fn from(val: TxInterval) -> Self {
if val.start.offset.is_some() || val.end.offset.is_some() {
warn!("Converting interval {:?} with offset to range!", &val);
}
if val.start.base > 0 {
(val.start.base - 1)..val.end.base
} else {
val.start.base..(val.end.base + 1)
}
}
}
#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize)]
pub struct TxPos {
pub base: i32,
pub offset: Option<i32>,
}
#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize)]
pub struct RnaLocEdit {
pub loc: Mu<RnaInterval>,
pub edit: Mu<NaEdit>,
}
impl RnaLocEdit {
fn with_reference(self, reference: String) -> Self {
RnaLocEdit {
loc: self.loc,
edit: match self.edit {
Mu::Certain(edit) => Mu::Certain(edit.with_reference(reference)),
Mu::Uncertain(edit) => Mu::Uncertain(edit.with_reference(reference)),
},
}
}
fn with_num(self) -> Self {
RnaLocEdit {
loc: self.loc,
edit: match self.edit {
Mu::Certain(edit) => Mu::Certain(edit.with_num()),
Mu::Uncertain(edit) => Mu::Uncertain(edit.with_num()),
},
}
}
}
#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize)]
pub struct RnaInterval {
pub start: RnaPos,
pub end: RnaPos,
}
impl From<RnaInterval> for Range<i32> {
fn from(val: RnaInterval) -> Self {
if val.start.offset.is_some() || val.end.offset.is_some() {
warn!("Converting interval {:?} with offset to range!", &val);
}
if val.start.base > 0 {
(val.start.base - 1)..val.end.base
} else {
val.start.base..(val.end.base + 1)
}
}
}
#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize)]
pub struct RnaPos {
pub base: i32,
pub offset: Option<i32>,
}
#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize)]
pub enum ProtLocEdit {
Ordinary {
loc: Mu<ProtInterval>,
edit: Mu<ProteinEdit>,
},
NoChange,
NoChangeUncertain,
NoProtein,
NoProteinUncertain,
Unknown,
InitiationUncertain,
}
#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize)]
pub struct ProtInterval {
pub start: ProtPos,
pub end: ProtPos,
}
impl From<ProtInterval> for Range<i32> {
fn from(val: ProtInterval) -> Self {
if val.start.number > 0 {
(val.start.number - 1)..val.end.number
} else {
val.start.number..(val.end.number + 1)
}
}
}
#[derive(Clone, Debug, PartialEq, Default, serde::Serialize, serde::Deserialize)]
pub struct ProtPos {
pub aa: String,
pub number: i32,
}
#[cfg(test)]
mod test {
use pretty_assertions::assert_eq;
use super::{TxInterval, TxPos};
use crate::parser::Mu;
#[test]
fn mu_construct() {
assert_eq!(format!("{:?}", Mu::Certain(1)), "Certain(1)");
assert_eq!(format!("{:?}", Mu::Certain(Some(1))), "Certain(Some(1))");
assert_eq!(format!("{:?}", Mu::Uncertain(1)), "Uncertain(1)");
assert_eq!(
format!("{:?}", Mu::Uncertain(Some(1))),
"Uncertain(Some(1))"
);
}
#[test]
fn mu_unwrap() {
assert_eq!(Mu::Certain(1).unwrap(), 1);
assert_eq!(Mu::Uncertain(1).unwrap(), 1);
}
#[test]
fn mu_inner() {
assert_eq!(Mu::Certain(1).inner(), &1);
assert_eq!(Mu::Uncertain(1).inner(), &1);
}
#[test]
fn mu_from() {
assert_eq!(Mu::from(1, true), Mu::Certain(1));
assert_eq!(Mu::from(1, false), Mu::Uncertain(1));
assert_eq!(Mu::from(Some(1), true), Mu::Certain(Some(1)));
assert_eq!(Mu::from(Some(1), false), Mu::Uncertain(Some(1)));
}
#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize)]
pub struct TestInterval {
pub start: TestPos,
pub end: TestPos,
}
#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize)]
pub struct TestPos {
pub base: i32,
pub offset: Option<i32>,
}
#[test]
fn mu_from_problematic() {
let test_itv = TestInterval {
start: TestPos {
base: 34,
offset: Some(1),
},
end: TestPos {
base: 35,
offset: Some(-1),
},
};
assert_eq!(
Mu::from(test_itv, true),
Mu::Certain(TestInterval {
start: TestPos {
base: 34,
offset: Some(1),
},
end: TestPos {
base: 35,
offset: Some(-1),
}
})
);
}
#[test]
fn mu_from_tx_interval() {
let test_itv = TxInterval {
start: TxPos {
base: 34,
offset: Some(1),
},
end: TxPos {
base: 35,
offset: Some(-1),
},
};
assert_eq!(
Mu::from(test_itv, true),
Mu::Certain(TxInterval {
start: TxPos {
base: 34,
offset: Some(1),
},
end: TxPos {
base: 35,
offset: Some(-1),
}
})
);
}
}