use std::fmt::{Debug, Display};
use std::hash::Hash;
use std::str::FromStr;
use crate::fst_type::{ArcType, WeightType};
use crate::weight::Weight;
pub trait ArcLabel:
Copy + Clone + PartialEq + Eq + PartialOrd + Ord + Hash + Debug + Display + FromStr + 'static
{
fn epsilon() -> Self;
fn no_label() -> Self;
fn to_i64(self) -> Option<i64>;
fn from_i64(key: i64) -> Option<Self>;
}
impl ArcLabel for i32 {
#[inline(always)]
fn epsilon() -> Self {
0
}
#[inline(always)]
fn no_label() -> Self {
-1
}
#[inline(always)]
fn to_i64(self) -> Option<i64> {
Some(self as i64)
}
#[inline(always)]
fn from_i64(key: i64) -> Option<Self> {
Self::try_from(key).ok()
}
}
impl ArcLabel for i64 {
#[inline(always)]
fn epsilon() -> Self {
0
}
#[inline(always)]
fn no_label() -> Self {
-1
}
#[inline(always)]
fn to_i64(self) -> Option<i64> {
Some(self)
}
#[inline(always)]
fn from_i64(key: i64) -> Option<Self> {
Some(key)
}
}
impl ArcLabel for u32 {
#[inline(always)]
fn epsilon() -> Self {
0
}
#[inline(always)]
fn no_label() -> Self {
u32::MAX
}
#[inline(always)]
fn to_i64(self) -> Option<i64> {
Some(self as i64)
}
#[inline(always)]
fn from_i64(key: i64) -> Option<Self> {
Self::try_from(key).ok()
}
}
impl ArcLabel for usize {
#[inline(always)]
fn epsilon() -> Self {
0
}
#[inline(always)]
fn no_label() -> Self {
usize::MAX
}
#[inline(always)]
fn to_i64(self) -> Option<i64> {
i64::try_from(self).ok()
}
#[inline(always)]
fn from_i64(key: i64) -> Option<Self> {
Self::try_from(key).ok()
}
}
pub trait ArcStateId: Copy + PartialEq + Eq + PartialOrd + Ord + Hash + Debug {
fn no_state() -> Self;
fn as_usize(&self) -> usize;
fn from_usize(n: usize) -> Self;
}
impl ArcStateId for i32 {
#[inline(always)]
fn no_state() -> Self {
-1
}
#[inline(always)]
fn as_usize(&self) -> usize {
debug_assert!(*self >= 0, "Attempted to use negative state ID as index");
*self as usize
}
#[inline(always)]
fn from_usize(n: usize) -> Self {
n as i32
}
}
impl ArcStateId for i8 {
#[inline(always)]
fn no_state() -> Self {
-1
}
#[inline(always)]
fn as_usize(&self) -> usize {
debug_assert!(*self >= 0, "Attempted to use negative state ID as index");
*self as usize
}
#[inline(always)]
fn from_usize(n: usize) -> Self {
n as i8
}
}
impl ArcStateId for u32 {
#[inline(always)]
fn no_state() -> Self {
u32::MAX
}
#[inline(always)]
fn as_usize(&self) -> usize {
*self as usize
}
#[inline(always)]
fn from_usize(n: usize) -> Self {
n as u32
}
}
impl ArcStateId for usize {
#[inline(always)]
fn no_state() -> Self {
usize::MAX
}
#[inline(always)]
fn as_usize(&self) -> usize {
*self
}
#[inline(always)]
fn from_usize(n: usize) -> Self {
n
}
}
pub trait Arc: Clone + PartialEq + Debug {
type Weight: Weight;
type Label: ArcLabel;
type StateId: ArcStateId;
type Reverse: Arc<
Label = Self::Label,
StateId = Self::StateId,
Weight = <Self::Weight as Weight>::ReverseWeight,
>;
fn new(
ilabel: Self::Label,
olabel: Self::Label,
weight: Self::Weight,
nextstate: Self::StateId,
) -> Self;
fn ilabel(&self) -> Self::Label;
fn olabel(&self) -> Self::Label;
fn weight(&self) -> &Self::Weight;
fn nextstate(&self) -> Self::StateId;
fn type_name() -> ArcType;
}
#[repr(C)]
#[derive(Debug, Clone, Copy, PartialEq, PartialOrd)]
pub struct ArcTpl<W, L = i32, S = i32> {
pub ilabel: L,
pub olabel: L,
pub weight: W,
pub nextstate: S,
}
impl<W, L, S> Arc for ArcTpl<W, L, S>
where
W: Weight,
L: ArcLabel,
S: ArcStateId,
{
type Weight = W;
type Label = L;
type StateId = S;
type Reverse = ArcTpl<W::ReverseWeight, L, S>;
#[inline(always)]
fn new(ilabel: L, olabel: L, weight: W, nextstate: S) -> Self {
Self {
ilabel,
olabel,
weight,
nextstate,
}
}
#[inline(always)]
fn ilabel(&self) -> Self::Label {
self.ilabel
}
#[inline(always)]
fn olabel(&self) -> Self::Label {
self.olabel
}
#[inline(always)]
fn weight(&self) -> &Self::Weight {
&self.weight
}
#[inline(always)]
fn nextstate(&self) -> Self::StateId {
self.nextstate
}
#[inline]
fn type_name() -> ArcType {
let w_type = W::type_name();
if w_type == WeightType::TROPICAL {
ArcType::STANDARD
} else {
ArcType::new_dynamic(w_type.to_string())
}
}
}
pub type StdArc = ArcTpl<crate::float_weight::TropicalWeight>;
pub type Std64Arc = ArcTpl<crate::float_weight::TropicalWeight64>;
pub type LogArc = ArcTpl<crate::float_weight::LogWeight>;
pub type Log64Arc = ArcTpl<crate::float_weight::Log64Weight>;
pub type RealArc = ArcTpl<crate::float_weight::RealWeight>;
pub type Real64Arc = ArcTpl<crate::float_weight::Real64Weight>;
pub type MinMaxArc = ArcTpl<crate::float_weight::MinMaxWeight>;
pub type MinMax64Arc = ArcTpl<crate::float_weight::MinMaxWeight64>;
pub type SignedLogArc = ArcTpl<crate::signed_log_weight::SignedLogWeight>;
pub type SignedLog64Arc = ArcTpl<crate::signed_log_weight::SignedLog64Weight>;
macro_rules! delegate_arc {
($weight:ty, $base:ty, $reverse:ty, $type_name:expr) => {
type Weight = $weight;
type Label = <$base as Arc>::Label;
type StateId = <$base as Arc>::StateId;
type Reverse = $reverse;
#[inline(always)]
fn new(
ilabel: Self::Label,
olabel: Self::Label,
weight: Self::Weight,
nextstate: Self::StateId,
) -> Self {
Self {
inner: ArcTpl::new(ilabel, olabel, weight, nextstate),
}
}
#[inline(always)]
fn ilabel(&self) -> Self::Label {
self.inner.ilabel
}
#[inline(always)]
fn olabel(&self) -> Self::Label {
self.inner.olabel
}
#[inline(always)]
fn weight(&self) -> &Self::Weight {
&self.inner.weight
}
#[inline(always)]
fn nextstate(&self) -> Self::StateId {
self.inner.nextstate
}
#[inline]
fn type_name() -> ArcType {
let base = <$base as Arc>::type_name();
ArcType::new_dynamic($type_name(base.as_str()))
}
};
}
#[derive(Debug, Clone, PartialEq)]
pub struct ReverseArc<A: Arc> {
inner: ArcTpl<<A::Weight as Weight>::ReverseWeight, A::Label, A::StateId>,
}
impl<A: Arc> Arc for ReverseArc<A>
where
<A::Weight as Weight>::ReverseWeight: Weight<ReverseWeight = A::Weight>,
{
delegate_arc!(
<A::Weight as Weight>::ReverseWeight,
A,
A,
|base: &str| format!("reverse_{base}")
);
}
#[derive(Debug, Clone, PartialEq)]
pub struct PowerArc<A: Arc, const N: usize> {
inner: ArcTpl<crate::weights::power_weight::PowerWeight<A::Weight, N>, A::Label, A::StateId>,
}
impl<A: Arc, const N: usize> Arc for PowerArc<A, N> {
delegate_arc!(
crate::weights::power_weight::PowerWeight<A::Weight, N>,
A,
PowerArc<A::Reverse, N>,
|base: &str| format!("{base}_^{N}")
);
}
#[derive(Debug, Clone, PartialEq)]
pub struct GallicArc<A: Arc, G: crate::weights::string_weight::GallicTypeMarker> {
inner: ArcTpl<
crate::weights::string_weight::GallicWeight<A::Label, A::Weight, G>,
A::Label,
A::StateId,
>,
}
impl<A: Arc, G: crate::weights::string_weight::GallicTypeMarker> Arc for GallicArc<A, G> {
delegate_arc!(
crate::weights::string_weight::GallicWeight<A::Label, A::Weight, G>,
A,
GallicArc<A::Reverse, G::Reverse>,
|base: &str| format!("{}{base}", G::ARC_PREFIX)
);
}
pub type ErrorArc = ArcTpl<crate::weights::error_weight::ErrorWeight>;
#[cfg(test)]
mod tests {
use super::*;
use crate::weights::float_weight::{LogWeight, TropicalWeight};
#[test]
fn a_reversed_wrapper_arc_keeps_its_type_name() {
use crate::weights::string_weight::{GallicLeft, GallicRight};
type Left = GallicArc<StdArc, GallicLeft>;
assert_eq!(Left::type_name().as_str(), "left_gallic_standard");
assert_eq!(
<Left as Arc>::Reverse::type_name().as_str(),
"right_gallic_standard",
"reversing a left-gallic arc gives a right-gallic one, not a bare weight"
);
type Right = GallicArc<StdArc, GallicRight>;
assert_eq!(
<Right as Arc>::Reverse::type_name().as_str(),
"left_gallic_standard"
);
assert_eq!(
<PowerArc<StdArc, 3> as Arc>::Reverse::type_name().as_str(),
"standard_^3"
);
assert_eq!(
<ReverseArc<StdArc> as Arc>::Reverse::type_name().as_str(),
"standard",
"reversing twice is the identity"
);
}
#[test]
fn arc_type_names_match_openfst() {
assert_eq!(StdArc::type_name().as_str(), "standard");
assert_eq!(LogArc::type_name().as_str(), "log");
assert_eq!(Log64Arc::type_name().as_str(), "log64");
assert_eq!(RealArc::type_name().as_str(), "real");
assert_eq!(MinMaxArc::type_name().as_str(), "minmax");
assert_eq!(
SignedLogArc::type_name().as_str(),
"signed_log_tropical_log"
);
assert_eq!(
SignedLog64Arc::type_name().as_str(),
"signed_log_tropical_log64"
);
}
#[test]
fn the_tropical_arc_is_called_standard() {
assert_eq!(TropicalWeight::type_name().as_str(), "tropical");
assert_eq!(StdArc::type_name().as_str(), "standard");
assert_eq!(
LogArc::type_name().as_str(),
LogWeight::type_name().as_str()
);
}
#[test]
fn the_wrapper_arcs_decorate_the_base_name() {
assert_eq!(
ReverseArc::<StdArc>::type_name().as_str(),
"reverse_standard"
);
assert_eq!(PowerArc::<StdArc, 3>::type_name().as_str(), "standard_^3");
assert_eq!(
GallicArc::<StdArc, crate::weights::string_weight::GallicLeft>::type_name().as_str(),
"left_gallic_standard"
);
assert_eq!(
GallicArc::<StdArc, crate::weights::string_weight::GallicMin>::type_name().as_str(),
"min_gallic_standard"
);
}
#[test]
fn an_arc_carries_its_four_fields() {
let arc = StdArc::new(1, 2, TropicalWeight(3.5), 4);
assert_eq!(arc.ilabel(), 1);
assert_eq!(arc.olabel(), 2);
assert_eq!(arc.weight(), &TropicalWeight(3.5));
assert_eq!(arc.nextstate(), 4);
}
#[test]
fn a_wrapper_arc_carries_its_fields_too() {
let arc = ReverseArc::<StdArc>::new(1, 2, TropicalWeight(3.5), 4);
assert_eq!(arc.ilabel(), 1);
assert_eq!(arc.olabel(), 2);
assert_eq!(arc.nextstate(), 4);
}
#[test]
fn the_special_labels_are_what_openfst_uses() {
assert_eq!(<i32 as ArcLabel>::epsilon(), 0);
assert_eq!(<i32 as ArcLabel>::no_label(), -1);
assert_eq!(<i64 as ArcLabel>::epsilon(), 0);
assert_eq!(<i64 as ArcLabel>::no_label(), -1);
assert_eq!(<u32 as ArcLabel>::epsilon(), 0);
assert_ne!(<u32 as ArcLabel>::no_label(), 0);
}
#[test]
fn the_standard_arc_is_four_words() {
assert_eq!(size_of::<StdArc>(), 16);
assert_eq!(align_of::<StdArc>(), 4);
}
}