use smallvec::SmallVec;
use std::fmt;
use crate::transducer::universal::position::{PositionVariant, UniversalPosition};
use crate::transducer::universal::subsumption::subsumes;
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct UniversalState<V: PositionVariant> {
positions: SmallVec<[UniversalPosition<V>; 8]>,
max_distance: u8,
length_diff: i8,
}
impl<V: PositionVariant> UniversalState<V> {
pub fn new(max_distance: u8) -> Self {
Self {
positions: SmallVec::new(),
max_distance,
length_diff: 0,
}
}
pub fn initial(max_distance: u8) -> Self {
let mut state = Self::new(max_distance);
let initial_pos =
UniversalPosition::new_i(0, 0, max_distance).expect("I + 0#0 should always be valid");
state.positions.push(initial_pos);
state
}
pub fn add_position(&mut self, pos: UniversalPosition<V>) {
for existing in &self.positions {
if subsumes(existing, &pos, self.max_distance) {
return; }
}
self.positions
.retain(|p| !subsumes(&pos, p, self.max_distance));
let insert_pos = self.positions.binary_search(&pos).unwrap_or_else(|pos| pos);
self.positions.insert(insert_pos, pos);
}
pub fn is_empty(&self) -> bool {
self.positions.is_empty()
}
pub fn len(&self) -> usize {
self.positions.len()
}
pub fn positions(&self) -> impl Iterator<Item = &UniversalPosition<V>> {
self.positions.iter()
}
pub fn contains(&self, pos: &UniversalPosition<V>) -> bool {
self.positions.iter().any(|p| p == pos)
}
pub fn is_final(&self) -> bool {
self.positions.iter().any(|pos| match pos {
UniversalPosition::MFinal { offset, .. } => *offset <= 0,
_ => false,
})
}
pub fn max_distance(&self) -> u8 {
self.max_distance
}
pub fn is_i_state(&self) -> bool {
!self.is_empty() && self.positions.iter().all(|p| p.is_i_type())
}
pub fn is_m_state(&self) -> bool {
!self.is_empty() && self.positions.iter().all(|p| p.is_m_type())
}
pub fn is_mixed_state(&self) -> bool {
if self.is_empty() {
return false;
}
let has_i = self.positions.iter().any(|p| p.is_i_type());
let has_m = self.positions.iter().any(|p| p.is_m_type());
has_i && has_m
}
#[inline]
pub fn length_diff(&self) -> i8 {
self.length_diff
}
pub fn transition(
&self,
bit_vector: &crate::transducer::universal::CharacteristicVector,
_input_length: usize,
) -> Option<Self> {
if self.is_empty() {
return None;
}
let mut next_state = Self::new(self.max_distance);
for pos in &self.positions {
let successors = pos.successors(bit_vector, self.max_distance);
for succ in successors {
next_state.add_position(succ);
}
}
if next_state.is_empty() {
return None;
}
next_state.length_diff = self.length_diff;
if next_state.is_empty() {
None
} else {
Some(next_state)
}
}
pub fn transition_with_consumption(
&self,
bit_vector: &crate::transducer::universal::CharacteristicVector,
consumed_query: bool,
consumed_dict: bool,
) -> Option<Self> {
if self.is_empty() {
return None;
}
let new_length_diff = self.length_diff
+ match (consumed_query, consumed_dict) {
(true, true) => 0, (true, false) => -1, (false, true) => 1, (false, false) => 0, };
let mut next_state = Self::new(self.max_distance);
next_state.length_diff = new_length_diff;
for pos in &self.positions {
let successors = pos.successors(bit_vector, self.max_distance);
for succ in successors {
next_state.add_position(succ);
}
}
if next_state.is_empty() {
return None;
}
let c = self.max_distance as i32;
if new_length_diff.abs() as i32 > c {
let mut converted_state = Self::new(self.max_distance);
converted_state.length_diff = new_length_diff;
for pos in &next_state.positions {
if let Some(converted) = convert_position_with_length_diff(
pos,
new_length_diff as i32,
self.max_distance,
) {
converted_state.add_position(converted);
}
}
if !converted_state.is_empty() {
next_state = converted_state;
}
}
if next_state.is_empty() {
None
} else {
Some(next_state)
}
}
}
fn convert_position_with_length_diff<V: PositionVariant>(
pos: &UniversalPosition<V>,
length_diff: i32,
max_distance: u8,
) -> Option<UniversalPosition<V>> {
let offset = pos.offset();
let errors = pos.errors();
let n = max_distance as i32;
match pos {
UniversalPosition::INonFinal { .. } => {
let crossing_adjustment = n + 1 - length_diff;
let new_offset = offset + crossing_adjustment;
UniversalPosition::new_m(new_offset, errors, max_distance).ok()
}
UniversalPosition::MFinal { .. } => {
let crossing_adjustment = n + 1 + length_diff;
let new_offset = offset - crossing_adjustment;
UniversalPosition::new_i(new_offset, errors, max_distance).ok()
}
}
}
impl<V: PositionVariant> fmt::Display for UniversalState<V> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{{")?;
let mut first = true;
for pos in &self.positions {
if !first {
write!(f, ", ")?;
}
write!(f, "{}", pos)?;
first = false;
}
write!(f, "}}")
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::transducer::universal::position::Standard;
#[test]
fn test_empty_state() {
let state = UniversalState::<Standard>::new(2);
assert!(state.is_empty());
assert_eq!(state.len(), 0);
assert!(!state.is_final());
assert_eq!(state.max_distance(), 2);
}
#[test]
fn test_initial_state() {
let state = UniversalState::<Standard>::initial(2);
assert!(!state.is_empty());
assert_eq!(state.len(), 1);
assert!(!state.is_final());
let pos = UniversalPosition::new_i(0, 0, 2)
.expect("test fixture: UniversalPosition::new_i with valid args");
assert!(state.contains(&pos));
}
#[test]
fn test_add_single_position() {
let mut state = UniversalState::<Standard>::new(2);
let pos = UniversalPosition::new_i(1, 1, 2)
.expect("test fixture: UniversalPosition::new_i with valid args");
state.add_position(pos.clone());
assert_eq!(state.len(), 1);
assert!(state.contains(&pos));
}
#[test]
fn test_add_multiple_non_subsuming_positions() {
let mut state = UniversalState::<Standard>::new(3);
let pos1 = UniversalPosition::new_i(0, 1, 3)
.expect("test fixture: UniversalPosition::new_i with valid args");
let pos2 = UniversalPosition::new_i(-2, 2, 3)
.expect("test fixture: UniversalPosition::new_i with valid args");
state.add_position(pos1.clone());
state.add_position(pos2.clone());
assert_eq!(state.len(), 2);
assert!(state.contains(&pos1));
assert!(state.contains(&pos2));
}
#[test]
fn test_add_position_removes_subsumed() {
let mut state = UniversalState::<Standard>::new(3);
let pos1 = UniversalPosition::new_i(2, 2, 3)
.expect("test fixture: UniversalPosition::new_i with valid args"); let pos2 = UniversalPosition::new_i(1, 1, 3)
.expect("test fixture: UniversalPosition::new_i with valid args");
state.add_position(pos1.clone());
assert_eq!(state.len(), 1);
state.add_position(pos2.clone());
assert_eq!(state.len(), 1);
assert!(!state.contains(&pos1));
assert!(state.contains(&pos2));
}
#[test]
fn test_add_position_rejected_if_subsumed() {
let mut state = UniversalState::<Standard>::new(3);
let pos1 = UniversalPosition::new_i(1, 1, 3)
.expect("test fixture: UniversalPosition::new_i with valid args"); let pos2 = UniversalPosition::new_i(2, 2, 3)
.expect("test fixture: UniversalPosition::new_i with valid args");
state.add_position(pos1.clone());
assert_eq!(state.len(), 1);
state.add_position(pos2.clone());
assert_eq!(state.len(), 1);
assert!(state.contains(&pos1));
assert!(!state.contains(&pos2));
}
#[test]
fn test_anti_chain_maintained() {
let mut state = UniversalState::<Standard>::new(3);
let pos1 = UniversalPosition::new_i(0, 1, 3)
.expect("test fixture: UniversalPosition::new_i with valid args");
let pos2 = UniversalPosition::new_i(-2, 2, 3)
.expect("test fixture: UniversalPosition::new_i with valid args"); let pos3 = UniversalPosition::new_i(-1, 1, 3)
.expect("test fixture: UniversalPosition::new_i with valid args");
state.add_position(pos1.clone());
state.add_position(pos2.clone());
state.add_position(pos3.clone());
let positions: Vec<_> = state.positions().collect();
for (i, p1) in positions.iter().enumerate() {
for (j, p2) in positions.iter().enumerate() {
if i != j {
assert!(!subsumes(p1, p2, state.max_distance()));
}
}
}
}
#[test]
fn test_final_state_with_m_zero() {
let mut state = UniversalState::<Standard>::new(2);
let pos = UniversalPosition::new_m(0, 0, 2)
.expect("test fixture: UniversalPosition::new_m with valid args");
state.add_position(pos);
assert!(state.is_final());
}
#[test]
fn test_final_state_with_m_negative() {
let mut state = UniversalState::<Standard>::new(2);
let pos = UniversalPosition::new_m(-1, 1, 2)
.expect("test fixture: UniversalPosition::new_m with valid args");
state.add_position(pos);
assert!(state.is_final());
}
#[test]
fn test_not_final_with_only_i_positions() {
let mut state = UniversalState::<Standard>::new(2);
let pos = UniversalPosition::new_i(0, 0, 2)
.expect("test fixture: UniversalPosition::new_i with valid args");
state.add_position(pos);
assert!(!state.is_final());
}
#[test]
fn test_is_i_state() {
let mut state = UniversalState::<Standard>::new(2);
let pos1 = UniversalPosition::new_i(0, 0, 2)
.expect("test fixture: UniversalPosition::new_i with valid args");
let pos2 = UniversalPosition::new_i(1, 1, 2)
.expect("test fixture: UniversalPosition::new_i with valid args");
state.add_position(pos1);
state.add_position(pos2);
assert!(state.is_i_state());
assert!(!state.is_m_state());
assert!(!state.is_mixed_state());
}
#[test]
fn test_is_m_state() {
let mut state = UniversalState::<Standard>::new(2);
let pos1 = UniversalPosition::new_m(0, 0, 2)
.expect("test fixture: UniversalPosition::new_m with valid args");
let pos2 = UniversalPosition::new_m(-1, 1, 2)
.expect("test fixture: UniversalPosition::new_m with valid args");
state.add_position(pos1);
state.add_position(pos2);
assert!(!state.is_i_state());
assert!(state.is_m_state());
assert!(!state.is_mixed_state());
}
#[test]
fn test_is_mixed_state() {
let mut state = UniversalState::<Standard>::new(2);
let i_pos = UniversalPosition::new_i(0, 0, 2)
.expect("test fixture: UniversalPosition::new_i with valid args");
let m_pos = UniversalPosition::new_m(0, 0, 2)
.expect("test fixture: UniversalPosition::new_m with valid args");
state.add_position(i_pos);
state.add_position(m_pos);
assert!(!state.is_i_state());
assert!(!state.is_m_state());
assert!(state.is_mixed_state());
}
#[test]
fn test_positions_iterator() {
let mut state = UniversalState::<Standard>::new(3);
let pos1 = UniversalPosition::new_i(0, 1, 3)
.expect("test fixture: UniversalPosition::new_i with valid args");
let pos2 = UniversalPosition::new_i(-2, 2, 3)
.expect("test fixture: UniversalPosition::new_i with valid args");
state.add_position(pos1.clone());
state.add_position(pos2.clone());
let positions: Vec<_> = state.positions().cloned().collect();
assert_eq!(positions.len(), 2);
assert!(positions.contains(&pos1));
assert!(positions.contains(&pos2));
}
#[test]
fn test_display_empty_state() {
let state = UniversalState::<Standard>::new(2);
assert_eq!(format!("{}", state), "{}");
}
#[test]
fn test_display_single_position() {
let mut state = UniversalState::<Standard>::new(2);
let pos = UniversalPosition::new_i(0, 0, 2)
.expect("test fixture: UniversalPosition::new_i with valid args");
state.add_position(pos);
let display = format!("{}", state);
assert!(display.contains("I + 0#0"));
}
#[test]
fn test_display_multiple_positions() {
let mut state = UniversalState::<Standard>::new(3);
let pos1 = UniversalPosition::new_i(0, 1, 3)
.expect("test fixture: UniversalPosition::new_i with valid args");
let pos2 = UniversalPosition::new_i(-2, 2, 3)
.expect("test fixture: UniversalPosition::new_i with valid args");
state.add_position(pos1);
state.add_position(pos2);
let display = format!("{}", state);
assert!(display.contains("I + 0#1"));
assert!(display.contains("I + -2#2"));
}
#[test]
fn test_state_equality() {
let mut state1 = UniversalState::<Standard>::new(2);
let mut state2 = UniversalState::<Standard>::new(2);
let pos = UniversalPosition::new_i(1, 1, 2)
.expect("test fixture: UniversalPosition::new_i with valid args");
state1.add_position(pos.clone());
state2.add_position(pos);
assert_eq!(state1, state2);
}
#[test]
fn test_state_inequality_different_positions() {
let mut state1 = UniversalState::<Standard>::new(2);
let mut state2 = UniversalState::<Standard>::new(2);
state1.add_position(
UniversalPosition::new_i(0, 0, 2)
.expect("test fixture: UniversalPosition::new_i with valid args"),
);
state2.add_position(
UniversalPosition::new_i(1, 1, 2)
.expect("test fixture: UniversalPosition::new_i with valid args"),
);
assert_ne!(state1, state2);
}
#[test]
fn test_state_clone() {
let mut state1 = UniversalState::<Standard>::new(2);
state1.add_position(
UniversalPosition::new_i(1, 1, 2)
.expect("test fixture: UniversalPosition::new_i with valid args"),
);
let state2 = state1.clone();
assert_eq!(state1, state2);
}
#[test]
fn test_transition_from_initial_match() {
use crate::transducer::universal::CharacteristicVector;
let state = UniversalState::<Standard>::initial(2);
let bv = CharacteristicVector::new('a', "abc");
let next = state.transition(&bv, 1).expect("Should have successor");
assert!(!next.is_empty());
}
#[test]
fn test_transition_from_initial_no_match() {
use crate::transducer::universal::CharacteristicVector;
let state = UniversalState::<Standard>::initial(2);
let bv = CharacteristicVector::new('x', "abc");
let next = state.transition(&bv, 1).expect("Should have successor");
assert!(!next.is_empty());
}
#[test]
fn test_transition_applies_subsumption() {
use crate::transducer::universal::CharacteristicVector;
let mut state = UniversalState::<Standard>::new(3);
state.add_position(
UniversalPosition::new_i(0, 0, 3)
.expect("test fixture: UniversalPosition::new_i with valid args"),
);
let bv = CharacteristicVector::new('x', "abcd");
let next = state.transition(&bv, 1).expect("Should have successor");
let positions: Vec<_> = next.positions().collect();
for (i, p1) in positions.iter().enumerate() {
for (j, p2) in positions.iter().enumerate() {
if i != j {
assert!(!subsumes(p1, p2, next.max_distance()));
}
}
}
}
#[test]
fn test_transition_empty_state() {
use crate::transducer::universal::CharacteristicVector;
let state = UniversalState::<Standard>::new(2);
let bv = CharacteristicVector::new('a', "abc");
assert!(state.transition(&bv, 1).is_none());
}
#[test]
fn test_transition_multiple_positions() {
use crate::transducer::universal::CharacteristicVector;
let mut state = UniversalState::<Standard>::new(2);
state.add_position(
UniversalPosition::new_i(0, 0, 2)
.expect("test fixture: UniversalPosition::new_i with valid args"),
);
state.add_position(
UniversalPosition::new_i(1, 1, 2)
.expect("test fixture: UniversalPosition::new_i with valid args"),
);
let bv = CharacteristicVector::new('a', "abc");
let next = state.transition(&bv, 1).expect("Should have successor");
assert!(!next.is_empty());
}
#[test]
fn test_transition_match_later() {
use crate::transducer::universal::CharacteristicVector;
let state = UniversalState::<Standard>::initial(2);
let bv = CharacteristicVector::new('c', "abc");
let next = state.transition(&bv, 1).expect("Should have successor");
assert!(!next.is_empty());
}
#[test]
fn test_transition_all_errors_consumed() {
use crate::transducer::universal::CharacteristicVector;
let mut state = UniversalState::<Standard>::new(2);
state.add_position(
UniversalPosition::new_i(0, 2, 2)
.expect("test fixture: UniversalPosition::new_i with valid args"),
);
let bv = CharacteristicVector::new('a', "$$abc");
let next = state.transition(&bv, 1).expect("Should have successor");
for pos in next.positions() {
assert!(pos.errors() <= 2, "Position {:?} exceeds max errors", pos);
}
}
#[test]
fn test_transition_preserves_max_distance() {
use crate::transducer::universal::CharacteristicVector;
let state = UniversalState::<Standard>::initial(3);
let bv = CharacteristicVector::new('a', "abc");
let next = state.transition(&bv, 1).expect("Should have successor");
assert_eq!(next.max_distance(), 3);
}
#[test]
fn test_transition_sequence() {
use crate::transducer::universal::CharacteristicVector;
let mut state = UniversalState::<Standard>::initial(2);
let bv1 = CharacteristicVector::new('a', "abc"); state = state.transition(&bv1, 1).expect("Should have successor");
assert!(!state.is_empty());
let bv2 = CharacteristicVector::new('a', "abc"); state = state.transition(&bv2, 2).expect("Should have successor");
assert!(!state.is_empty());
let bv3 = CharacteristicVector::new('a', "abc"); state = state.transition(&bv3, 3).expect("Should have successor");
assert!(!state.is_empty());
}
#[test]
fn test_transition_no_valid_successors() {
use crate::transducer::universal::CharacteristicVector;
let mut state = UniversalState::<Standard>::new(0); state.add_position(
UniversalPosition::new_i(0, 0, 0)
.expect("test fixture: UniversalPosition::new_i with valid args"),
);
let bv = CharacteristicVector::new('x', "abc");
assert!(state.transition(&bv, 1).is_none());
}
#[test]
fn test_transition_from_m_type_state() {
use crate::transducer::universal::CharacteristicVector;
let mut state = UniversalState::<Standard>::new(2);
state.add_position(
UniversalPosition::new_m(-1, 0, 2)
.expect("test fixture: UniversalPosition::new_m with valid args"),
);
let bv = CharacteristicVector::new('a', "abc");
let next = state.transition(&bv, 1).expect("Should have successor");
assert!(!next.is_empty());
for pos in next.positions() {
assert!(pos.is_m_type());
}
}
#[test]
fn test_transition_union_of_successors() {
use crate::transducer::universal::CharacteristicVector;
let mut state = UniversalState::<Standard>::new(2);
state.add_position(
UniversalPosition::new_i(0, 0, 2)
.expect("test fixture: UniversalPosition::new_i with valid args"),
);
state.add_position(
UniversalPosition::new_i(-1, 1, 2)
.expect("test fixture: UniversalPosition::new_i with valid args"),
);
let bv = CharacteristicVector::new('a', "abc");
let next = state.transition(&bv, 1).expect("Should have successor");
assert!(!next.is_empty());
let positions: Vec<_> = next.positions().collect();
for (i, p1) in positions.iter().enumerate() {
for (j, p2) in positions.iter().enumerate() {
if i != j {
assert!(!subsumes(p1, p2, next.max_distance()));
}
}
}
}
#[test]
fn test_transition_multiple_matches() {
use crate::transducer::universal::CharacteristicVector;
let state = UniversalState::<Standard>::initial(2);
let bv = CharacteristicVector::new('a', "aaa");
let next = state.transition(&bv, 1).expect("Should have successor");
assert!(!next.is_empty());
}
}