use crate::types::*;
pub const TRANSITION_SMOOTHING_LOGPROB: f32 = -13.815510557964274;
#[derive(PartialEq,Clone,Debug)]
pub struct Offset {
pub begin: usize,
pub end: usize,
}
#[derive(Clone,Debug)]
pub struct Match<'a> {
pub text: &'a str,
pub offset: Offset,
pub variants: Option<Vec<VariantResult>>,
pub selected: Option<usize>,
pub prevboundary: Option<usize>,
pub nextboundary: Option<usize>,
pub n: usize
}
impl<'a> Match<'a> {
pub fn new_empty(text: &'a str, offset: Offset) -> Self {
Match {
text,
offset,
variants: None,
selected: None,
prevboundary: None,
nextboundary: None,
n: 0
}
}
pub fn is_empty(&self) -> bool {
self.variants.is_none() || self.variants.as_ref().unwrap().is_empty()
}
pub fn solution(&self) -> Option<&VariantResult> {
if let Some(selected) = self.selected {
self.variants.as_ref().expect("match must have variants when 'selected' is set").get(selected)
} else {
None
}
}
pub fn internal_boundaries(&self, boundaries: &'a [Match<'_>]) -> &'a [Match<'_>] {
let mut begin = None;
let mut end = 0;
for (i, boundary) in boundaries.iter().enumerate() {
if boundary.offset.begin > self.offset.begin && boundary.offset.end < self.offset.end {
if begin.is_none() {
begin = Some(i);
} else {
end = i+1;
}
}
}
if begin.is_none() || begin.unwrap() >= end {
&[]
} else {
&boundaries[begin.unwrap()..end]
}
}
}
#[derive(Clone,Debug)]
pub struct Context<'a> {
pub left: Option<&'a str>,
pub right: Option<&'a str>
}
#[derive(PartialEq,PartialOrd,Clone,Debug)]
pub struct OutputSymbol {
pub vocab_id: VocabId,
pub match_index: usize,
pub variant_index: Option<usize>,
pub boundary_index: usize,
pub symbol: usize,
}
#[derive(Clone,Debug)]
pub struct Sequence {
pub output_symbols: Vec<OutputSymbol>,
pub variant_cost: f32,
pub lm_logprob: f32,
pub perplexity: f64,
}
impl Sequence {
pub fn new(variant_cost: f32) -> Self {
Self {
output_symbols: Vec::new(),
variant_cost,
lm_logprob: 0.0,
perplexity: 0.0,
}
}
}
#[derive(PartialEq,PartialOrd,Copy,Clone,Debug)]
pub enum BoundaryStrength {
None,
Weak,
Normal,
Hard
}
pub fn find_boundaries<'a>(text: &'a str) -> Vec<Match<'a>> {
let mut boundaries = Vec::new();
let mut begin: Option<usize> = None;
for (i,c) in text.char_indices() {
if let Some(b) = begin {
if c.is_alphabetic() {
boundaries.push(Match::new_empty(&text[b..i], Offset {
begin: b,
end: i
}));
begin = None;
}
} else {
if !c.is_alphabetic() {
begin = Some(i);
}
}
}
if let Some(b) = begin {
boundaries.push(Match::new_empty(&text[b..], Offset {
begin: b,
end: text.len()
}));
} else {
boundaries.push(Match::new_empty("", Offset {
begin: text.len(),
end: text.len()
}));
}
boundaries
}
pub fn classify_boundaries(boundaries: &Vec<Match<'_>>) -> Vec<BoundaryStrength> {
let mut strengths = Vec::new();
for (i, boundary) in boundaries.iter().enumerate() {
let strength = if i == boundaries.len() - 1 {
BoundaryStrength::Hard
} else if boundary.text.len() > 1 {
BoundaryStrength::Hard
} else {
match boundary.text {
"'" | "-" | "_" => BoundaryStrength::Weak,
_ => BoundaryStrength::Normal
}
};
strengths.push(strength)
}
strengths
}
pub fn find_match_ngrams<'a>(text: &'a str, boundaries: &[Match<'a>], order: u8, begin: usize, end: Option<usize>) -> Vec<Match<'a>> {
let mut ngrams = Vec::new();
let mut begin = begin;
let end = end.unwrap_or(text.len());
let mut i = 0;
while let Some(boundary) = boundaries.get(i + order as usize - 1) {
if boundary.offset.begin > end {
break;
}
let matchtext = &text[begin..boundary.offset.begin];
if !matchtext.is_empty() && matchtext != " " {
let mut ngram = Match::new_empty(matchtext, Offset {
begin: begin,
end: boundary.offset.begin,
});
ngram.n = order as usize;
ngrams.push(ngram);
}
begin = boundaries.get(i).expect("boundary").offset.end;
i += 1;
}
if begin < end {
let matchtext = &text[begin..end];
if !matchtext.is_empty() && matchtext != " " {
let mut ngram = Match::new_empty(matchtext, Offset {
begin: begin,
end: end,
});
ngram.n = order as usize;
if ngram.internal_boundaries(boundaries).iter().count() == order as usize {
ngrams.push(ngram);
}
}
}
ngrams
}
pub fn redundant_match<'a>(candidate: &Match<'a>, matches: &[Match<'a>]) -> bool {
for refmatch in matches.iter() {
if refmatch.n == 1 {
if refmatch.offset.begin >= candidate.offset.begin && refmatch.offset.end <= candidate.offset.end {
if let Some(variants) = &refmatch.variants {
if variants.is_empty() || variants.get(0).expect("variant").dist_score < 1.0 {
return false; }
} else {
return false; }
}
} else {
break; }
}
true
}