use crate::accum_ftzr::{Ftzr, IterFtzr, LinearFixed};
use crate::as_tokens::AsTokens;
#[cfg(feature = "serde1")]
use serde::{Deserialize, Serialize};
use std::convert::{TryFrom, TryInto};
#[derive(Hash, Copy, Clone, PartialEq, Ord, PartialOrd, Eq, Debug, Default)]
#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]
pub struct NGram<const N: usize>();
#[derive(Hash, Copy, Clone, PartialEq, Ord, PartialOrd, Eq, Debug)]
pub struct NGramIter<'a, T, const N: usize> {
idx: usize,
data: &'a [T],
}
impl<'a, T: 'a, const N: usize> Iterator for NGramIter<'a, T, N>
where
[T; N]: TryFrom<&'a [T]>,
{
type Item = [T; N];
#[inline]
fn next(&mut self) -> Option<Self::Item> {
let jdx = self.idx + N;
if jdx <= self.data.len() {
let ret = Some(
TryInto::try_into(&self.data[self.idx..jdx])
.map_err(|_| ())
.expect("Error converting from slice to [T;N]"),
);
self.idx += 1;
ret
} else {
None
}
}
}
impl<const N: usize> LinearFixed for NGram<N> {
fn chunk_size(&self) -> usize {
N
}
}
impl<'a, D, T: 'a, const N: usize> IterFtzr<&'a D> for NGram<N>
where
D: AsTokens<Token = T> + ?Sized,
[T; N]: TryFrom<&'a [T]>,
{
type TokenGroup = [T; N];
type Iter = NGramIter<'a, T, N>;
fn iterate_features(&self, origin: &'a D) -> Self::Iter {
NGramIter {
idx: 0,
data: origin.as_tokens(),
}
}
}
pub fn n_gram<const N: usize>() -> NGram<N> {
NGram::<N>()
}
pub fn bigram() -> NGram<2> {
NGram::<2>()
}
pub fn trigram() -> NGram<3> {
NGram::<3>()
}
impl<Origin, const N: usize> Ftzr<Origin> for NGram<N>
where
Self: IterFtzr<Origin>,
{
type TokenGroup = <Self as IterFtzr<Origin>>::TokenGroup;
fn push_tokens<Push>(&self, origin: Origin, push: &mut Push)
where
Push: FnMut(Self::TokenGroup),
{
for t in self.iterate_features(origin) {
push(t)
}
}
}