use crate::arc::{Arc, ArcLabel};
use crate::data_structures::compact_set::{CompactSet, CompactSetKey};
pub trait ArcFilter<A: Arc> {
fn call(&self, arc: &A) -> bool;
}
#[derive(Debug, Clone, Default)]
pub struct AnyArcFilter;
impl<A: Arc> ArcFilter<A> for AnyArcFilter {
#[inline(always)]
fn call(&self, _arc: &A) -> bool {
true
}
}
#[derive(Debug, Clone, Default)]
pub struct EpsilonArcFilter;
impl<A: Arc> ArcFilter<A> for EpsilonArcFilter {
#[inline(always)]
fn call(&self, arc: &A) -> bool {
arc.ilabel() == <A::Label as ArcLabel>::epsilon()
&& arc.olabel() == <A::Label as ArcLabel>::epsilon()
}
}
#[derive(Debug, Clone, Default)]
pub struct InputEpsilonArcFilter;
impl<A: Arc> ArcFilter<A> for InputEpsilonArcFilter {
#[inline(always)]
fn call(&self, arc: &A) -> bool {
arc.ilabel() == <A::Label as ArcLabel>::epsilon()
}
}
#[derive(Debug, Clone, Default)]
pub struct OutputEpsilonArcFilter;
impl<A: Arc> ArcFilter<A> for OutputEpsilonArcFilter {
#[inline(always)]
fn call(&self, arc: &A) -> bool {
arc.olabel() == <A::Label as ArcLabel>::epsilon()
}
}
#[derive(Debug, Clone)]
pub struct LabelArcFilter<L> {
label: L,
match_input: bool,
keep_match: bool,
}
impl<L> LabelArcFilter<L> {
pub fn new(label: L) -> Self {
Self {
label,
match_input: true,
keep_match: true,
}
}
pub fn with_options(label: L, match_input: bool, keep_match: bool) -> Self {
Self {
label,
match_input,
keep_match,
}
}
}
impl<A: Arc> ArcFilter<A> for LabelArcFilter<A::Label> {
#[inline]
fn call(&self, arc: &A) -> bool {
let match_found = if self.match_input {
arc.ilabel() == self.label
} else {
arc.olabel() == self.label
};
if self.keep_match {
match_found
} else {
!match_found
}
}
}
#[derive(Debug, Clone)]
pub struct MultiLabelArcFilter<L: CompactSetKey> {
labels: CompactSet<L>,
match_input: bool,
keep_match: bool,
}
impl<L: CompactSetKey> Default for MultiLabelArcFilter<L> {
fn default() -> Self {
Self::new()
}
}
impl<L: CompactSetKey> MultiLabelArcFilter<L> {
pub fn new() -> Self {
Self {
labels: CompactSet::new(),
match_input: true,
keep_match: true,
}
}
pub fn with_options(match_input: bool, keep_match: bool) -> Self {
Self {
labels: CompactSet::new(),
match_input,
keep_match,
}
}
pub fn add_label(&mut self, label: L) {
self.labels.insert(label);
}
}
impl<A: Arc> ArcFilter<A> for MultiLabelArcFilter<A::Label>
where
A::Label: CompactSetKey,
{
#[inline]
fn call(&self, arc: &A) -> bool {
let target_label = if self.match_input {
arc.ilabel()
} else {
arc.olabel()
};
let match_found = self.labels.is_member(target_label);
if self.keep_match {
match_found
} else {
!match_found
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::arc::StdArc;
use crate::weight::Weight;
use crate::weights::float_weight::TropicalWeight;
fn arc(ilabel: i32, olabel: i32) -> StdArc {
StdArc::new(ilabel, olabel, TropicalWeight::one(), 0)
}
#[test]
fn any_accepts_everything() {
assert!(AnyArcFilter.call(&arc(0, 0)));
assert!(AnyArcFilter.call(&arc(1, 2)));
}
#[test]
fn the_epsilon_filters_look_at_the_side_they_name() {
assert!(EpsilonArcFilter.call(&arc(0, 0)));
assert!(!EpsilonArcFilter.call(&arc(0, 1)));
assert!(!EpsilonArcFilter.call(&arc(1, 0)));
assert!(InputEpsilonArcFilter.call(&arc(0, 1)));
assert!(InputEpsilonArcFilter.call(&arc(0, 0)));
assert!(!InputEpsilonArcFilter.call(&arc(1, 0)));
assert!(OutputEpsilonArcFilter.call(&arc(1, 0)));
assert!(OutputEpsilonArcFilter.call(&arc(0, 0)));
assert!(!OutputEpsilonArcFilter.call(&arc(0, 1)));
}
#[test]
fn the_epsilon_filters_do_not_assume_an_i32_label() {
type WideArc = crate::arc::ArcTpl<TropicalWeight, i64, i32>;
let epsilon: WideArc = Arc::new(0, 0, TropicalWeight::one(), 0);
let labelled: WideArc = Arc::new(1, 1, TropicalWeight::one(), 0);
assert!(EpsilonArcFilter.call(&epsilon));
assert!(!EpsilonArcFilter.call(&labelled));
}
#[test]
fn a_label_filter_can_match_either_side_and_invert() {
let input_keep = LabelArcFilter::new(5);
assert!(input_keep.call(&arc(5, 9)));
assert!(!input_keep.call(&arc(9, 5)));
let output_keep = LabelArcFilter::with_options(5, false, true);
assert!(output_keep.call(&arc(9, 5)));
assert!(!output_keep.call(&arc(5, 9)));
let input_drop = LabelArcFilter::with_options(5, true, false);
assert!(!input_drop.call(&arc(5, 9)));
assert!(input_drop.call(&arc(9, 5)));
}
#[test]
fn a_multi_label_filter_matches_any_of_its_labels() {
let mut filter = MultiLabelArcFilter::new();
filter.add_label(1);
filter.add_label(3);
assert!(filter.call(&arc(1, 0)));
assert!(filter.call(&arc(3, 0)));
assert!(!filter.call(&arc(2, 0)));
assert!(!filter.call(&arc(0, 1)), "it looks at the input side");
}
#[test]
fn a_multi_label_filter_can_invert_and_match_the_output_side() {
let mut filter = MultiLabelArcFilter::with_options(false, false);
filter.add_label(1);
assert!(
!filter.call(&arc(0, 1)),
"1 matches, and matches are dropped"
);
assert!(filter.call(&arc(1, 2)), "the output label 2 does not match");
}
#[test]
fn an_empty_multi_label_filter_matches_nothing() {
let filter: MultiLabelArcFilter<i32> = MultiLabelArcFilter::new();
assert!(!filter.call(&arc(0, 0)));
assert!(!filter.call(&arc(7, 7)));
}
}