use crate::arc::Arc;
use crate::fst::{Fst, MutableFst};
use crate::semiring::Semiring;
use crate::Result;
pub fn concat<W, F1, F2, M>(fst1: &F1, fst2: &F2) -> Result<M>
where
W: Semiring,
F1: Fst<W>,
F2: Fst<W>,
M: MutableFst<W> + Default,
{
let mut result = M::default();
let mut state_map1 = vec![None; fst1.num_states()];
for state in fst1.states() {
let new_state = result.add_state();
state_map1[state as usize] = Some(new_state);
if let Some(weight) = fst1.final_weight(state) {
result.set_final(new_state, weight.clone());
}
}
if let Some(start1) = fst1.start() {
if let Some(new_start) = state_map1[start1 as usize] {
result.set_start(new_start);
}
}
for state in fst1.states() {
if let Some(new_state) = state_map1[state as usize] {
for arc in fst1.arcs(state) {
if let Some(new_nextstate) = state_map1[arc.nextstate as usize] {
result.add_arc(
new_state,
Arc::new(arc.ilabel, arc.olabel, arc.weight.clone(), new_nextstate),
);
}
}
}
}
let mut state_map2 = vec![None; fst2.num_states()];
for state in fst2.states() {
let new_state = result.add_state();
state_map2[state as usize] = Some(new_state);
if let Some(weight) = fst2.final_weight(state) {
result.set_final(new_state, weight.clone());
}
}
for state in fst2.states() {
if let Some(new_state) = state_map2[state as usize] {
for arc in fst2.arcs(state) {
if let Some(new_nextstate) = state_map2[arc.nextstate as usize] {
result.add_arc(
new_state,
Arc::new(arc.ilabel, arc.olabel, arc.weight.clone(), new_nextstate),
);
}
}
}
}
if let Some(start2) = fst2.start() {
if let Some(new_start2) = state_map2[start2 as usize] {
for state in fst1.states() {
if let Some(final_weight) = fst1.final_weight(state) {
if let Some(new_state) = state_map1[state as usize] {
result.remove_final(new_state);
result.add_arc(new_state, Arc::epsilon(final_weight.clone(), new_start2));
}
}
}
}
}
Ok(result)
}
#[cfg(test)]
mod tests {
use super::*;
use crate::prelude::*;
use num_traits::One;
#[test]
fn test_concat_basic() {
let mut fst1 = VectorFst::<TropicalWeight>::new();
let s0 = fst1.add_state();
let s1 = fst1.add_state();
fst1.set_start(s0);
fst1.set_final(s1, TropicalWeight::one());
fst1.add_arc(s0, Arc::new(1, 1, TropicalWeight::new(1.0), s1));
let mut fst2 = VectorFst::<TropicalWeight>::new();
let t0 = fst2.add_state();
let t1 = fst2.add_state();
fst2.set_start(t0);
fst2.set_final(t1, TropicalWeight::one());
fst2.add_arc(t0, Arc::new(2, 2, TropicalWeight::new(2.0), t1));
let concatenated: VectorFst<TropicalWeight> = concat(&fst1, &fst2).unwrap();
assert_eq!(
concatenated.num_states(),
fst1.num_states() + fst2.num_states()
);
assert!(concatenated.start().is_some());
let mut final_count = 0;
for state in concatenated.states() {
if concatenated.is_final(state) {
final_count += 1;
}
}
assert!(final_count > 0);
}
#[test]
fn test_concat_empty() {
let fst1 = VectorFst::<TropicalWeight>::new();
let mut fst2 = VectorFst::<TropicalWeight>::new();
let s0 = fst2.add_state();
fst2.set_start(s0);
fst2.set_final(s0, TropicalWeight::one());
let concatenated: VectorFst<TropicalWeight> = concat(&fst1, &fst2).unwrap();
assert!(concatenated.is_empty() || concatenated.num_arcs_total() == 0);
}
}