1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
use crate::fst_properties::mutable_properties::project_properties;
use crate::fst_properties::FstProperties;
use crate::fst_traits::MutableFst;
use crate::semirings::Semiring;

#[derive(Debug, Clone, PartialEq, PartialOrd, Copy)]
/// Different types of labels projection in a FST.
pub enum ProjectType {
    /// Input projection : output labels are replaced with input ones.
    ProjectInput,
    /// Output projection : input labels are replaced with output ones.
    ProjectOutput,
}

/// This operation projects an FST onto its domain or range by either copying
/// each transition input label to its output label or vice versa.
/// # Example 1
///
/// ## Project input
/// ```
/// # #[macro_use] extern crate rustfst;
/// # use anyhow::Result;
/// # use rustfst::utils::{acceptor, transducer};
/// # use rustfst::semirings::{Semiring, IntegerWeight};
/// # use rustfst::fst_impls::VectorFst;
/// # use rustfst::algorithms::{project, ProjectType};
/// # fn main() -> Result<()> {
/// let mut fst : VectorFst<IntegerWeight> = fst![2 => 3];
/// project(&mut fst, ProjectType::ProjectInput);
/// assert_eq!(fst, fst![2]);
/// # Ok(())
/// # }
/// ```
///
/// ## Project output
///
/// ```rust
/// # #[macro_use] extern crate rustfst;
/// # use anyhow::Result;
/// # use rustfst::utils::{acceptor, transducer};
/// # use rustfst::semirings::{Semiring, IntegerWeight};
/// # use rustfst::fst_impls::VectorFst;
/// # use rustfst::algorithms::{project, ProjectType};
/// # fn main() -> Result<()> {
/// let mut fst : VectorFst<IntegerWeight> = fst![2 => 3];
/// project(&mut fst, ProjectType::ProjectOutput);
/// assert_eq!(fst, fst![3]);
/// # Ok(())
/// # }
/// ```
///
/// # Example 2
///
/// ## Input
///
/// ![project_in](https://raw.githubusercontent.com/Garvys/rustfst-images-doc/master/images/project_in.svg?sanitize=true)
///
/// ## Project input
///
/// ![project_out_project-input](https://raw.githubusercontent.com/Garvys/rustfst-images-doc/master/images/project_out_project-input.svg?sanitize=true)
///
/// ## Project output
///
/// ![project_out_project-input](https://raw.githubusercontent.com/Garvys/rustfst-images-doc/master/images/project_out_project-output.svg?sanitize=true)
pub fn project<W: Semiring, F: MutableFst<W>>(fst: &mut F, project_type: ProjectType) {
    let props = fst.properties();
    match project_type {
        ProjectType::ProjectInput => {
            for state in fst.states_range() {
                let mut it_trs = unsafe { fst.tr_iter_unchecked_mut(state) };
                for idx_tr in 0..it_trs.len() {
                    let tr = unsafe { it_trs.get_unchecked(idx_tr) };
                    let ilabel = tr.ilabel;
                    unsafe { it_trs.set_olabel_unchecked(idx_tr, ilabel) }
                }
            }
        }
        ProjectType::ProjectOutput => {
            for state in fst.states_range() {
                let mut it_trs = unsafe { fst.tr_iter_unchecked_mut(state) };
                for idx_tr in 0..it_trs.len() {
                    let tr = unsafe { it_trs.get_unchecked(idx_tr) };
                    let olabel = tr.olabel;
                    unsafe { it_trs.set_ilabel_unchecked(idx_tr, olabel) }
                }
            }
        }
    };
    fst.set_properties_with_mask(
        project_properties(props, project_type),
        FstProperties::all_properties(),
    );
}

#[cfg(test)]
mod tests {
    use proptest::prelude::*;

    use crate::fst_properties::FstProperties;
    use crate::fst_traits::CoreFst;
    use crate::prelude::*;

    use super::*;

    proptest! {
        #[test]
        fn test_project_input_proptest(mut fst in any::<VectorFst<TropicalWeight>>()) {
            project(&mut fst, ProjectType::ProjectInput);
            prop_assert!(fst.properties().intersects(FstProperties::ACCEPTOR));
        }
    }

    proptest! {
        #[test]
        fn test_project_output_proptest(mut fst in any::<VectorFst<TropicalWeight>>()) {
            project(&mut fst, ProjectType::ProjectOutput);
            prop_assert!(fst.properties().intersects(FstProperties::ACCEPTOR));
        }
    }
}