use crate::transducer::universal::position::{PositionVariant, UniversalPosition};
pub fn right_most<'a, V: PositionVariant>(
positions: impl Iterator<Item = &'a UniversalPosition<V>>,
) -> Option<UniversalPosition<V>>
where
UniversalPosition<V>: 'a,
{
positions
.max_by_key(|pos| {
let offset = pos.offset();
let errors = pos.errors() as i32;
errors - offset
})
.cloned()
}
pub fn diagonal_crossed<V: PositionVariant>(
pos: &UniversalPosition<V>,
k: usize,
max_distance: u8,
) -> bool {
let offset = pos.offset();
let errors = pos.errors() as i32;
let n = max_distance as i32;
let k = k as i32;
match pos {
UniversalPosition::INonFinal { .. } => {
(k <= 2 * n + 1) && (errors <= offset + 2 * n + 1 - k)
}
UniversalPosition::MFinal { .. } => {
errors > offset + n
}
}
}
pub fn convert_position<V: PositionVariant>(
pos: &UniversalPosition<V>,
k: usize,
max_distance: u8,
) -> Option<UniversalPosition<V>> {
let offset = pos.offset();
let errors = pos.errors();
let n = max_distance as i32;
let k = k as i32;
match pos {
UniversalPosition::INonFinal { .. } => {
let new_offset = offset + n + 1 - k;
UniversalPosition::new_m(new_offset, errors, max_distance).ok()
}
UniversalPosition::MFinal { .. } => {
let new_offset = offset - n - 1 + k;
UniversalPosition::new_i(new_offset, errors, max_distance).ok()
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::transducer::universal::position::Standard;
#[test]
fn test_right_most_single_position() {
let positions = vec![UniversalPosition::<Standard>::new_i(0, 1, 2)
.expect("test fixture: UniversalPosition::new_i with valid args")];
let rm =
right_most(positions.iter()).expect("test fixture: right_most on non-empty iterator");
assert_eq!(rm.offset(), 0);
assert_eq!(rm.errors(), 1);
}
#[test]
fn test_right_most_multiple_positions() {
let positions = vec![
UniversalPosition::<Standard>::new_i(0, 1, 2)
.expect("test fixture: UniversalPosition::new_i with valid args"),
UniversalPosition::<Standard>::new_i(-2, 2, 2)
.expect("test fixture: UniversalPosition::new_i with valid args"), UniversalPosition::<Standard>::new_i(-1, 1, 2)
.expect("test fixture: UniversalPosition::new_i with valid args"),
];
let rm =
right_most(positions.iter()).expect("test fixture: right_most on non-empty iterator");
assert_eq!(rm.offset(), -2);
assert_eq!(rm.errors(), 2);
}
#[test]
fn test_right_most_empty_set() {
let positions: Vec<UniversalPosition<Standard>> = vec![];
assert!(right_most(positions.iter()).is_none());
}
#[test]
fn test_right_most_m_type_positions() {
let positions = vec![
UniversalPosition::<Standard>::new_m(0, 0, 2)
.expect("test fixture: UniversalPosition::new_m with valid args"),
UniversalPosition::<Standard>::new_m(-1, 1, 2)
.expect("test fixture: UniversalPosition::new_m with valid args"), ];
let rm =
right_most(positions.iter()).expect("test fixture: right_most on non-empty iterator");
assert_eq!(rm.offset(), -1);
assert_eq!(rm.errors(), 1);
}
#[test]
fn test_diagonal_not_crossed_initial() {
let pos = UniversalPosition::<Standard>::new_i(0, 0, 2)
.expect("test fixture: UniversalPosition::new_i with valid args");
assert!(diagonal_crossed(&pos, 0, 2));
}
#[test]
fn test_diagonal_crossed_i_type() {
let pos = UniversalPosition::<Standard>::new_i(0, 3, 3)
.expect("test fixture: UniversalPosition::new_i with valid args");
assert!(diagonal_crossed(&pos, 2, 3));
}
#[test]
fn test_diagonal_not_crossed_i_type_k_too_large() {
let pos = UniversalPosition::<Standard>::new_i(0, 0, 2)
.expect("test fixture: UniversalPosition::new_i with valid args");
assert!(!diagonal_crossed(&pos, 10, 2));
}
#[test]
fn test_diagonal_crossed_m_type() {
let pos = UniversalPosition::<Standard>::new_m(-1, 2, 2)
.expect("test fixture: UniversalPosition::new_m with valid args");
assert!(diagonal_crossed(&pos, 3, 2));
}
#[test]
fn test_diagonal_not_crossed_m_type() {
let pos = UniversalPosition::<Standard>::new_m(0, 0, 2)
.expect("test fixture: UniversalPosition::new_m with valid args");
assert!(!diagonal_crossed(&pos, 3, 2));
}
#[test]
fn test_convert_i_to_m() {
let i_pos = UniversalPosition::<Standard>::new_i(0, 0, 2)
.expect("test fixture: UniversalPosition::new_i with valid args");
let m_pos = convert_position(&i_pos, 3, 2)
.expect("doc/test fixture: convert_position with valid args");
assert!(m_pos.is_m_type());
assert_eq!(m_pos.offset(), 0);
assert_eq!(m_pos.errors(), 0);
}
#[test]
fn test_convert_m_to_i() {
let m_pos = UniversalPosition::<Standard>::new_m(0, 0, 2)
.expect("test fixture: UniversalPosition::new_m with valid args");
let i_pos = convert_position(&m_pos, 3, 2)
.expect("doc/test fixture: convert_position with valid args");
assert!(i_pos.is_i_type());
assert_eq!(i_pos.offset(), 0);
assert_eq!(i_pos.errors(), 0);
}
#[test]
fn test_convert_i_to_m_with_offset() {
let i_pos = UniversalPosition::<Standard>::new_i(0, 1, 2)
.expect("test fixture: UniversalPosition::new_i with valid args");
let m_pos = convert_position(&i_pos, 3, 2)
.expect("doc/test fixture: convert_position with valid args");
assert!(m_pos.is_m_type());
assert_eq!(m_pos.offset(), 0);
assert_eq!(m_pos.errors(), 1);
}
#[test]
fn test_convert_m_to_i_with_offset() {
let m_pos = UniversalPosition::<Standard>::new_m(-1, 1, 2)
.expect("test fixture: UniversalPosition::new_m with valid args");
let i_pos = convert_position(&m_pos, 3, 2)
.expect("doc/test fixture: convert_position with valid args");
assert!(i_pos.is_i_type());
assert_eq!(i_pos.offset(), -1);
assert_eq!(i_pos.errors(), 1);
}
#[test]
fn test_convert_invalid_result() {
let i_pos = UniversalPosition::<Standard>::new_i(5, 5, 5)
.expect("test fixture: UniversalPosition::new_i with valid args");
let result = convert_position(&i_pos, 1, 2);
if let Some(m_pos) = result {
assert!(m_pos.is_m_type());
}
}
#[test]
fn test_convert_preserves_errors() {
let i_pos = UniversalPosition::<Standard>::new_i(0, 2, 2)
.expect("test fixture: UniversalPosition::new_i with valid args");
let m_pos = convert_position(&i_pos, 3, 2)
.expect("doc/test fixture: convert_position with valid args");
assert_eq!(m_pos.errors(), 2);
let m_pos = UniversalPosition::<Standard>::new_m(0, 2, 2)
.expect("test fixture: UniversalPosition::new_m with valid args");
let i_pos = convert_position(&m_pos, 3, 2)
.expect("doc/test fixture: convert_position with valid args");
assert_eq!(i_pos.errors(), 2);
}
#[test]
fn test_convert_roundtrip() {
let i_pos = UniversalPosition::<Standard>::new_i(0, 0, 2)
.expect("test fixture: UniversalPosition::new_i with valid args");
let m_pos = convert_position(&i_pos, 3, 2)
.expect("doc/test fixture: convert_position with valid args");
let i_pos2 = convert_position(&m_pos, 3, 2)
.expect("doc/test fixture: convert_position with valid args");
assert_eq!(i_pos.offset(), i_pos2.offset());
assert_eq!(i_pos.errors(), i_pos2.errors());
}
}