use crate::arc::Arc;
use crate::fst::{MutableFst, StateId};
use crate::semiring::Semiring;
use crate::Result;
use std::collections::HashSet;
pub fn arc_unique<W, F>(fst: &mut F) -> Result<()>
where
W: Semiring + Clone + Eq + std::hash::Hash,
F: MutableFst<W>,
{
let num_states = fst.num_states();
for state in 0..num_states as StateId {
let arcs: Vec<Arc<W>> = fst.arcs(state).collect();
if arcs.is_empty() {
continue;
}
let mut seen = HashSet::new();
let mut unique_arcs = Vec::new();
for arc in arcs {
let key = (arc.ilabel, arc.olabel, arc.weight.clone(), arc.nextstate);
if seen.insert(key) {
unique_arcs.push(arc);
}
}
fst.delete_arcs(state);
for arc in unique_arcs {
fst.add_arc(state, arc);
}
}
Ok(())
}
#[cfg(test)]
mod tests {
use super::*;
use crate::prelude::*;
#[test]
fn test_exact_duplicates() {
let mut fst = VectorFst::<TropicalWeight>::new();
let s0 = fst.add_state();
let s1 = fst.add_state();
fst.set_start(s0);
let arc = Arc::new(1, 2, TropicalWeight::new(1.0), s1);
fst.add_arc(s0, arc.clone());
fst.add_arc(s0, arc.clone());
arc_unique(&mut fst).unwrap();
assert_eq!(fst.num_arcs(s0), 1);
let result_arc = fst.arcs(s0).next().unwrap();
assert_eq!(result_arc.ilabel, 1);
assert_eq!(result_arc.olabel, 2);
assert_eq!(result_arc.weight, TropicalWeight::new(1.0));
}
#[test]
fn test_no_duplicates() {
let mut fst = VectorFst::<TropicalWeight>::new();
let s0 = fst.add_state();
let s1 = fst.add_state();
let s2 = fst.add_state();
fst.set_start(s0);
fst.add_arc(s0, Arc::new(1, 1, TropicalWeight::new(1.0), s1));
fst.add_arc(s0, Arc::new(2, 2, TropicalWeight::new(2.0), s2));
arc_unique(&mut fst).unwrap();
assert_eq!(fst.num_arcs(s0), 2);
}
#[test]
fn test_partial_duplicates() {
let mut fst = VectorFst::<TropicalWeight>::new();
let s0 = fst.add_state();
let s1 = fst.add_state();
let s2 = fst.add_state();
fst.set_start(s0);
let arc = Arc::new(1, 1, TropicalWeight::new(1.0), s1);
fst.add_arc(s0, arc.clone());
fst.add_arc(s0, arc.clone()); fst.add_arc(s0, Arc::new(2, 2, TropicalWeight::new(2.0), s2));
arc_unique(&mut fst).unwrap();
assert_eq!(fst.num_arcs(s0), 2);
}
#[test]
fn test_same_labels_different_weights() {
let mut fst = VectorFst::<TropicalWeight>::new();
let s0 = fst.add_state();
let s1 = fst.add_state();
fst.set_start(s0);
fst.add_arc(s0, Arc::new(1, 1, TropicalWeight::new(1.0), s1));
fst.add_arc(s0, Arc::new(1, 1, TropicalWeight::new(2.0), s1));
arc_unique(&mut fst).unwrap();
assert_eq!(fst.num_arcs(s0), 2); }
#[test]
fn test_empty_fst() {
let mut fst = VectorFst::<TropicalWeight>::new();
arc_unique(&mut fst).unwrap();
assert_eq!(fst.num_states(), 0);
}
#[test]
fn test_multiple_exact_copies() {
let mut fst = VectorFst::<TropicalWeight>::new();
let s0 = fst.add_state();
let s1 = fst.add_state();
fst.set_start(s0);
let arc = Arc::new(1, 1, TropicalWeight::new(1.0), s1);
fst.add_arc(s0, arc.clone());
fst.add_arc(s0, arc.clone());
fst.add_arc(s0, arc.clone());
fst.add_arc(s0, arc.clone());
arc_unique(&mut fst).unwrap();
assert_eq!(fst.num_arcs(s0), 1);
}
#[test]
fn test_different_semirings_tropical() {
let mut fst = VectorFst::<TropicalWeight>::new();
let s0 = fst.add_state();
let s1 = fst.add_state();
fst.set_start(s0);
let arc = Arc::new(1, 1, TropicalWeight::new(3.0), s1);
fst.add_arc(s0, arc.clone());
fst.add_arc(s0, arc.clone());
arc_unique(&mut fst).unwrap();
assert_eq!(fst.num_arcs(s0), 1);
}
#[test]
fn test_different_semirings_log() {
let mut fst = VectorFst::<LogWeight>::new();
let s0 = fst.add_state();
let s1 = fst.add_state();
fst.set_start(s0);
let arc = Arc::new(1, 1, LogWeight::new(2.0), s1);
fst.add_arc(s0, arc.clone());
fst.add_arc(s0, arc.clone());
arc_unique(&mut fst).unwrap();
assert_eq!(fst.num_arcs(s0), 1);
}
#[test]
fn test_different_semirings_boolean() {
let mut fst = VectorFst::<BooleanWeight>::new();
let s0 = fst.add_state();
let s1 = fst.add_state();
fst.set_start(s0);
let arc = Arc::new(1, 1, BooleanWeight::one(), s1);
fst.add_arc(s0, arc.clone());
fst.add_arc(s0, arc.clone());
arc_unique(&mut fst).unwrap();
assert_eq!(fst.num_arcs(s0), 1);
}
#[test]
fn test_multiple_states() {
let mut fst = VectorFst::<TropicalWeight>::new();
let s0 = fst.add_state();
let s1 = fst.add_state();
let s2 = fst.add_state();
fst.set_start(s0);
let arc1 = Arc::new(1, 1, TropicalWeight::new(1.0), s1);
fst.add_arc(s0, arc1.clone());
fst.add_arc(s0, arc1.clone());
let arc2 = Arc::new(2, 2, TropicalWeight::new(2.0), s2);
fst.add_arc(s1, arc2.clone());
fst.add_arc(s1, arc2.clone());
arc_unique(&mut fst).unwrap();
assert_eq!(fst.num_arcs(s0), 1);
assert_eq!(fst.num_arcs(s1), 1);
}
}