use crate::Expression;
use std::collections::{BTreeMap, BTreeSet};
#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct Graph<I: Clone + Ord> {
pub(crate) states: Vec<State<I>>,
pub(crate) initial: BTreeSet<usize>,
}
#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct State<I: Clone + Ord> {
pub(crate) epsilon: BTreeSet<usize>,
pub(crate) non_epsilon: BTreeMap<I, (BTreeSet<usize>, Option<&'static str>)>,
pub(crate) accepting: bool,
}
#[inline]
#[cfg(test)]
pub(crate) fn chain<I: Clone + Ord>(a1: &Graph<I>, a2: &Graph<I>, input: &[I]) -> bool {
let mut s1 = a1.step();
let mut i = input.iter();
if s1.currently_accepting() && a2.accept(i.clone()) {
return true;
}
while let Some(token) = i.next() {
s1.step(token);
if s1.currently_accepting() && a2.accept(i.clone()) {
return true;
}
}
false
}
impl<I: Clone + Ord> IntoIterator for Graph<I> {
type Item = State<I>;
type IntoIter = std::vec::IntoIter<State<I>>;
#[inline(always)]
fn into_iter(self) -> Self::IntoIter {
self.states.into_iter()
}
}
impl<'a, I: Clone + Ord> IntoIterator for &'a Graph<I> {
type Item = &'a State<I>;
type IntoIter = core::slice::Iter<'a, State<I>>;
#[inline(always)]
fn into_iter(self) -> Self::IntoIter {
self.states.iter()
}
}
impl<I: Clone + Ord> Default for Graph<I> {
#[inline(always)]
fn default() -> Self {
Self::void()
}
}
impl<I: Clone + Ord> Graph<I> {
#[inline]
#[must_use]
pub fn void() -> Self {
Self {
states: vec![],
initial: BTreeSet::new(),
}
}
#[inline]
#[must_use]
pub fn empty() -> Self {
Self {
states: vec![State {
epsilon: BTreeSet::new(),
non_epsilon: BTreeMap::new(),
accepting: true,
}],
initial: core::iter::once(0).collect(),
}
}
#[inline]
#[must_use]
#[allow(clippy::arithmetic_side_effects)]
pub fn unit(singleton: I, fn_name: Option<&'static str>) -> Self {
(crate::empty() >> (singleton, fn_name, crate::empty())).evaluate()
}
#[inline]
#[must_use]
#[allow(clippy::missing_panics_doc)]
pub fn take_all_epsilon_transitions(&self, mut queue: Vec<usize>) -> BTreeSet<usize> {
let mut superposition = BTreeSet::<usize>::new();
while let Some(state) = queue.pop() {
for next in &get!(self.states, state).epsilon {
if !superposition.contains(next) {
queue.push(*next);
}
}
let _ = superposition.insert(state);
}
superposition
}
#[inline]
#[must_use]
pub fn step(&self) -> Stepper<'_, I> {
Stepper::new(self)
}
#[inline]
#[must_use]
#[allow(clippy::missing_panics_doc)]
pub fn accept<Iter: IntoIterator>(&self, iter: Iter) -> bool
where
Iter::Item: core::borrow::Borrow<I>,
{
let mut stepper = self.step();
stepper.extend(iter);
stepper.currently_accepting()
}
#[inline(always)]
pub fn reject<Iter: IntoIterator<Item = I>>(&self, iter: Iter) -> bool {
!self.accept(iter)
}
#[must_use]
#[inline(always)]
pub fn size(&self) -> usize {
self.states.len()
}
#[inline]
pub fn fuzz(&self) -> Result<crate::Fuzzer<I>, crate::NeverAccepts> {
crate::Fuzzer::try_from_reversed(self.reverse().compile())
}
#[inline]
#[must_use]
pub fn would_ever_accept(&self) -> bool {
self.states.iter().any(|state| state.accepting) && !self.initial.is_empty()
}
}
impl<I: Clone + Ord + Expression> core::fmt::Display for Graph<I> {
#[inline]
#[allow(clippy::use_debug)]
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
writeln!(f, "Initial states: {:?}", self.initial)?;
for (i, state) in self.states.iter().enumerate() {
write!(f, "State {i} {state}")?;
}
Ok(())
}
}
impl<I: Clone + Ord + Expression> core::fmt::Display for State<I> {
#[inline]
#[allow(clippy::use_debug)]
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
writeln!(
f,
"({}accepting):",
if self.accepting { "" } else { "NOT " }
)?;
if !self.epsilon.is_empty() {
writeln!(f, " epsilon --> {:?}", self.epsilon)?;
}
for (input, &(ref transitions, fn_name)) in &self.non_epsilon {
writeln!(f, " {input:?} --> {transitions:?} >>= {fn_name:?}")?;
}
Ok(())
}
}
impl<I: Clone + Ord> State<I> {
#[inline]
pub fn transition(&self, input: &I) -> Option<&(BTreeSet<usize>, Option<&'static str>)> {
self.non_epsilon.get(input)
}
}
#[cfg(feature = "quickcheck")]
impl<I: Ord + quickcheck::Arbitrary> quickcheck::Arbitrary for Graph<I> {
#[inline]
fn arbitrary(g: &mut quickcheck::Gen) -> Self {
let mut states = quickcheck::Arbitrary::arbitrary(g);
cut_nonsense(&mut states);
let mut initial = BTreeSet::arbitrary(g);
initial.retain(|i| i < &states.len());
Self { states, initial }
}
#[inline]
fn shrink(&self) -> Box<dyn Iterator<Item = Self>> {
Box::new((self.states.clone(), self.initial.clone()).shrink().map(
|(mut states, mut initial)| {
cut_nonsense(&mut states);
initial.retain(|i| i < &states.len());
Self { states, initial }
},
))
}
}
#[cfg(feature = "quickcheck")]
impl<I: Ord + quickcheck::Arbitrary> quickcheck::Arbitrary for State<I> {
#[inline]
fn arbitrary(g: &mut quickcheck::Gen) -> Self {
Self {
epsilon: quickcheck::Arbitrary::arbitrary(g),
non_epsilon: BTreeMap::<I, BTreeSet<usize>>::arbitrary(g)
.into_iter()
.map(|(k, v)| (k, (v, None)))
.collect(),
accepting: quickcheck::Arbitrary::arbitrary(g),
}
}
#[inline]
fn shrink(&self) -> Box<dyn Iterator<Item = Self>> {
Box::new(
(
self.epsilon.clone(),
self.non_epsilon
.iter()
.map(|(token, &(ref map, _))| (token.clone(), map.clone()))
.collect::<BTreeMap<_, _>>(),
self.accepting,
)
.shrink()
.map(|(epsilon, non_epsilon, accepting)| Self {
epsilon,
non_epsilon: non_epsilon
.into_iter()
.map(|(dst, set)| (dst, (set, None)))
.collect(),
accepting,
}),
)
}
}
#[cfg(feature = "quickcheck")]
fn cut_nonsense<I: Clone + Ord>(v: &mut Vec<State<I>>) {
let size = v.len();
for state in v {
state.epsilon.retain(|i| i < &size);
for &mut (ref mut destination, _) in state.non_epsilon.values_mut() {
destination.retain(|index| index < &size);
}
}
}
#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct Stepper<'graph, I: Clone + Ord> {
graph: &'graph Graph<I>,
state: BTreeSet<usize>,
}
impl<'graph, I: Clone + Ord> Stepper<'graph, I> {
#[inline]
#[must_use]
fn new(graph: &'graph Graph<I>) -> Self {
Self {
graph,
state: graph.take_all_epsilon_transitions(graph.initial.iter().copied().collect()),
}
}
#[inline]
fn step(&mut self, token: &I) {
self.state = self.graph.take_all_epsilon_transitions(
self.state
.iter()
.filter_map(|&index| get!(self.graph.states, index).transition(token))
.flat_map(|&(ref map, _)| map.iter().copied())
.collect(),
);
}
#[inline]
fn currently_accepting(&self) -> bool {
for &index in &self.state {
if get!(self.graph.states, index).accepting {
return true;
}
}
false
}
}
impl<I: Clone + Ord, B: core::borrow::Borrow<I>> Extend<B> for Stepper<'_, I> {
#[inline]
fn extend<T: IntoIterator<Item = B>>(&mut self, iter: T) {
for input in iter {
self.step(input.borrow());
}
}
}