use crate::{
alignment::{
Alignment, AlignmentStates, NextCiglet, NextCigletMut, PairwiseSequence, PeekOp, profile::ScalarProfile,
sw::sw_scalar_align,
},
data::{
amino_acids::AminoAcids,
cigar::CigarError,
matrices::WeightMatrix,
types::cigar::{Cigar, Ciglet},
},
};
fn example_alignment() -> Alignment<u32> {
const WEIGHTS: WeightMatrix<i8, 5> = WeightMatrix::new_dna_matrix(4, -2, Some(b'N'));
const GAP_OPEN: i8 = -3;
const GAP_EXTEND: i8 = -1;
let reference: &[u8] = b"GGCCACAGGATTGAGC";
let query: &[u8] = b"TCTCAGATTGCAGTTT";
let profile = ScalarProfile::<5>::new(query, &WEIGHTS, GAP_OPEN, GAP_EXTEND).unwrap();
sw_scalar_align(reference, &profile).unwrap()
}
#[test]
fn alignment_invert() {
let alignment = example_alignment();
let invert_alignment = alignment.invert();
assert_eq!(alignment.ref_range, 3..15);
assert_eq!(alignment.query_range, 1..13);
assert_eq!(alignment.states, Cigar::from_slice_unchecked("1S5M1D4M1I2M3S"));
assert_eq!(invert_alignment.ref_range, 1..13);
assert_eq!(invert_alignment.query_range, 3..15);
assert_eq!(invert_alignment.states, Cigar::from_slice_unchecked("3S5M1I4M1D2M1S"));
}
#[test]
fn alignment_reversal() {
let a = example_alignment();
assert_eq!(a, a.to_reverse().to_reverse());
let mut b = a.clone();
b.make_reverse();
b.make_reverse();
assert_eq!(a, b);
}
#[test]
fn states_sequence() {
let cigar = Cigar::from_slice_unchecked(b"3H4S4S3H10M9I3M1D5M3H5S10S1H");
let alignment_states = AlignmentStates::from_cigar_unchecked(&cigar);
let mut alignment_states_mut = alignment_states.clone();
let mut cigar_slice = alignment_states.as_slice();
let mut cigar_mut_slice = alignment_states_mut.as_mut_slice();
let mut ciglet_iterator = cigar.into_iter();
assert_eq!(cigar_slice.peek_op(), Some(b'H'));
assert_eq!(cigar_mut_slice.peek_op(), Some(b'H'));
assert_eq!(ciglet_iterator.peek_op(), Some(b'H'));
assert_eq!(cigar_slice.peek_op_back(), Some(b'H'));
assert_eq!(cigar_mut_slice.peek_op_back(), Some(b'H'));
assert_eq!(ciglet_iterator.peek_op_back(), Some(b'H'));
assert_eq!(cigar_slice.peek_op(), Some(b'H'));
assert_eq!(cigar_mut_slice.peek_op(), Some(b'H'));
assert_eq!(ciglet_iterator.peek_op(), Some(b'H'));
assert_eq!(cigar_slice.peek_op_back(), Some(b'H'));
assert_eq!(cigar_mut_slice.peek_op_back(), Some(b'H'));
assert_eq!(ciglet_iterator.peek_op_back(), Some(b'H'));
assert_eq!(cigar_slice.remove_clipping_front(), 7);
assert_eq!(cigar_mut_slice.remove_clipping_front(), 7);
assert_eq!(ciglet_iterator.remove_clipping_front(), 7);
assert_eq!(cigar_slice.remove_clipping_back(), 11);
assert_eq!(cigar_mut_slice.remove_clipping_back(), 11);
assert_eq!(ciglet_iterator.remove_clipping_back(), 11);
assert_eq!(cigar_slice.peek_op(), Some(b'S'));
assert_eq!(cigar_mut_slice.peek_op(), Some(b'S'));
assert_eq!(ciglet_iterator.peek_op(), Some(b'S'));
assert_eq!(cigar_slice.peek_op_back(), Some(b'S'));
assert_eq!(cigar_mut_slice.peek_op_back(), Some(b'S'));
assert_eq!(ciglet_iterator.peek_op_back(), Some(b'S'));
assert_eq!(cigar_slice.remove_clipping_front(), 4);
assert_eq!(cigar_mut_slice.remove_clipping_front(), 4);
assert_eq!(ciglet_iterator.remove_clipping_front(), 4);
assert_eq!(cigar_slice.remove_clipping_back(), 5);
assert_eq!(cigar_mut_slice.remove_clipping_back(), 5);
assert_eq!(ciglet_iterator.remove_clipping_back(), 5);
assert_eq!(cigar_slice.remove_clipping_front(), 3);
assert_eq!(cigar_mut_slice.remove_clipping_front(), 3);
assert_eq!(ciglet_iterator.remove_clipping_front(), 3);
assert_eq!(cigar_slice.remove_clipping_back(), 3);
assert_eq!(cigar_mut_slice.remove_clipping_back(), 3);
assert_eq!(ciglet_iterator.remove_clipping_back(), 3);
assert_eq!(cigar_slice.remove_clipping_front(), 0);
assert_eq!(cigar_mut_slice.remove_clipping_front(), 0);
assert_eq!(ciglet_iterator.remove_clipping_front(), 0);
assert_eq!(cigar_slice.remove_clipping_back(), 0);
assert_eq!(cigar_mut_slice.remove_clipping_back(), 0);
assert_eq!(ciglet_iterator.remove_clipping_back(), 0);
assert_eq!(cigar_slice.next_ciglet_if_op(|op| op == b'I'), None);
assert_eq!(cigar_mut_slice.next_ciglet_if_op(|op| op == b'I'), None);
assert_eq!(ciglet_iterator.next_ciglet_if_op(|op| op == b'I'), None);
assert_eq!(cigar_slice.next_ciglet_back_if_op(|op| op == b'I'), None);
assert_eq!(cigar_mut_slice.next_ciglet_back_if_op(|op| op == b'I'), None);
assert_eq!(ciglet_iterator.next_ciglet_back_if_op(|op| op == b'I'), None);
assert_eq!(
cigar_slice.next_ciglet_if_op(|op| op == b'M'),
Some(Ciglet { inc: 10, op: b'M' })
);
assert_eq!(
cigar_mut_slice.next_ciglet_if_op(|op| op == b'M'),
Some(Ciglet { inc: 10, op: b'M' })
);
assert_eq!(
ciglet_iterator.next_ciglet_if_op(|op| op == b'M'),
Some(Ciglet { inc: 10, op: b'M' })
);
assert_eq!(
cigar_slice.next_ciglet_back_if_op(|op| op == b'M'),
Some(Ciglet { inc: 5, op: b'M' })
);
assert_eq!(
cigar_mut_slice.next_ciglet_back_if_op(|op| op == b'M'),
Some(Ciglet { inc: 5, op: b'M' })
);
assert_eq!(
ciglet_iterator.next_ciglet_back_if_op(|op| op == b'M'),
Some(Ciglet { inc: 5, op: b'M' })
);
assert_eq!(cigar_slice.next_ciglet(), Some(Ciglet { inc: 9, op: b'I' }));
assert_eq!(cigar_mut_slice.next_ciglet(), Some(Ciglet { inc: 9, op: b'I' }));
assert_eq!(ciglet_iterator.next_ciglet(), Some(Ciglet { inc: 9, op: b'I' }));
assert_eq!(cigar_slice.next_ciglet_back(), Some(Ciglet { inc: 1, op: b'D' }));
assert_eq!(cigar_mut_slice.next_ciglet_back(), Some(Ciglet { inc: 1, op: b'D' }));
assert_eq!(ciglet_iterator.next_ciglet_back(), Some(Ciglet { inc: 1, op: b'D' }));
assert!(!cigar_slice.is_empty());
assert!(!cigar_mut_slice.is_empty());
assert_eq!(cigar_slice.next_ciglet(), Some(Ciglet { inc: 3, op: b'M' }));
assert_eq!(cigar_mut_slice.next_ciglet(), Some(Ciglet { inc: 3, op: b'M' }));
assert_eq!(ciglet_iterator.next_ciglet(), Some(Ciglet { inc: 3, op: b'M' }));
assert_eq!(cigar_slice.next_ciglet_back(), None);
assert_eq!(cigar_mut_slice.next_ciglet_back(), None);
assert_eq!(ciglet_iterator.next_ciglet_back(), None);
assert_eq!(cigar_slice.next_ciglet(), None);
assert_eq!(cigar_mut_slice.next_ciglet(), None);
assert_eq!(ciglet_iterator.next_ciglet(), None);
assert!(cigar_slice.is_empty());
assert!(cigar_mut_slice.is_empty());
}
#[test]
fn states_sequence_mut() {
let cigar = Cigar::from_slice_unchecked(b"3H4S4S3H10M9I3M1D5M3H5S10S1H");
let mut alignment_states = AlignmentStates::from_cigar_unchecked(&cigar);
let mut cigar_mut_slice = alignment_states.as_mut_slice();
assert_eq!(cigar_mut_slice.peek_op(), Some(b'H'));
assert_eq!(cigar_mut_slice.peek_op_back(), Some(b'H'));
assert_eq!(cigar_mut_slice.peek_op(), Some(b'H'));
assert_eq!(cigar_mut_slice.peek_op_back(), Some(b'H'));
assert_eq!(cigar_mut_slice.remove_clipping_front(), 7);
assert_eq!(cigar_mut_slice.remove_clipping_back(), 11);
assert_eq!(cigar_mut_slice.peek_op(), Some(b'S'));
assert_eq!(cigar_mut_slice.peek_op_back(), Some(b'S'));
assert_eq!(cigar_mut_slice.remove_clipping_front(), 4);
assert_eq!(cigar_mut_slice.remove_clipping_back(), 5);
assert_eq!(cigar_mut_slice.remove_clipping_front(), 3);
assert_eq!(cigar_mut_slice.remove_clipping_back(), 3);
assert_eq!(cigar_mut_slice.remove_clipping_front(), 0);
assert_eq!(cigar_mut_slice.remove_clipping_back(), 0);
assert_eq!(cigar_mut_slice.next_ciglet_if_op(|op| op == b'I'), None);
assert_eq!(cigar_mut_slice.next_ciglet_back_if_op(|op| op == b'I'), None);
assert_eq!(cigar_mut_slice.next_ciglet_if_op_mut(|op| op == b'I'), None);
assert_eq!(cigar_mut_slice.next_ciglet_back_if_op_mut(|op| op == b'I'), None);
let next_ciglet = cigar_mut_slice.next_ciglet_if_op_mut(|op| op == b'M');
assert_eq!(next_ciglet, Some(&mut Ciglet { inc: 10, op: b'M' }));
next_ciglet.unwrap().inc += 1;
let next_ciglet_back = cigar_mut_slice.next_ciglet_back_if_op_mut(|op| op == b'M');
assert_eq!(next_ciglet_back, Some(&mut Ciglet { inc: 5, op: b'M' }));
next_ciglet_back.unwrap().op = b'I';
assert_eq!(cigar_mut_slice.next_ciglet_mut(), Some(&mut Ciglet { inc: 9, op: b'I' }));
assert_eq!(cigar_mut_slice.next_ciglet_back_mut(), Some(&mut Ciglet { inc: 1, op: b'D' }));
assert!(!cigar_mut_slice.is_empty());
assert_eq!(cigar_mut_slice.next_ciglet(), Some(Ciglet { inc: 3, op: b'M' }));
assert_eq!(cigar_mut_slice.next_ciglet_back(), None);
assert_eq!(cigar_mut_slice.next_ciglet(), None);
assert!(cigar_mut_slice.is_empty());
assert_eq!(alignment_states, Cigar::from_slice_unchecked(b"3H4S4S3H11M9I3M1D5I3H5S10S1H"));
}
#[test]
fn states_sequence_zero_ciglets() {
let cigar = Cigar::from_slice_unchecked(b"0H0S0P0I10M0D10N1S0H");
let alignment_states = AlignmentStates::from_cigar_unchecked(&cigar);
let mut alignment_states_mut = alignment_states.clone();
let mut cigar_slice = alignment_states.as_slice();
let mut cigar_mut_slice = alignment_states_mut.as_mut_slice();
let mut ciglet_iterator = cigar.into_iter();
assert_eq!(cigar_slice.peek_op(), Some(b'M'));
assert_eq!(cigar_mut_slice.peek_op(), Some(b'M'));
assert_eq!(ciglet_iterator.peek_op(), Some(b'M'));
assert_eq!(cigar_slice.peek_op_back(), Some(b'S'));
assert_eq!(cigar_mut_slice.peek_op_back(), Some(b'S'));
assert_eq!(ciglet_iterator.peek_op_back(), Some(b'S'));
assert_eq!(cigar_slice.peek_op(), Some(b'M'));
assert_eq!(cigar_mut_slice.peek_op(), Some(b'M'));
assert_eq!(ciglet_iterator.peek_op(), Some(b'M'));
assert_eq!(cigar_slice.peek_op_back(), Some(b'S'));
assert_eq!(cigar_mut_slice.peek_op_back(), Some(b'S'));
assert_eq!(ciglet_iterator.peek_op_back(), Some(b'S'));
assert_eq!(cigar_slice.remove_clipping_front(), 0);
assert_eq!(cigar_mut_slice.remove_clipping_front(), 0);
assert_eq!(ciglet_iterator.remove_clipping_front(), 0);
assert_eq!(cigar_slice.remove_clipping_back(), 1);
assert_eq!(cigar_mut_slice.remove_clipping_back(), 1);
assert_eq!(ciglet_iterator.remove_clipping_back(), 1);
assert_eq!(cigar_slice.peek_op(), Some(b'M'));
assert_eq!(cigar_mut_slice.peek_op(), Some(b'M'));
assert_eq!(ciglet_iterator.peek_op(), Some(b'M'));
assert_eq!(cigar_slice.peek_op_back(), Some(b'N'));
assert_eq!(cigar_mut_slice.peek_op_back(), Some(b'N'));
assert_eq!(ciglet_iterator.peek_op_back(), Some(b'N'));
assert_eq!(cigar_slice.remove_clipping_front(), 0);
assert_eq!(cigar_mut_slice.remove_clipping_front(), 0);
assert_eq!(ciglet_iterator.remove_clipping_front(), 0);
assert_eq!(cigar_slice.remove_clipping_back(), 0);
assert_eq!(cigar_mut_slice.remove_clipping_back(), 0);
assert_eq!(ciglet_iterator.remove_clipping_back(), 0);
assert_eq!(cigar_slice.next_ciglet_if_op(|op| op == b'P'), None);
assert_eq!(cigar_mut_slice.next_ciglet_if_op(|op| op == b'P'), None);
assert_eq!(ciglet_iterator.next_ciglet_if_op(|op| op == b'P'), None);
assert_eq!(cigar_slice.next_ciglet_back_if_op(|op| op == b'P'), None);
assert_eq!(cigar_mut_slice.next_ciglet_back_if_op(|op| op == b'P'), None);
assert_eq!(ciglet_iterator.next_ciglet_back_if_op(|op| op == b'P'), None);
assert_eq!(
cigar_slice.next_ciglet_if_op(|op| op == b'M'),
Some(Ciglet { inc: 10, op: b'M' })
);
assert_eq!(
cigar_mut_slice.next_ciglet_if_op(|op| op == b'M'),
Some(Ciglet { inc: 10, op: b'M' })
);
assert_eq!(
ciglet_iterator.next_ciglet_if_op(|op| op == b'M'),
Some(Ciglet { inc: 10, op: b'M' })
);
assert_eq!(
cigar_slice.next_ciglet_back_if_op(|op| op == b'N'),
Some(Ciglet { inc: 10, op: b'N' })
);
assert_eq!(
cigar_mut_slice.next_ciglet_back_if_op(|op| op == b'N'),
Some(Ciglet { inc: 10, op: b'N' })
);
assert_eq!(
ciglet_iterator.next_ciglet_back_if_op(|op| op == b'N'),
Some(Ciglet { inc: 10, op: b'N' })
);
assert_eq!(cigar_slice.next_ciglet(), None);
assert_eq!(cigar_mut_slice.next_ciglet(), None);
assert_eq!(ciglet_iterator.next_ciglet(), None);
assert_eq!(cigar_slice.next_ciglet_back(), None);
assert_eq!(cigar_mut_slice.next_ciglet_back(), None);
assert_eq!(ciglet_iterator.next_ciglet_back(), None);
assert!(cigar_slice.is_empty());
assert!(cigar_mut_slice.is_empty());
}
#[test]
fn align_with_cigar() {
let data: [(usize, Cigar, [AminoAcids; 4]); 1] = [(
2,
Cigar::from_slice_unchecked("4M"),
[b"PLEASANTLY".into(), b"MEANLY".into(), b"LEAS".into(), b"MEAN".into()],
)];
for (rpos, cig, [ref_input, query_input, ref_output, query_ouptut]) in data {
assert_eq!(
ref_input.align_and_collect(&query_input, &cig, rpos),
(ref_output, query_ouptut)
);
}
}
#[test]
fn collect_ciglets() {
assert!(
AlignmentStates::from_ciglets_unchecked(vec![
Ciglet { inc: 10, op: b'M' },
Ciglet { inc: 5, op: b'I' },
Ciglet { inc: 12, op: b'D' }
])
.iter()
.copied()
.collect::<Result<AlignmentStates, _>>()
.is_ok()
);
assert_eq!(
AlignmentStates::from_ciglets_unchecked(vec![
Ciglet { inc: 10, op: b'M' },
Ciglet { inc: 0, op: b'I' },
Ciglet { inc: 12, op: b'D' }
])
.iter()
.copied()
.collect::<Result<AlignmentStates, _>>(),
Err(CigarError::IncZero)
);
assert_eq!(
AlignmentStates::from_ciglets_unchecked(vec![
Ciglet { inc: 10, op: b'M' },
Ciglet { inc: 5, op: b'I' },
Ciglet { inc: 12, op: b'Q' }
])
.iter()
.copied()
.collect::<Result<AlignmentStates, _>>(),
Err(CigarError::InvalidOperation)
);
assert_eq!(
AlignmentStates::from_ciglets_unchecked(vec![
Ciglet { inc: 10, op: b'M' },
Ciglet { inc: 5, op: b'I' },
Ciglet { inc: 12, op: b'I' },
])
.iter()
.copied()
.collect::<Result<AlignmentStates, _>>(),
Ok(AlignmentStates::from_ciglets_unchecked(vec![
Ciglet { inc: 10, op: b'M' },
Ciglet { inc: 17, op: b'I' },
]))
);
assert_eq!(
AlignmentStates::from_ciglets_unchecked(vec![
Ciglet { inc: 10, op: 0 },
Ciglet { inc: 5, op: b'I' },
Ciglet { inc: 12, op: b'D' }
])
.iter()
.copied()
.collect::<Result<AlignmentStates, _>>(),
Err(CigarError::InvalidOperation)
);
}
#[test]
fn test_eq_states_cigar() {
let mut states = AlignmentStates::new();
states.add_ciglet(Ciglet { inc: 10, op: b'M' });
let cigar = Cigar::from_slice_unchecked(b"10M3D");
assert_ne!(states, cigar);
assert_ne!(cigar, states);
let cigar = Cigar::from_slice_unchecked(b"10M$$");
assert_ne!(states, cigar);
assert_ne!(cigar, states);
let cigar = Cigar::from_slice_unchecked(b"10M");
assert_eq!(states, cigar);
assert_eq!(cigar, states);
let cigar = Cigar::new();
let states = AlignmentStates::new();
assert_eq!(states, cigar);
assert_eq!(cigar, states);
}
#[test]
#[should_panic(expected = "attempt to add with overflow")]
fn overflow_merging_add_ciglet() {
let mut states = AlignmentStates::from_ciglets_unchecked([Ciglet {
inc: usize::MAX,
op: b'M',
}]);
states.add_ciglet(Ciglet { inc: 1, op: b'M' });
}
#[test]
#[should_panic(expected = "attempt to add with overflow")]
fn overflow_merging_add_state() {
let mut states = AlignmentStates::from_ciglets_unchecked([Ciglet {
inc: usize::MAX,
op: b'M',
}]);
states.add_state(b'M');
}
#[test]
#[should_panic(expected = "attempt to add with overflow")]
fn overflow_merging_add_inc_op() {
let mut states = AlignmentStates::from_ciglets_unchecked([Ciglet {
inc: usize::MAX,
op: b'M',
}]);
states.add_inc_op(1, b'M');
}
#[test]
#[should_panic(expected = "attempt to add with overflow")]
fn overflow_merging_prepend_ciglet() {
let mut states = AlignmentStates::from_ciglets_unchecked([Ciglet {
inc: usize::MAX,
op: b'M',
}]);
states.prepend_ciglet(Ciglet { inc: 1, op: b'M' });
}
#[test]
#[should_panic(expected = "attempt to add with overflow")]
fn overflow_merging_prepend_state() {
let mut states = AlignmentStates::from_ciglets_unchecked([Ciglet {
inc: usize::MAX,
op: b'M',
}]);
states.prepend_state(b'M');
}
#[test]
#[should_panic(expected = "attempt to add with overflow")]
fn overflow_merging_prepend_inc_op() {
let mut states = AlignmentStates::from_ciglets_unchecked([Ciglet {
inc: usize::MAX,
op: b'M',
}]);
states.prepend_inc_op(1, b'M');
}
#[test]
#[should_panic(expected = "attempt to add with overflow")]
fn overflow_merging_add_soft() {
let mut states = AlignmentStates::from_ciglets_unchecked([Ciglet {
inc: usize::MAX,
op: b'S',
}]);
states.soft_clip(1);
}
#[test]
#[should_panic(expected = "attempt to add with overflow")]
fn overflow_merging_prepend_soft() {
let mut states = AlignmentStates::from_ciglets_unchecked([Ciglet {
inc: usize::MAX,
op: b'S',
}]);
states.prepend_soft_clip(1);
}
#[test]
fn overflow_collect() {
let states = AlignmentStates::from_ciglets_unchecked([
Ciglet {
inc: usize::MAX,
op: b'M',
},
Ciglet { inc: 1, op: b'M' },
]);
let result = states.into_iter().collect::<Result<AlignmentStates, _>>();
assert_eq!(result, Err(CigarError::MergeIncOverflow));
}
#[test]
fn overflow_try_from() {
let states = AlignmentStates::from_ciglets_unchecked([
Ciglet {
inc: usize::MAX,
op: b'M',
},
Ciglet { inc: 1, op: b'M' },
]);
let cigar = states.to_cigar_unchecked();
let result = AlignmentStates::try_from(cigar);
assert_eq!(result, Err(CigarError::MergeIncOverflow));
}