diff --git a/src/custom_errors.rs b/src/custom_errors.rs index d0002a9..c709be6 100644 --- a/src/custom_errors.rs +++ b/src/custom_errors.rs @@ -11,3 +11,18 @@ pub enum DFAError { #[error("Invalid symbol: {0}")] InvalidSymbol(&'static str), } + +#[derive(Debug, Error)] +pub enum NFAError { + #[error("Already Existing Transition {0}")] + ExistingTransition(&'static str), +} + +#[derive(Debug, Error)] +pub enum AutomatonError { + #[error("DFA Error {0}")] + DFAError(DFAError), + + #[error("NFA Error {0}")] + NFAError(NFAError), +} diff --git a/src/d_transition_function.rs b/src/d_transition_function.rs index 7dc1e7a..a2c2007 100644 --- a/src/d_transition_function.rs +++ b/src/d_transition_function.rs @@ -3,7 +3,9 @@ use std::collections::HashMap; use crate::{ - custom_errors::DFAError, globals::State, symbol_table::Symbol, + custom_errors::{AutomatonError, DFAError}, + globals::State, + symbol_table::Symbol, transition_function::BasicFunctionsForTransitions, }; @@ -22,19 +24,19 @@ impl BasicFunctionsForTransitions for DTransitionFunction { state: &State, symbol: &Symbol, next_state: &State, - ) -> Result<(), crate::custom_errors::DFAError> { + ) -> Result<(), AutomatonError> { if *symbol == Symbol::Epsilon { - return Err(DFAError::InvalidTransition( + return Err(AutomatonError::DFAError(DFAError::InvalidTransition( "Epsilon should not be present in DFA Transitions", - )); + ))); } let entry = self.f.entry(*state).or_insert(HashMap::new()); if entry.contains_key(symbol) { - return Err(DFAError::InvalidTransition( + return Err(AutomatonError::DFAError(DFAError::InvalidTransition( "Adding more than one state for the same transition for DFA", - )); + ))); } entry.insert(*symbol, *next_state); @@ -49,16 +51,17 @@ impl BasicFunctionsForTransitions for DTransitionFunction { on_states.reverse(); for &curr_state in on_states.iter() { - let mut new_transitions: HashMap = HashMap::new(); + if let Some((state, symbol_state_map)) = self.f.remove_entry(&curr_state) { + let mut new_transitions: HashMap = HashMap::new(); - // (curr_state, symbol) -> next_state - for (symbol, next_state) in self.f[&curr_state].iter() { - new_transitions.insert(*symbol, *next_state + increment); - } + // (state, symbol) -> next_state + for (symbol, next_state) in symbol_state_map.iter() { + new_transitions.insert(*symbol, *next_state + increment); + } - // update the transitions - self.f.insert(curr_state + increment, new_transitions); - self.f.remove_entry(&curr_state); + // update the transitions + self.f.insert(state + increment, new_transitions); + } } } } diff --git a/src/n_transition_function.rs b/src/n_transition_function.rs index 99aeb0e..92b7efb 100644 --- a/src/n_transition_function.rs +++ b/src/n_transition_function.rs @@ -1,9 +1,10 @@ #![allow(dead_code)] use crate::{ - custom_errors::DFAError, + custom_errors::{AutomatonError, NFAError}, globals::State, - symbol_table::{Symbol, SymbolTable}, + symbol_table::Symbol, + transition_function::BasicFunctionsForTransitions, }; use std::collections::{HashMap, HashSet}; @@ -12,7 +13,98 @@ pub struct NTransitionFunction { f: HashMap>>, } +impl BasicFunctionsForTransitions for NTransitionFunction { + fn new() -> Self { + NTransitionFunction { f: HashMap::new() } + } + + fn extend(&mut self, increment: usize) { + // + let mut on_states: Vec = self.f.keys().map(|&val| val).collect(); + on_states.sort(); + // going through keys in decreasing order, in order to avoid overlapping issue + on_states.reverse(); + + for &curr_state in on_states.iter() { + if let Some((state, symbol_to_next_state_set_map)) = self.f.remove_entry(&curr_state) { + let mut new_transitions: HashMap> = HashMap::new(); + + // (curr_state, symbol) -> next_state_set + for (symbol, next_state_set) in symbol_to_next_state_set_map.iter() { + // new_transitions.insert(*symbol, *next_state + increment); + let mut new_next_state_set = HashSet::new(); + + for &next_state in next_state_set.iter() { + new_next_state_set.insert(next_state + increment); + } + + new_transitions.insert(*symbol, new_next_state_set); + } + + // update the transitions + self.f.insert(state + increment, new_transitions); + } + } + } + + fn add_transition( + &mut self, + state: &State, + symbol: &Symbol, + next_state: &State, + ) -> Result<(), AutomatonError> { + let state_transitions = self.f.entry(*state).or_insert(HashMap::new()); + let state_symbol_transitions = state_transitions.entry(*symbol).or_insert(HashSet::new()); + + if state_symbol_transitions.contains(next_state) { + return Err(AutomatonError::NFAError(NFAError::ExistingTransition(""))); + } + + state_symbol_transitions.insert(*next_state); + Ok(()) + } +} + #[cfg(test)] mod tests { use super::*; + + #[test] + fn check_normal_and_multiple_transitions() { + let mut nt = NTransitionFunction::new(); + let _ = nt + .add_transition(&0, &Symbol::Character('a'), &1) + .unwrap_or_else(|err| panic!("Error in adding transition : {}", err.to_string())); + let _ = nt + .add_transition(&0, &Symbol::Character('a'), &2) + .unwrap_or_else(|err| panic!("Error in adding transition : {}", err.to_string())); + + assert!(nt.f.contains_key(&0)); + assert!(nt.f[&0].contains_key(&Symbol::Character('a'))); + assert!(nt.f[&0][&Symbol::Character('a')].contains(&1)); + assert!(nt.f[&0][&Symbol::Character('a')].contains(&2)); + } + + #[test] + fn check_adding_epsilon_transition() { + let mut nt = NTransitionFunction::new(); + + let _ = nt + .add_transition(&0, &Symbol::Epsilon, &2) + .unwrap_or_else(|err| panic!("Error in adding transition : {}", err.to_string())); + assert!(nt.f.contains_key(&0)); + assert!(nt.f[&0].contains_key(&Symbol::Epsilon)); + assert!(nt.f[&0][&Symbol::Epsilon].contains(&2)); + } + + #[test] + fn check_adding_same_transition() { + let mut nt = NTransitionFunction::new(); + let _ = nt + .add_transition(&0, &Symbol::Character('a'), &1) + .unwrap_or_else(|err| panic!("Error in adding transition : {}", err.to_string())); + let result = nt.add_transition(&0, &Symbol::Character('a'), &1); + + assert!(result.is_err_and(|err| err.to_string().contains("Existing Transition"))); + } } diff --git a/src/transition_function.rs b/src/transition_function.rs index 29a068f..71d52dc 100644 --- a/src/transition_function.rs +++ b/src/transition_function.rs @@ -3,7 +3,7 @@ use std::collections::HashSet; use std::ops::Index; -use crate::custom_errors::DFAError; +use crate::custom_errors::AutomatonError; use crate::d_transition_function; use crate::globals::State; use crate::n_transition_function; @@ -26,7 +26,7 @@ pub trait BasicFunctionsForTransitions { state: &State, symbol: &Symbol, next_state: &State, - ) -> Result<(), DFAError>; + ) -> Result<(), AutomatonError>; fn extend(&mut self, increment: usize); }