use std::fmt;
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum PositionError {
InvalidIPosition {
offset: i32,
errors: u8,
max_distance: u8,
},
InvalidMPosition {
offset: i32,
errors: u8,
max_distance: u8,
},
}
impl fmt::Display for PositionError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
PositionError::InvalidIPosition {
offset,
errors,
max_distance,
} => {
write!(
f,
"Invalid I-position: I + {}#{} with n={}. \
Invariant: |t| ≤ k ∧ -n ≤ t ≤ n ∧ 0 ≤ k ≤ n",
offset, errors, max_distance
)
}
PositionError::InvalidMPosition {
offset,
errors,
max_distance,
} => {
write!(
f,
"Invalid M-position: M + {}#{} with n={}. \
Invariant: k ≥ -t - n ∧ -2n ≤ t ≤ 0 ∧ 0 ≤ k ≤ n",
offset, errors, max_distance
)
}
}
}
}
impl std::error::Error for PositionError {}
pub trait PositionVariant: Clone + fmt::Debug + PartialEq + Eq + std::hash::Hash {
type State: Clone + fmt::Debug + PartialEq + Eq + std::hash::Hash + Default;
fn variant_name() -> &'static str;
fn compute_i_successors(
offset: i32,
errors: u8,
variant_state: &Self::State,
bit_vector: &crate::transducer::universal::CharacteristicVector,
max_distance: u8,
) -> Vec<UniversalPosition<Self>>;
fn compute_m_successors(
offset: i32,
errors: u8,
variant_state: &Self::State,
bit_vector: &crate::transducer::universal::CharacteristicVector,
max_distance: u8,
) -> Vec<UniversalPosition<Self>>;
}
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct Standard;
impl PositionVariant for Standard {
type State = ();
fn variant_name() -> &'static str {
"Standard"
}
fn compute_i_successors(
offset: i32,
errors: u8,
_variant_state: &Self::State,
bit_vector: &crate::transducer::universal::CharacteristicVector,
max_distance: u8,
) -> Vec<UniversalPosition<Self>> {
UniversalPosition::<Self>::successors_i_type_standard(
offset,
errors,
bit_vector,
max_distance,
)
}
fn compute_m_successors(
offset: i32,
errors: u8,
_variant_state: &Self::State,
bit_vector: &crate::transducer::universal::CharacteristicVector,
max_distance: u8,
) -> Vec<UniversalPosition<Self>> {
UniversalPosition::<Self>::successors_m_type_standard(
offset,
errors,
bit_vector,
max_distance,
)
}
}
#[derive(Clone, Debug, PartialEq, Eq, Hash, Default)]
pub enum TranspositionState {
#[default]
Usual,
Transposing,
}
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct Transposition;
impl PositionVariant for Transposition {
type State = TranspositionState;
fn variant_name() -> &'static str {
"Transposition"
}
fn compute_i_successors(
offset: i32,
errors: u8,
variant_state: &Self::State,
bit_vector: &crate::transducer::universal::CharacteristicVector,
max_distance: u8,
) -> Vec<UniversalPosition<Self>> {
match variant_state {
TranspositionState::Usual => {
let mut successors = UniversalPosition::<Self>::successors_i_type_standard(
offset,
errors,
bit_vector,
max_distance,
);
let next_match_index = (max_distance as i32 + offset + 1) as usize;
if next_match_index < bit_vector.len()
&& bit_vector.is_match(next_match_index)
&& errors < max_distance
{
if let Ok(trans) = UniversalPosition::new_i_with_state(
offset - 1,
errors + 1,
max_distance,
TranspositionState::Transposing,
) {
successors.push(trans);
}
}
successors
}
TranspositionState::Transposing => {
let match_index = (max_distance as i32 + offset) as usize;
if match_index < bit_vector.len() && bit_vector.is_match(match_index) {
if let Ok(succ) = UniversalPosition::new_i_with_state(
offset + 1,
errors - 1,
max_distance,
TranspositionState::Usual,
) {
vec![succ]
} else {
vec![]
}
} else {
vec![]
}
}
}
}
fn compute_m_successors(
offset: i32,
errors: u8,
variant_state: &Self::State,
bit_vector: &crate::transducer::universal::CharacteristicVector,
max_distance: u8,
) -> Vec<UniversalPosition<Self>> {
match variant_state {
TranspositionState::Usual => {
let mut successors = UniversalPosition::<Self>::successors_m_type_standard(
offset,
errors,
bit_vector,
max_distance,
);
let next_match_index = (max_distance as i32 + offset + 1) as usize;
if next_match_index < bit_vector.len()
&& bit_vector.is_match(next_match_index)
&& errors < max_distance
{
if let Ok(trans) = UniversalPosition::new_m_with_state(
offset - 1,
errors + 1,
max_distance,
TranspositionState::Transposing,
) {
successors.push(trans);
}
}
successors
}
TranspositionState::Transposing => {
let match_index = (max_distance as i32 + offset) as usize;
if match_index < bit_vector.len() && bit_vector.is_match(match_index) {
if let Ok(succ) = UniversalPosition::new_m_with_state(
offset + 1,
errors - 1,
max_distance,
TranspositionState::Usual,
) {
vec![succ]
} else {
vec![]
}
} else {
vec![]
}
}
}
}
}
#[derive(Clone, Debug, PartialEq, Eq, Hash, Default)]
pub enum MergeSplitState {
#[default]
Usual,
Splitting,
}
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct MergeAndSplit;
impl PositionVariant for MergeAndSplit {
type State = MergeSplitState;
fn variant_name() -> &'static str {
"MergeAndSplit"
}
fn compute_i_successors(
offset: i32,
errors: u8,
variant_state: &Self::State,
bit_vector: &crate::transducer::universal::CharacteristicVector,
max_distance: u8,
) -> Vec<UniversalPosition<Self>> {
let is_splitting = matches!(variant_state, MergeSplitState::Splitting);
let mut successors = UniversalPosition::<Self>::successors_i_type_standard(
offset,
errors,
bit_vector,
max_distance,
);
let match_index = (max_distance as i32 + offset) as usize;
let next_match_index = (max_distance as i32 + offset + 1) as usize;
if is_splitting {
if match_index < bit_vector.len() && bit_vector.is_match(match_index) {
if let Ok(succ) = UniversalPosition::new_i_with_state(
offset, errors - 1, max_distance,
MergeSplitState::Usual,
) {
successors.push(succ);
}
}
} else {
if next_match_index < bit_vector.len()
&& bit_vector.is_match(next_match_index)
&& errors < max_distance
{
if let Ok(merge) = UniversalPosition::new_i_with_state(
offset + 1,
errors + 1,
max_distance,
MergeSplitState::Usual,
) {
successors.push(merge);
}
}
if match_index < bit_vector.len()
&& bit_vector.is_match(match_index)
&& errors < max_distance
{
if let Ok(split) = UniversalPosition::new_i_with_state(
offset - 1,
errors + 1,
max_distance,
MergeSplitState::Splitting,
) {
successors.push(split);
}
}
}
successors
}
fn compute_m_successors(
offset: i32,
errors: u8,
variant_state: &Self::State,
bit_vector: &crate::transducer::universal::CharacteristicVector,
max_distance: u8,
) -> Vec<UniversalPosition<Self>> {
let is_splitting = matches!(variant_state, MergeSplitState::Splitting);
let mut successors = UniversalPosition::<Self>::successors_m_type_standard(
offset,
errors,
bit_vector,
max_distance,
);
let match_index = (max_distance as i32 + offset) as usize;
let next_match_index = (max_distance as i32 + offset + 1) as usize;
if is_splitting {
if match_index < bit_vector.len() && bit_vector.is_match(match_index) {
if let Ok(succ) = UniversalPosition::new_m_with_state(
offset, errors - 1, max_distance,
MergeSplitState::Usual,
) {
successors.push(succ);
}
}
} else {
if next_match_index < bit_vector.len()
&& bit_vector.is_match(next_match_index)
&& errors < max_distance
{
if let Ok(merge) = UniversalPosition::new_m_with_state(
offset + 1,
errors + 1,
max_distance,
MergeSplitState::Usual,
) {
successors.push(merge);
}
}
if match_index < bit_vector.len()
&& bit_vector.is_match(match_index)
&& errors < max_distance
{
if let Ok(split) = UniversalPosition::new_m_with_state(
offset - 1,
errors + 1,
max_distance,
MergeSplitState::Splitting,
) {
successors.push(split);
}
}
}
successors
}
}
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub enum UniversalPosition<V: PositionVariant> {
INonFinal {
offset: i32,
errors: u8,
variant_state: V::State,
},
MFinal {
offset: i32,
errors: u8,
variant_state: V::State,
},
}
impl<V: PositionVariant> PartialOrd for UniversalPosition<V> {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
Some(self.cmp(other))
}
}
impl<V: PositionVariant> Ord for UniversalPosition<V> {
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
use std::cmp::Ordering;
use UniversalPosition::*;
match (self, other) {
(
INonFinal {
errors: e1,
offset: o1,
..
},
INonFinal {
errors: e2,
offset: o2,
..
},
)
| (
MFinal {
errors: e1,
offset: o1,
..
},
MFinal {
errors: e2,
offset: o2,
..
},
) => match e1.cmp(e2) {
Ordering::Equal => o1.cmp(o2),
other => other,
},
(INonFinal { .. }, MFinal { .. }) => Ordering::Less,
(MFinal { .. }, INonFinal { .. }) => Ordering::Greater,
}
}
}
impl<V: PositionVariant> UniversalPosition<V> {
pub fn new_i(offset: i32, errors: u8, max_distance: u8) -> Result<Self, PositionError> {
let n = max_distance as i32;
if offset.abs() as u8 > errors || offset < -n || offset > n || errors > max_distance {
return Err(PositionError::InvalidIPosition {
offset,
errors,
max_distance,
});
}
Ok(Self::INonFinal {
offset,
errors,
variant_state: V::State::default(),
})
}
pub fn new_m(offset: i32, errors: u8, max_distance: u8) -> Result<Self, PositionError> {
let n = max_distance as i32;
if (errors as i32) < -offset - n || offset < -2 * n || offset > 0 || errors > max_distance {
return Err(PositionError::InvalidMPosition {
offset,
errors,
max_distance,
});
}
Ok(Self::MFinal {
offset,
errors,
variant_state: V::State::default(),
})
}
pub fn new_i_with_state(
offset: i32,
errors: u8,
max_distance: u8,
variant_state: V::State,
) -> Result<Self, PositionError> {
let n = max_distance as i32;
if offset.abs() as u8 > errors || offset < -n || offset > n || errors > max_distance {
return Err(PositionError::InvalidIPosition {
offset,
errors,
max_distance,
});
}
Ok(Self::INonFinal {
offset,
errors,
variant_state,
})
}
pub fn new_m_with_state(
offset: i32,
errors: u8,
max_distance: u8,
variant_state: V::State,
) -> Result<Self, PositionError> {
let n = max_distance as i32;
if (errors as i32) < -offset - n || offset < -2 * n || offset > 0 || errors > max_distance {
return Err(PositionError::InvalidMPosition {
offset,
errors,
max_distance,
});
}
Ok(Self::MFinal {
offset,
errors,
variant_state,
})
}
pub fn offset(&self) -> i32 {
match self {
Self::INonFinal { offset, .. } | Self::MFinal { offset, .. } => *offset,
}
}
pub fn variant_state(&self) -> &V::State {
match self {
Self::INonFinal { variant_state, .. } | Self::MFinal { variant_state, .. } => {
variant_state
}
}
}
pub fn errors(&self) -> u8 {
match self {
Self::INonFinal { errors, .. } | Self::MFinal { errors, .. } => *errors,
}
}
pub fn is_i_type(&self) -> bool {
matches!(self, Self::INonFinal { .. })
}
pub fn is_m_type(&self) -> bool {
matches!(self, Self::MFinal { .. })
}
pub fn successors(
&self,
bit_vector: &crate::transducer::universal::CharacteristicVector,
max_distance: u8,
) -> Vec<Self> {
match self {
Self::INonFinal {
offset,
errors,
variant_state,
} => V::compute_i_successors(*offset, *errors, variant_state, bit_vector, max_distance),
Self::MFinal {
offset,
errors,
variant_state,
} => V::compute_m_successors(*offset, *errors, variant_state, bit_vector, max_distance),
}
}
fn successors_i_type_standard(
offset: i32,
errors: u8,
bit_vector: &crate::transducer::universal::CharacteristicVector,
max_distance: u8,
) -> Vec<Self> {
let mut successors = Vec::new();
let match_index = (max_distance as i32 + offset) as usize;
if match_index < bit_vector.len() {
if bit_vector.is_match(match_index) {
if let Ok(succ) = Self::new_i(offset, errors, max_distance) {
successors.push(succ);
}
return successors;
} else {
if errors < max_distance {
if let Ok(succ) = Self::new_i(offset - 1, errors + 1, max_distance) {
successors.push(succ);
}
if let Ok(succ) = Self::new_i(offset, errors + 1, max_distance) {
successors.push(succ);
}
for idx in (match_index + 1)..bit_vector.len() {
if bit_vector.is_match(idx) {
let skip_distance = (idx - match_index) as i32;
let new_offset = offset + skip_distance;
let new_errors = errors + skip_distance as u8;
if new_errors <= max_distance {
if let Ok(succ) = Self::new_i(new_offset, new_errors, max_distance)
{
successors.push(succ);
}
}
break;
}
}
}
return successors;
}
}
if errors >= max_distance {
return successors;
}
if bit_vector.is_empty() {
if let Ok(succ) = Self::new_i(offset - 1, errors + 1, max_distance) {
successors.push(succ);
}
return successors;
}
if let Ok(succ) = Self::new_i(offset - 1, errors + 1, max_distance) {
successors.push(succ);
}
if let Ok(succ) = Self::new_i(offset, errors + 1, max_distance) {
successors.push(succ);
}
successors
}
fn successors_m_type_standard(
offset: i32,
errors: u8,
bit_vector: &crate::transducer::universal::CharacteristicVector,
max_distance: u8,
) -> Vec<Self> {
let mut successors = Vec::new();
if bit_vector.starts_with_one() {
if let Ok(succ) = Self::new_m(offset + 1, errors, max_distance) {
successors.push(succ);
}
return successors;
}
if errors >= max_distance {
return successors; }
if bit_vector.is_empty() {
if let Ok(succ) = Self::new_m(offset, errors + 1, max_distance) {
successors.push(succ);
}
return successors;
}
if bit_vector.is_all_zeros() {
if let Ok(succ) = Self::new_m(offset, errors + 1, max_distance) {
successors.push(succ);
}
if let Ok(succ) = Self::new_m(offset + 1, errors + 1, max_distance) {
successors.push(succ);
}
return successors;
}
if let Ok(succ) = Self::new_m(offset, errors + 1, max_distance) {
successors.push(succ);
}
if let Ok(succ) = Self::new_m(offset + 1, errors + 1, max_distance) {
successors.push(succ);
}
if let Some(j) = bit_vector.first_match() {
let j = j as i32;
let new_offset = offset + j + 1;
let new_errors = errors + j as u8;
if new_errors <= max_distance {
if let Ok(succ) = Self::new_m(new_offset, new_errors, max_distance) {
successors.push(succ);
}
}
}
successors
}
}
impl<V: PositionVariant> fmt::Display for UniversalPosition<V> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::INonFinal { offset, errors, .. } => {
write!(f, "I + {}#{}", offset, errors)
}
Self::MFinal { offset, errors, .. } => {
write!(f, "M + {}#{}", offset, errors)
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_i_position_initial_state() {
let pos = UniversalPosition::<Standard>::new_i(0, 0, 2)
.expect("test fixture: UniversalPosition::new_i with valid args");
assert_eq!(pos.offset(), 0);
assert_eq!(pos.errors(), 0);
assert!(pos.is_i_type());
assert!(!pos.is_m_type());
assert_eq!(format!("{}", pos), "I + 0#0");
}
#[test]
fn test_i_position_positive_offset() {
let pos = UniversalPosition::<Standard>::new_i(2, 2, 3)
.expect("test fixture: UniversalPosition::new_i with valid args");
assert_eq!(pos.offset(), 2);
assert_eq!(pos.errors(), 2);
}
#[test]
fn test_i_position_negative_offset() {
let pos = UniversalPosition::<Standard>::new_i(-2, 2, 3)
.expect("test fixture: UniversalPosition::new_i with valid args");
assert_eq!(pos.offset(), -2);
assert_eq!(pos.errors(), 2);
}
#[test]
fn test_i_position_boundary_max_n() {
let pos = UniversalPosition::<Standard>::new_i(3, 3, 3)
.expect("test fixture: UniversalPosition::new_i with valid args");
assert_eq!(pos.offset(), 3);
assert_eq!(pos.errors(), 3);
}
#[test]
fn test_i_position_boundary_min_n() {
let pos = UniversalPosition::<Standard>::new_i(-3, 3, 3)
.expect("test fixture: UniversalPosition::new_i with valid args");
assert_eq!(pos.offset(), -3);
assert_eq!(pos.errors(), 3);
}
#[test]
fn test_i_position_violates_offset_abs_constraint() {
let result = UniversalPosition::<Standard>::new_i(3, 2, 3);
assert!(matches!(
result,
Err(PositionError::InvalidIPosition { .. })
));
}
#[test]
fn test_i_position_violates_offset_too_large() {
let result = UniversalPosition::<Standard>::new_i(4, 3, 3);
assert!(matches!(
result,
Err(PositionError::InvalidIPosition { .. })
));
}
#[test]
fn test_i_position_violates_offset_too_negative() {
let result = UniversalPosition::<Standard>::new_i(-4, 3, 3);
assert!(matches!(
result,
Err(PositionError::InvalidIPosition { .. })
));
}
#[test]
fn test_i_position_violates_errors_too_large() {
let result = UniversalPosition::<Standard>::new_i(0, 4, 3);
assert!(matches!(
result,
Err(PositionError::InvalidIPosition { .. })
));
}
#[test]
fn test_m_position_final_exact() {
let pos = UniversalPosition::<Standard>::new_m(0, 0, 2)
.expect("test fixture: UniversalPosition::new_m with valid args");
assert_eq!(pos.offset(), 0);
assert_eq!(pos.errors(), 0);
assert!(!pos.is_i_type());
assert!(pos.is_m_type());
assert_eq!(format!("{}", pos), "M + 0#0");
}
#[test]
fn test_m_position_one_before_end() {
let pos = UniversalPosition::<Standard>::new_m(-1, 1, 2)
.expect("test fixture: UniversalPosition::new_m with valid args");
assert_eq!(pos.offset(), -1);
assert_eq!(pos.errors(), 1);
}
#[test]
fn test_m_position_boundary_min_offset() {
let pos = UniversalPosition::<Standard>::new_m(-4, 2, 2)
.expect("test fixture: UniversalPosition::new_m with valid args");
assert_eq!(pos.offset(), -4);
assert_eq!(pos.errors(), 2);
}
#[test]
fn test_m_position_violates_offset_positive() {
let result = UniversalPosition::<Standard>::new_m(1, 0, 2);
assert!(matches!(
result,
Err(PositionError::InvalidMPosition { .. })
));
}
#[test]
fn test_m_position_violates_offset_too_negative() {
let result = UniversalPosition::<Standard>::new_m(-5, 2, 2);
assert!(matches!(
result,
Err(PositionError::InvalidMPosition { .. })
));
}
#[test]
fn test_m_position_violates_errors_constraint() {
let result = UniversalPosition::<Standard>::new_m(-3, 0, 2);
assert!(matches!(
result,
Err(PositionError::InvalidMPosition { .. })
));
}
#[test]
fn test_m_position_violates_errors_too_large() {
let result = UniversalPosition::<Standard>::new_m(-1, 3, 2);
assert!(matches!(
result,
Err(PositionError::InvalidMPosition { .. })
));
}
#[test]
fn test_position_variants_different_types() {
let std_pos = UniversalPosition::<Standard>::new_i(0, 0, 2)
.expect("test fixture: UniversalPosition::new_i with valid args");
let trans_pos = UniversalPosition::<Transposition>::new_i(0, 0, 2)
.expect("test fixture: UniversalPosition::new_i with valid args");
let ms_pos = UniversalPosition::<MergeAndSplit>::new_i(0, 0, 2)
.expect("test fixture: UniversalPosition::new_i with valid args");
assert_eq!(std_pos.offset(), trans_pos.offset());
assert_eq!(std_pos.errors(), trans_pos.errors());
assert_eq!(std_pos.offset(), ms_pos.offset());
}
#[test]
fn test_variant_names() {
assert_eq!(Standard::variant_name(), "Standard");
assert_eq!(Transposition::variant_name(), "Transposition");
assert_eq!(MergeAndSplit::variant_name(), "MergeAndSplit");
}
#[test]
fn test_position_equality() {
let pos1 = UniversalPosition::<Standard>::new_i(1, 1, 2)
.expect("test fixture: UniversalPosition::new_i with valid args");
let pos2 = UniversalPosition::<Standard>::new_i(1, 1, 2)
.expect("test fixture: UniversalPosition::new_i with valid args");
let pos3 = UniversalPosition::<Standard>::new_i(1, 2, 2)
.expect("test fixture: UniversalPosition::new_i with valid args");
assert_eq!(pos1, pos2);
assert_ne!(pos1, pos3);
}
#[test]
fn test_position_clone() {
let pos1 = UniversalPosition::<Standard>::new_i(1, 1, 2)
.expect("test fixture: UniversalPosition::new_i with valid args");
let pos2 = pos1.clone();
assert_eq!(pos1, pos2);
assert_eq!(pos1.offset(), pos2.offset());
assert_eq!(pos1.errors(), pos2.errors());
}
#[test]
fn test_display_i_positions() {
let pos1 = UniversalPosition::<Standard>::new_i(0, 0, 2)
.expect("test fixture: UniversalPosition::new_i with valid args");
let pos2 = UniversalPosition::<Standard>::new_i(2, 2, 2)
.expect("test fixture: UniversalPosition::new_i with valid args");
let pos3 = UniversalPosition::<Standard>::new_i(-1, 1, 2)
.expect("test fixture: UniversalPosition::new_i with valid args");
assert_eq!(format!("{}", pos1), "I + 0#0");
assert_eq!(format!("{}", pos2), "I + 2#2");
assert_eq!(format!("{}", pos3), "I + -1#1");
}
#[test]
fn test_display_m_positions() {
let pos1 = UniversalPosition::<Standard>::new_m(0, 0, 2)
.expect("test fixture: UniversalPosition::new_m with valid args");
let pos2 = UniversalPosition::<Standard>::new_m(-2, 0, 2)
.expect("test fixture: UniversalPosition::new_m with valid args");
let pos3 = UniversalPosition::<Standard>::new_m(-1, 1, 2)
.expect("test fixture: UniversalPosition::new_m with valid args");
assert_eq!(format!("{}", pos1), "M + 0#0");
assert_eq!(format!("{}", pos2), "M + -2#0");
assert_eq!(format!("{}", pos3), "M + -1#1");
}
#[test]
fn test_position_error_display() {
let err = PositionError::InvalidIPosition {
offset: 3,
errors: 1,
max_distance: 2,
};
let display = format!("{}", err);
assert!(display.contains("Invalid I-position"));
assert!(display.contains("3#1"));
assert!(display.contains("n=2"));
let err = PositionError::InvalidMPosition {
offset: -5,
errors: 0,
max_distance: 2,
};
let display = format!("{}", err);
assert!(display.contains("Invalid M-position"));
assert!(display.contains("-5#0"));
assert!(display.contains("n=2"));
}
use crate::transducer::universal::CharacteristicVector;
#[test]
fn test_successors_match() {
let pos = UniversalPosition::<Standard>::new_i(0, 0, 2)
.expect("test fixture: UniversalPosition::new_i with valid args");
let bv = CharacteristicVector::new('a', "$$abc");
let succs = pos.successors(&bv, 2);
assert_eq!(succs.len(), 1, "Expected 1 successor for match");
assert_eq!(
succs[0],
UniversalPosition::<Standard>::new_i(0, 0, 2)
.expect("test fixture: UniversalPosition::new_i with valid args")
);
}
#[test]
fn test_successors_all_zeros() {
let pos = UniversalPosition::<Standard>::new_i(0, 0, 2)
.expect("test fixture: UniversalPosition::new_i with valid args");
let bv = CharacteristicVector::new('x', "abc");
let succs = pos.successors(&bv, 2);
assert_eq!(succs.len(), 2);
assert!(succs.contains(
&UniversalPosition::<Standard>::new_i(-1, 1, 2)
.expect("test fixture: UniversalPosition::new_i with valid args")
));
assert!(succs.contains(
&UniversalPosition::<Standard>::new_i(0, 1, 2)
.expect("test fixture: UniversalPosition::new_i with valid args")
));
}
#[test]
fn test_successors_match_later() {
let pos = UniversalPosition::<Standard>::new_i(0, 0, 2)
.expect("test fixture: UniversalPosition::new_i with valid args");
let bv = CharacteristicVector::new('b', "$$abc");
let succs = pos.successors(&bv, 2);
assert_eq!(succs.len(), 3, "Expected 3 successors");
assert!(succs.contains(
&UniversalPosition::<Standard>::new_i(-1, 1, 2)
.expect("test fixture: UniversalPosition::new_i with valid args")
));
assert!(succs.contains(
&UniversalPosition::<Standard>::new_i(0, 1, 2)
.expect("test fixture: UniversalPosition::new_i with valid args")
));
assert!(succs.contains(
&UniversalPosition::<Standard>::new_i(1, 1, 2)
.expect("test fixture: UniversalPosition::new_i with valid args")
));
}
#[test]
fn test_successors_empty_bit_vector() {
let pos = UniversalPosition::<Standard>::new_i(0, 0, 2)
.expect("test fixture: UniversalPosition::new_i with valid args");
let bv = CharacteristicVector::new('a', "");
let succs = pos.successors(&bv, 2);
assert_eq!(succs.len(), 1);
assert_eq!(
succs[0],
UniversalPosition::<Standard>::new_i(-1, 1, 2)
.expect("test fixture: UniversalPosition::new_i with valid args")
);
}
#[test]
fn test_successors_max_errors_reached() {
let pos = UniversalPosition::<Standard>::new_i(0, 2, 2)
.expect("test fixture: UniversalPosition::new_i with valid args");
let bv = CharacteristicVector::new('x', "abc");
let succs = pos.successors(&bv, 2);
assert_eq!(succs.len(), 0);
}
#[test]
fn test_successors_match_at_max_errors() {
let pos = UniversalPosition::<Standard>::new_i(0, 2, 2)
.expect("test fixture: UniversalPosition::new_i with valid args");
let bv = CharacteristicVector::new('a', "$$abc");
let succs = pos.successors(&bv, 2);
assert_eq!(
succs.len(),
1,
"Expected 1 successor for match at max errors"
);
assert_eq!(
succs[0],
UniversalPosition::<Standard>::new_i(0, 2, 2)
.expect("test fixture: UniversalPosition::new_i with valid args")
);
}
#[test]
fn test_successors_negative_offset() {
let pos = UniversalPosition::<Standard>::new_i(-1, 1, 2)
.expect("test fixture: UniversalPosition::new_i with valid args");
let bv = CharacteristicVector::new('$', "$$abc");
let succs = pos.successors(&bv, 2);
assert_eq!(
succs.len(),
1,
"Expected 1 successor for match with negative offset"
);
assert_eq!(
succs[0],
UniversalPosition::<Standard>::new_i(-1, 1, 2)
.expect("test fixture: UniversalPosition::new_i with valid args")
);
}
#[test]
fn test_successors_skip_multiple() {
let pos = UniversalPosition::<Standard>::new_i(0, 0, 3)
.expect("test fixture: UniversalPosition::new_i with valid args");
let bv = CharacteristicVector::new('c', "$$$abcd");
let succs = pos.successors(&bv, 3);
assert_eq!(succs.len(), 3, "Expected 3 successors");
assert!(succs.contains(
&UniversalPosition::<Standard>::new_i(-1, 1, 3)
.expect("test fixture: UniversalPosition::new_i with valid args")
));
assert!(succs.contains(
&UniversalPosition::<Standard>::new_i(0, 1, 3)
.expect("test fixture: UniversalPosition::new_i with valid args")
));
assert!(succs.contains(
&UniversalPosition::<Standard>::new_i(2, 2, 3)
.expect("test fixture: UniversalPosition::new_i with valid args")
));
}
#[test]
fn test_successors_invariant_violation_filtered() {
let pos = UniversalPosition::<Standard>::new_i(2, 2, 2)
.expect("test fixture: UniversalPosition::new_i with valid args");
let bv = CharacteristicVector::new('x', "abc");
let succs = pos.successors(&bv, 2);
assert_eq!(succs.len(), 0);
}
#[test]
fn test_successors_m_type_position() {
let pos = UniversalPosition::<Standard>::new_m(-1, 0, 2)
.expect("test fixture: UniversalPosition::new_m with valid args");
let bv = CharacteristicVector::new('a', "abc");
let succs = pos.successors(&bv, 2);
assert_eq!(succs.len(), 1);
assert_eq!(
succs[0],
UniversalPosition::<Standard>::new_m(0, 0, 2)
.expect("test fixture: UniversalPosition::new_m with valid args")
);
}
#[test]
fn test_successors_boundary_offset() {
let pos = UniversalPosition::<Standard>::new_i(2, 2, 2)
.expect("test fixture: UniversalPosition::new_i with valid args");
let bv = CharacteristicVector::new('c', "$$abc");
let succs = pos.successors(&bv, 2);
assert_eq!(
succs.len(),
1,
"Expected 1 successor for boundary offset match"
);
assert_eq!(
succs[0],
UniversalPosition::<Standard>::new_i(2, 2, 2)
.expect("test fixture: UniversalPosition::new_i with valid args")
);
}
#[test]
fn test_successors_multiple_matches() {
let pos = UniversalPosition::<Standard>::new_i(0, 0, 2)
.expect("test fixture: UniversalPosition::new_i with valid args");
let bv = CharacteristicVector::new('a', "$$aba");
let succs = pos.successors(&bv, 2);
assert_eq!(succs.len(), 1, "Expected 1 successor for first match");
assert_eq!(
succs[0],
UniversalPosition::<Standard>::new_i(0, 0, 2)
.expect("test fixture: UniversalPosition::new_i with valid args")
);
}
#[test]
fn test_successors_preserves_variant_type() {
let pos = UniversalPosition::<Standard>::new_i(0, 0, 2)
.expect("test fixture: UniversalPosition::new_i with valid args");
let bv = CharacteristicVector::new('a', "$$a");
let succs = pos.successors(&bv, 2);
assert_eq!(succs.len(), 1, "Expected 1 successor");
assert_eq!(
succs[0],
UniversalPosition::<Standard>::new_i(0, 0, 2)
.expect("test fixture: UniversalPosition::new_i with valid args")
);
}
}