use super::nfa::{self, NfaState, NfaStateId};
use super::small_set::{SmallSet, SmallSet256};
use super::state::StateAttributesBuilder;
use super::{Automaton, NondeterministicAutomaton, State as DfaStateId, StateAttributes, StateTable, TransitionLabel};
use crate::debug;
use crate::query::error::CompilerError;
use smallvec::{smallvec, SmallVec};
use vector_map::VecMap;
pub(super) fn minimize(nfa: NondeterministicAutomaton) -> Result<Automaton, CompilerError> {
    let minimizer = Minimizer {
        nfa,
        superstates: VecMap::new(),
        checkpoints: VecMap::new(),
        active_superstates: smallvec![],
        dfa_states: vec![],
        accepting: SmallSet256::default(),
    };
    minimizer.run()
}
pub(super) struct Minimizer<'q> {
    nfa: NondeterministicAutomaton<'q>,
    superstates: VecMap<SmallSet256, DfaStateId>,
    checkpoints: VecMap<SmallSet256, NfaStateId>,
    active_superstates: SmallVec<[SmallSet256; 2]>,
    dfa_states: Vec<StateTable<'q>>,
    accepting: SmallSet256,
}
#[derive(Debug)]
struct SuperstateTransitionTable<'q> {
    labelled: VecMap<TransitionLabel<'q>, SmallSet256>,
    wildcard: SmallSet256,
}
impl<'q> Minimizer<'q> {
    fn run(mut self) -> Result<Automaton<'q>, CompilerError> {
        self.dfa_states.push(StateTable {
            transitions: smallvec![],
            fallback_state: Self::rejecting_state(),
            attributes: StateAttributesBuilder::new().rejecting().into(),
        });
        self.superstates.insert(SmallSet256::default(), Self::rejecting_state());
        let initial_superstate = [0].into();
        self.activate_if_new(initial_superstate)?;
        while let Some(superstate) = self.active_superstates.pop() {
            self.process_superstate(superstate)?;
        }
        Ok(Automaton {
            states: self.dfa_states,
        })
    }
    fn rejecting_state() -> DfaStateId {
        DfaStateId(0)
    }
    fn activate_if_new(&mut self, superstate: SmallSet256) -> Result<(), CompilerError> {
        if !self.superstates.contains_key(&superstate) {
            let identifier = self
                .superstates
                .len()
                .try_into()
                .map(DfaStateId)
                .map_err(|err| CompilerError::QueryTooComplex(Some(err)))?;
            self.superstates.insert(superstate, identifier);
            self.active_superstates.push(superstate);
            self.dfa_states.push(StateTable::default());
            debug!("New superstate created: {superstate:?} {identifier}");
            if superstate.contains(self.nfa.accepting_state().0) {
                self.accepting.insert(identifier.0);
            }
        }
        Ok(())
    }
    fn process_superstate(&mut self, current_superstate: SmallSet256) -> Result<(), CompilerError> {
        let current_checkpoint = self.determine_checkpoint(current_superstate);
        debug!("Expanding superstate: {current_superstate:?}, last checkpoint is {current_checkpoint:?}");
        let mut transitions = self.process_nfa_transitions(current_superstate, current_checkpoint)?;
        debug!("Raw transitions: {:?}", transitions);
        self.normalize_superstate_transitions(&mut transitions, current_checkpoint)?;
        debug!("Normalized transitions: {:?}", transitions);
        let translated_transitions: SmallVec<_> = transitions
            .labelled
            .into_iter()
            .map(|(label, state)| (label, self.superstates[&state]))
            .collect();
        debug!("Translated transitions: {translated_transitions:?}");
        let id = self.superstates[¤t_superstate];
        let fallback_state = self.superstates[&transitions.wildcard];
        let attributes = self.build_attributes(id, &translated_transitions, fallback_state);
        let table = &mut self.dfa_states[id.0 as usize];
        table.transitions = translated_transitions;
        table.fallback_state = fallback_state;
        table.attributes = attributes;
        Ok(())
    }
    fn build_attributes(
        &self,
        id: DfaStateId,
        transitions: &[(TransitionLabel, DfaStateId)],
        fallback: DfaStateId,
    ) -> StateAttributes {
        let mut attrs = StateAttributesBuilder::new();
        if self.accepting.contains(id.0) {
            debug!("{id} is accepting");
            attrs = attrs.accepting();
        }
        if id == Self::rejecting_state() {
            debug!("{id} is rejecting");
            attrs = attrs.rejecting();
        }
        if transitions.len() == 1 && fallback == Self::rejecting_state() {
            debug!("{id} is unitary");
            attrs = attrs.unitary();
        }
        if self.accepting.contains(fallback.0) || transitions.iter().any(|(_, s)| self.accepting.contains(s.0)) {
            debug!("{id} has transitions to accepting");
            attrs = attrs.transitions_to_accepting();
        }
        attrs.into()
    }
    fn determine_checkpoint(&mut self, superstate: SmallSet256) -> Option<NfaStateId> {
        if let Some(nfa_state) = self.as_checkpoint(superstate) {
            self.checkpoints.insert(superstate, nfa_state);
            Some(nfa_state)
        } else {
            self.checkpoints.get(&superstate).copied()
        }
    }
    fn as_checkpoint(&self, superstate: SmallSet256) -> Option<NfaStateId> {
        if let Some(single_state) = superstate.singleton().map(NfaStateId) {
            if matches!(self.nfa[single_state], NfaState::Recursive(_)) {
                return Some(single_state);
            }
        }
        None
    }
    fn process_nfa_transitions(
        &self,
        current_superstate: SmallSet256,
        current_checkpoint: Option<NfaStateId>,
    ) -> Result<SuperstateTransitionTable<'q>, CompilerError> {
        let mut wildcard_targets = current_superstate
            .iter()
            .map(NfaStateId)
            .filter_map(|id| match self.nfa[id] {
                NfaState::Recursive(nfa::Transition::Wildcard) | NfaState::Direct(nfa::Transition::Wildcard) => {
                    Some(id.next().map(|x| x.0))
                }
                _ => None,
            })
            .collect::<Result<SmallSet256, _>>()?;
        if let Some(checkpoint) = current_checkpoint {
            wildcard_targets.insert(checkpoint.0);
        }
        debug!("Wildcard target: {wildcard_targets:?}");
        let mut transitions = SuperstateTransitionTable {
            labelled: VecMap::new(),
            wildcard: wildcard_targets,
        };
        for nfa_state in current_superstate.iter().map(NfaStateId) {
            match self.nfa[nfa_state] {
                NfaState::Direct(nfa::Transition::Labelled(label))
                | NfaState::Recursive(nfa::Transition::Labelled(label)) => {
                    debug!("Considering transition {nfa_state} --{}-> {}", label, nfa_state.next()?,);
                    if let Some(target) = transitions.labelled.get_mut(&label) {
                        target.insert(nfa_state.next()?.0);
                    } else {
                        let mut new_set = transitions.wildcard;
                        new_set.insert(nfa_state.next()?.0);
                        transitions.labelled.insert(label, new_set);
                    }
                }
                NfaState::Direct(nfa::Transition::Wildcard)
                | NfaState::Recursive(nfa::Transition::Wildcard)
                | NfaState::Accepting => (),
            }
        }
        Ok(transitions)
    }
    fn normalize_superstate_transitions(
        &mut self,
        transitions: &mut SuperstateTransitionTable,
        current_checkpoint: Option<NfaStateId>,
    ) -> Result<(), CompilerError> {
        fn normalize_one(
            this: &mut Minimizer,
            state: &mut SmallSet256,
            current_checkpoint: Option<NfaStateId>,
        ) -> Result<(), CompilerError> {
            if let Some(checkpoint) = current_checkpoint {
                state.insert(checkpoint.0);
            }
            this.normalize(state);
            this.activate_if_new(*state)?;
            if let Some(checkpoint) = current_checkpoint {
                this.checkpoints.insert(*state, checkpoint);
            }
            Ok(())
        }
        normalize_one(self, &mut transitions.wildcard, current_checkpoint)?;
        for (_, state) in &mut transitions.labelled {
            normalize_one(self, state, current_checkpoint)?;
        }
        Ok(())
    }
    fn normalize(&self, superstate: &mut SmallSet256) {
        let furthest_checkpoint = superstate
            .iter()
            .map(NfaStateId)
            .filter(|&x| matches!(self.nfa[x], NfaState::Recursive(_)))
            .max();
        if let Some(cutoff) = furthest_checkpoint {
            superstate.remove_all_before(cutoff.0);
        }
    }
}
#[cfg(test)]
mod tests {
    use super::super::*;
    use super::*;
    use nfa::NfaState;
    use pretty_assertions::assert_eq;
    use smallvec::smallvec;
    #[test]
    fn empty_query() {
        let nfa = NondeterministicAutomaton {
            ordered_states: vec![NfaState::Accepting],
        };
        let result = minimize(nfa).unwrap();
        let expected = Automaton {
            states: vec![
                StateTable {
                    transitions: smallvec![],
                    fallback_state: State(0),
                    attributes: StateAttributes::REJECTING,
                },
                StateTable {
                    transitions: smallvec![],
                    fallback_state: State(0),
                    attributes: StateAttributes::ACCEPTING,
                },
            ],
        };
        assert_eq!(result, expected);
    }
    #[test]
    fn simple_wildcard() {
        let nfa = NondeterministicAutomaton {
            ordered_states: vec![NfaState::Direct(nfa::Transition::Wildcard), NfaState::Accepting],
        };
        let result = minimize(nfa).unwrap();
        let expected = Automaton {
            states: vec![
                StateTable {
                    transitions: smallvec![],
                    fallback_state: State(0),
                    attributes: StateAttributes::REJECTING,
                },
                StateTable {
                    transitions: smallvec![],
                    fallback_state: State(2),
                    attributes: StateAttributes::TRANSITIONS_TO_ACCEPTING,
                },
                StateTable {
                    transitions: smallvec![],
                    fallback_state: State(0),
                    attributes: StateAttributes::ACCEPTING,
                },
            ],
        };
        assert_eq!(result, expected);
    }
    #[test]
    fn simple_nonnegative_indexed() {
        let label = TransitionLabel::ArrayIndex(0.try_into().unwrap());
        let nfa = NondeterministicAutomaton {
            ordered_states: vec![NfaState::Direct(nfa::Transition::Labelled(label)), NfaState::Accepting],
        };
        let result = minimize(nfa).unwrap();
        let expected = Automaton {
            states: vec![
                StateTable {
                    transitions: smallvec![],
                    fallback_state: State(0),
                    attributes: StateAttributes::REJECTING,
                },
                StateTable {
                    transitions: smallvec![(label, State(2))],
                    fallback_state: State(0),
                    attributes: StateAttributes::UNITARY | StateAttributes::TRANSITIONS_TO_ACCEPTING,
                },
                StateTable {
                    transitions: smallvec![],
                    fallback_state: State(0),
                    attributes: StateAttributes::ACCEPTING,
                },
            ],
        };
        assert_eq!(result, expected);
    }
    #[test]
    fn simple_descendant_wildcard() {
        let nfa = NondeterministicAutomaton {
            ordered_states: vec![NfaState::Recursive(nfa::Transition::Wildcard), NfaState::Accepting],
        };
        let result = minimize(nfa).unwrap();
        let expected = Automaton {
            states: vec![
                StateTable {
                    transitions: smallvec![],
                    fallback_state: State(0),
                    attributes: StateAttributes::REJECTING,
                },
                StateTable {
                    transitions: smallvec![],
                    fallback_state: State(2),
                    attributes: StateAttributes::TRANSITIONS_TO_ACCEPTING,
                },
                StateTable {
                    transitions: smallvec![],
                    fallback_state: State(2),
                    attributes: StateAttributes::ACCEPTING | StateAttributes::TRANSITIONS_TO_ACCEPTING,
                },
            ],
        };
        assert_eq!(result, expected);
    }
    #[test]
    fn interstitial_descendant_wildcard() {
        let label_a = JsonString::new("a");
        let label_a = (&label_a).into();
        let label_b = JsonString::new("b");
        let label_b = (&label_b).into();
        let nfa = NondeterministicAutomaton {
            ordered_states: vec![
                NfaState::Recursive(nfa::Transition::Labelled(label_a)),
                NfaState::Direct(nfa::Transition::Labelled(label_b)),
                NfaState::Recursive(nfa::Transition::Wildcard),
                NfaState::Direct(nfa::Transition::Labelled(label_a)),
                NfaState::Recursive(nfa::Transition::Labelled(label_b)),
                NfaState::Accepting,
            ],
        };
        let result = minimize(nfa).unwrap();
        let expected = Automaton {
            states: vec![
                StateTable {
                    transitions: smallvec![],
                    fallback_state: State(0),
                    attributes: StateAttributes::REJECTING,
                },
                StateTable {
                    transitions: smallvec![(label_a, State(2))],
                    fallback_state: State(1),
                    attributes: StateAttributes::EMPTY,
                },
                StateTable {
                    transitions: smallvec![(label_a, State(2)), (label_b, State(3))],
                    fallback_state: State(1),
                    attributes: StateAttributes::EMPTY,
                },
                StateTable {
                    transitions: smallvec![],
                    fallback_state: State(4),
                    attributes: StateAttributes::EMPTY,
                },
                StateTable {
                    transitions: smallvec![(label_a, State(5))],
                    fallback_state: State(4),
                    attributes: StateAttributes::EMPTY,
                },
                StateTable {
                    transitions: smallvec![(label_b, State(6))],
                    fallback_state: State(5),
                    attributes: StateAttributes::TRANSITIONS_TO_ACCEPTING,
                },
                StateTable {
                    transitions: smallvec![(label_b, State(6))],
                    fallback_state: State(5),
                    attributes: StateAttributes::ACCEPTING | StateAttributes::TRANSITIONS_TO_ACCEPTING,
                },
            ],
        };
        assert_eq!(result, expected);
    }
    #[test]
    fn interstitial_nondescendant_wildcard() {
        let label_a = JsonString::new("a");
        let label_a = (&label_a).into();
        let label_b = JsonString::new("b");
        let label_b = (&label_b).into();
        let nfa = NondeterministicAutomaton {
            ordered_states: vec![
                NfaState::Recursive(nfa::Transition::Labelled(label_a)),
                NfaState::Direct(nfa::Transition::Labelled(label_b)),
                NfaState::Direct(nfa::Transition::Wildcard),
                NfaState::Direct(nfa::Transition::Labelled(label_a)),
                NfaState::Recursive(nfa::Transition::Labelled(label_b)),
                NfaState::Accepting,
            ],
        };
        let result = minimize(nfa).unwrap();
        let expected = Automaton {
            states: vec![
                StateTable {
                    transitions: smallvec![],
                    fallback_state: State(0),
                    attributes: StateAttributes::REJECTING,
                },
                StateTable {
                    transitions: smallvec![(label_a, State(2))],
                    fallback_state: State(1),
                    attributes: StateAttributes::EMPTY,
                },
                StateTable {
                    transitions: smallvec![(label_a, State(2)), (label_b, State(3))],
                    fallback_state: State(1),
                    attributes: StateAttributes::EMPTY,
                },
                StateTable {
                    transitions: smallvec![(label_a, State(5))],
                    fallback_state: State(4),
                    attributes: StateAttributes::EMPTY,
                },
                StateTable {
                    transitions: smallvec![(label_a, State(6))],
                    fallback_state: State(1),
                    attributes: StateAttributes::EMPTY,
                },
                StateTable {
                    transitions: smallvec![(label_a, State(6)), (label_b, State(3))],
                    fallback_state: State(1),
                    attributes: StateAttributes::EMPTY,
                },
                StateTable {
                    transitions: smallvec![(label_b, State(7))],
                    fallback_state: State(6),
                    attributes: StateAttributes::TRANSITIONS_TO_ACCEPTING,
                },
                StateTable {
                    transitions: smallvec![(label_b, State(7))],
                    fallback_state: State(6),
                    attributes: StateAttributes::ACCEPTING | StateAttributes::TRANSITIONS_TO_ACCEPTING,
                },
            ],
        };
        assert_eq!(result, expected);
    }
    #[test]
    fn simple_multi_accepting() {
        let label = JsonString::new("a");
        let label = (&label).into();
        let nfa = NondeterministicAutomaton {
            ordered_states: vec![
                NfaState::Recursive(nfa::Transition::Labelled(label)),
                NfaState::Direct(nfa::Transition::Wildcard),
                NfaState::Accepting,
            ],
        };
        let result = minimize(nfa).unwrap();
        let expected = Automaton {
            states: vec![
                StateTable {
                    transitions: smallvec![],
                    fallback_state: State(0),
                    attributes: StateAttributes::REJECTING,
                },
                StateTable {
                    transitions: smallvec![(label, State(2)),],
                    fallback_state: State(1),
                    attributes: StateAttributes::EMPTY,
                },
                StateTable {
                    transitions: smallvec![(label, State(4))],
                    fallback_state: State(3),
                    attributes: StateAttributes::TRANSITIONS_TO_ACCEPTING,
                },
                StateTable {
                    transitions: smallvec![(label, State(2))],
                    fallback_state: State(1),
                    attributes: StateAttributes::ACCEPTING,
                },
                StateTable {
                    transitions: smallvec![(label, State(4))],
                    fallback_state: State(3),
                    attributes: StateAttributes::ACCEPTING | StateAttributes::TRANSITIONS_TO_ACCEPTING,
                },
            ],
        };
        assert_eq!(result, expected);
    }
    #[test]
    fn simple_multi_accepting_nneg_index() {
        let label = TransitionLabel::ArrayIndex(0.try_into().unwrap());
        let nfa = NondeterministicAutomaton {
            ordered_states: vec![
                NfaState::Recursive(nfa::Transition::Labelled(label)),
                NfaState::Accepting,
            ],
        };
        let result = minimize(nfa).unwrap();
        let expected = Automaton {
            states: vec![
                StateTable {
                    transitions: smallvec![],
                    fallback_state: State(0),
                    attributes: StateAttributes::REJECTING,
                },
                StateTable {
                    transitions: smallvec![(label, State(2)),],
                    fallback_state: State(1),
                    attributes: StateAttributes::TRANSITIONS_TO_ACCEPTING,
                },
                StateTable {
                    transitions: smallvec![(label, State(2))],
                    fallback_state: State(1),
                    attributes: StateAttributes::TRANSITIONS_TO_ACCEPTING | StateAttributes::ACCEPTING,
                },
            ],
        };
        assert_eq!(result, expected);
    }
    #[test]
    fn chained_wildcard_children() {
        let label = JsonString::new("a");
        let label = (&label).into();
        let nfa = NondeterministicAutomaton {
            ordered_states: vec![
                NfaState::Direct(nfa::Transition::Labelled(label)),
                NfaState::Direct(nfa::Transition::Wildcard),
                NfaState::Direct(nfa::Transition::Wildcard),
                NfaState::Direct(nfa::Transition::Wildcard),
                NfaState::Accepting,
            ],
        };
        let result = minimize(nfa).unwrap();
        let expected = Automaton {
            states: vec![
                StateTable {
                    transitions: smallvec![],
                    fallback_state: State(0),
                    attributes: StateAttributes::REJECTING,
                },
                StateTable {
                    transitions: smallvec![(label, State(2))],
                    fallback_state: State(0),
                    attributes: StateAttributes::UNITARY,
                },
                StateTable {
                    transitions: smallvec![],
                    fallback_state: State(3),
                    attributes: StateAttributes::EMPTY,
                },
                StateTable {
                    transitions: smallvec![],
                    fallback_state: State(4),
                    attributes: StateAttributes::EMPTY,
                },
                StateTable {
                    transitions: smallvec![],
                    fallback_state: State(5),
                    attributes: StateAttributes::TRANSITIONS_TO_ACCEPTING,
                },
                StateTable {
                    transitions: smallvec![],
                    fallback_state: State(0),
                    attributes: StateAttributes::ACCEPTING,
                },
            ],
        };
        assert_eq!(result, expected);
    }
    #[test]
    fn chained_wildcard_children_after_descendant() {
        let label = JsonString::new("a");
        let label = (&label).into();
        let nfa = NondeterministicAutomaton {
            ordered_states: vec![
                NfaState::Recursive(nfa::Transition::Labelled(label)),
                NfaState::Direct(nfa::Transition::Wildcard),
                NfaState::Direct(nfa::Transition::Wildcard),
                NfaState::Accepting,
            ],
        };
        let result = minimize(nfa).unwrap();
        let expected = Automaton {
            states: vec![
                StateTable {
                    transitions: smallvec![],
                    fallback_state: State(0),
                    attributes: StateAttributes::REJECTING,
                },
                StateTable {
                    transitions: smallvec![(label, State(2))],
                    fallback_state: State(1),
                    attributes: StateAttributes::EMPTY,
                },
                StateTable {
                    transitions: smallvec![(label, State(4))],
                    fallback_state: State(3),
                    attributes: StateAttributes::EMPTY,
                },
                StateTable {
                    transitions: smallvec![(label, State(8))],
                    fallback_state: State(7),
                    attributes: StateAttributes::EMPTY | StateAttributes::TRANSITIONS_TO_ACCEPTING,
                },
                StateTable {
                    transitions: smallvec![(label, State(6))],
                    fallback_state: State(5),
                    attributes: StateAttributes::EMPTY | StateAttributes::TRANSITIONS_TO_ACCEPTING,
                },
                StateTable {
                    transitions: smallvec![(label, State(8))],
                    fallback_state: State(7),
                    attributes: StateAttributes::ACCEPTING | StateAttributes::TRANSITIONS_TO_ACCEPTING,
                },
                StateTable {
                    transitions: smallvec![(label, State(6))],
                    fallback_state: State(5),
                    attributes: StateAttributes::ACCEPTING | StateAttributes::TRANSITIONS_TO_ACCEPTING,
                },
                StateTable {
                    transitions: smallvec![(label, State(2))],
                    fallback_state: State(1),
                    attributes: StateAttributes::ACCEPTING,
                },
                StateTable {
                    transitions: smallvec![(label, State(4))],
                    fallback_state: State(3),
                    attributes: StateAttributes::ACCEPTING,
                },
            ],
        };
        assert_eq!(result, expected);
    }
    #[test]
    fn child_and_descendant() {
        let label_a = JsonString::new("a");
        let label_a = (&label_a).into();
        let label_b = JsonString::new("b");
        let label_b = (&label_b).into();
        let label_c = JsonString::new("c");
        let label_c = (&label_c).into();
        let label_d = JsonString::new("d");
        let label_d = (&label_d).into();
        let label_x = JsonString::new("x");
        let label_x = (&label_x).into();
        let nfa = NondeterministicAutomaton {
            ordered_states: vec![
                NfaState::Direct(nfa::Transition::Labelled(label_x)),
                NfaState::Recursive(nfa::Transition::Labelled(label_a)),
                NfaState::Direct(nfa::Transition::Labelled(label_b)),
                NfaState::Direct(nfa::Transition::Labelled(label_a)),
                NfaState::Direct(nfa::Transition::Labelled(label_b)),
                NfaState::Direct(nfa::Transition::Labelled(label_c)),
                NfaState::Recursive(nfa::Transition::Labelled(label_d)),
                NfaState::Accepting,
            ],
        };
        let result = minimize(nfa).unwrap();
        let expected = Automaton {
            states: vec![
                StateTable {
                    transitions: smallvec![],
                    fallback_state: State(0),
                    attributes: StateAttributes::REJECTING,
                },
                StateTable {
                    transitions: smallvec![(label_x, State(2))],
                    fallback_state: State(0),
                    attributes: StateAttributes::UNITARY,
                },
                StateTable {
                    transitions: smallvec![(label_a, State(3))],
                    fallback_state: State(2),
                    attributes: StateAttributes::EMPTY,
                },
                StateTable {
                    transitions: smallvec![(label_a, State(3)), (label_b, State(4))],
                    fallback_state: State(2),
                    attributes: StateAttributes::EMPTY,
                },
                StateTable {
                    transitions: smallvec![(label_a, State(5))],
                    fallback_state: State(2),
                    attributes: StateAttributes::EMPTY,
                },
                StateTable {
                    transitions: smallvec![(label_a, State(3)), (label_b, State(6))],
                    fallback_state: State(2),
                    attributes: StateAttributes::EMPTY,
                },
                StateTable {
                    transitions: smallvec![(label_a, State(5)), (label_c, State(7))],
                    fallback_state: State(2),
                    attributes: StateAttributes::EMPTY,
                },
                StateTable {
                    transitions: smallvec![(label_d, State(8))],
                    fallback_state: State(7),
                    attributes: StateAttributes::TRANSITIONS_TO_ACCEPTING,
                },
                StateTable {
                    transitions: smallvec![(label_d, State(8))],
                    fallback_state: State(7),
                    attributes: StateAttributes::ACCEPTING | StateAttributes::TRANSITIONS_TO_ACCEPTING,
                },
            ],
        };
        assert_eq!(result, expected);
    }
    #[test]
    fn child_descendant_and_child_wildcard() {
        let label_a = JsonString::new("a");
        let label_a = (&label_a).into();
        let label_b = JsonString::new("b");
        let label_b = (&label_b).into();
        let label_x = JsonString::new("x");
        let label_x = (&label_x).into();
        let nfa = NondeterministicAutomaton {
            ordered_states: vec![
                NfaState::Direct(nfa::Transition::Labelled(label_x)),
                NfaState::Direct(nfa::Transition::Wildcard),
                NfaState::Recursive(nfa::Transition::Labelled(label_a)),
                NfaState::Direct(nfa::Transition::Wildcard),
                NfaState::Direct(nfa::Transition::Labelled(label_b)),
                NfaState::Accepting,
            ],
        };
        let result = minimize(nfa).unwrap();
        let expected = Automaton {
            states: vec![
                StateTable {
                    transitions: smallvec![],
                    fallback_state: State(0),
                    attributes: StateAttributes::REJECTING,
                },
                StateTable {
                    transitions: smallvec![(label_x, State(2))],
                    fallback_state: State(0),
                    attributes: StateAttributes::UNITARY,
                },
                StateTable {
                    transitions: smallvec![],
                    fallback_state: State(3),
                    attributes: StateAttributes::EMPTY,
                },
                StateTable {
                    transitions: smallvec![(label_a, State(4))],
                    fallback_state: State(3),
                    attributes: StateAttributes::EMPTY,
                },
                StateTable {
                    transitions: smallvec![(label_a, State(6))],
                    fallback_state: State(5),
                    attributes: StateAttributes::EMPTY,
                },
                StateTable {
                    transitions: smallvec![(label_a, State(4)), (label_b, State(8))],
                    fallback_state: State(3),
                    attributes: StateAttributes::TRANSITIONS_TO_ACCEPTING,
                },
                StateTable {
                    transitions: smallvec![(label_a, State(6)), (label_b, State(7))],
                    fallback_state: State(5),
                    attributes: StateAttributes::TRANSITIONS_TO_ACCEPTING,
                },
                StateTable {
                    transitions: smallvec![(label_a, State(4)), (label_b, State(8))],
                    fallback_state: State(3),
                    attributes: StateAttributes::ACCEPTING | StateAttributes::TRANSITIONS_TO_ACCEPTING,
                },
                StateTable {
                    transitions: smallvec![(label_a, State(4))],
                    fallback_state: State(3),
                    attributes: StateAttributes::ACCEPTING,
                },
            ],
        };
        assert_eq!(result, expected);
    }
    #[test]
    fn all_name_and_wildcard_selectors() {
        let label_a = JsonString::new("a");
        let label_a = (&label_a).into();
        let label_b = JsonString::new("b");
        let label_b = (&label_b).into();
        let label_c = JsonString::new("c");
        let label_c = (&label_c).into();
        let label_d = JsonString::new("d");
        let label_d = (&label_d).into();
        let nfa = NondeterministicAutomaton {
            ordered_states: vec![
                NfaState::Direct(nfa::Transition::Labelled(label_a)),
                NfaState::Direct(nfa::Transition::Labelled(label_b)),
                NfaState::Recursive(nfa::Transition::Labelled(label_c)),
                NfaState::Recursive(nfa::Transition::Labelled(label_d)),
                NfaState::Direct(nfa::Transition::Wildcard),
                NfaState::Recursive(nfa::Transition::Wildcard),
                NfaState::Accepting,
            ],
        };
        let expected = Automaton {
            states: vec![
                StateTable {
                    transitions: smallvec![],
                    fallback_state: State(0),
                    attributes: StateAttributes::REJECTING,
                },
                StateTable {
                    transitions: smallvec![(label_a, State(2)),],
                    fallback_state: State(0),
                    attributes: StateAttributes::UNITARY,
                },
                StateTable {
                    transitions: smallvec![(label_b, State(3))],
                    fallback_state: State(0),
                    attributes: StateAttributes::UNITARY,
                },
                StateTable {
                    transitions: smallvec![(label_c, State(4))],
                    fallback_state: State(3),
                    attributes: StateAttributes::EMPTY,
                },
                StateTable {
                    transitions: smallvec![(label_d, State(5))],
                    fallback_state: State(4),
                    attributes: StateAttributes::EMPTY,
                },
                StateTable {
                    transitions: smallvec![(label_d, State(6))],
                    fallback_state: State(6),
                    attributes: StateAttributes::EMPTY,
                },
                StateTable {
                    transitions: smallvec![],
                    fallback_state: State(7),
                    attributes: StateAttributes::TRANSITIONS_TO_ACCEPTING,
                },
                StateTable {
                    transitions: smallvec![],
                    fallback_state: State(7),
                    attributes: StateAttributes::ACCEPTING | StateAttributes::TRANSITIONS_TO_ACCEPTING,
                },
            ],
        };
        let result = minimize(nfa).unwrap();
        assert_eq!(result, expected);
    }
}