private static void WriteStates(TextWriter writer, FiniteAutomata automata) { writer.WriteLine($"\"\" [shape=none]"); foreach (var state in automata.States) { writer.WriteLine($"\"{state.StateName}\" [shape= {(state.IsFinal ? "doublecircle" : "circle")}]"); } }
private static void PrepareDotFile(FiniteAutomata automata) { using (var writer = new StreamWriter($"../../../{DotFileName}", false)) { writer.WriteLine("digraph myAutomation {"); writer.WriteLine("rankdir=LR;"); WriteStates(writer, automata); WriteTransitions(writer, automata); writer.WriteLine("}"); } }
private static void WriteTransitions(TextWriter writer, FiniteAutomata automata) { writer.WriteLine($"\"\" -> \"{automata.States.GetInitialState().StateName}\""); foreach (var state in automata.States) { foreach (var transition in state.Transitions) { writer.WriteLine( $"\"{state.StateName}\" -> \"{transition.TransitionTo.StateName}\" [label = \"{transition.GetTextForGraphLabel()}\"]"); } } }
public static void WriteToFile(FiniteAutomata automata, string fileName) { using (var w = File.CreateText($"../../../{fileName}.txt")) { w.WriteLine($"# {automata.Comment}"); w.WriteLine( $"alphabet: {string.Join(",", automata.Alphabet.AlphabetChars.Select(char.ToLower).ToList())}"); w.WriteLine( $"states: {string.Join(",", automata.States.OrderByDescending(x => x.IsInitial).Select(x => x.StateName))}"); w.WriteLine( $"final: {string.Join(",", automata.States.Where(x => x.IsFinal).Select(x => x.StateName))}"); w.WriteLine("transitions:"); foreach (var state in automata.States) { foreach (var transition in state.Transitions) { w.WriteLine( $"{transition.TransitionFrom.StateName}," + $"{(transition.TransitionChar == Epsilon.Letter ? '_' : transition.TransitionChar)}" + $" --> {transition.TransitionTo.StateName}"); } } w.WriteLine("end."); w.WriteLine(Environment.NewLine); if (automata.TestWords.Any()) { w.WriteLine("words:"); foreach (var word in automata.TestWords) { w.WriteLine($"{word},{(word.IsAccepted ? "y" : "n")}"); } w.WriteLine("end."); } w.WriteLine(Environment.NewLine); w.WriteLine($"finite: {(automata.IsFinite ? "y" : "n")}"); w.WriteLine(Environment.NewLine); w.WriteLine($"dfa: {(automata.IsDfa ? "y" : "n")}"); } }
public virtual FiniteAutomata ToDfa() { if (this.IsDfa) { return(this); } // A dictionary to store the combined state and it's individual states. var currentStatesDict = new Dictionary <State, List <State> >(); var notProcessedYetStates = new List <State>(); var finishedStates = new List <State>(); // First get the epsilon closure. Then make a single state and add it to the to be processed states. var initialEpsilonClosure = this.GetPossibleEpsilonStates(new List <State>() { this.States.GetInitialState() }) .OrderBy(x => x.StateName) .ToList(); var initialDfaState = this.MakeSingleState(initialEpsilonClosure, true, initialEpsilonClosure.Any(x => x.IsFinal)); notProcessedYetStates.Add(initialDfaState); currentStatesDict.Add(initialDfaState, initialEpsilonClosure); while (notProcessedYetStates.Any()) { // Always process the first one. We know that there is at least 1 because of the check in the while loop. var toBeProcessed = notProcessedYetStates[0]; var currentStates = currentStatesDict[toBeProcessed]; foreach (var letter in this.Alphabet.AlphabetChars) { // 1. Get all the possible states reachable by the letter. If any then also get the epsilon closures that can be reached // from these states. Note the ordering by name to ensure consistency and to avoid scenarios where {2,3} is considered a different // state than {3,2}. var states = this.GetPossibleStates(currentStates, letter); if (states.Any()) { var statesAndEpsilonClosure = this.GetPossibleEpsilonStates(states) .OrderBy(x => x.StateName) .ToList(); // Note that we mark the combined state as final as long as any of the states in the set are final. var newState = this.MakeSingleState( statesAndEpsilonClosure, false, statesAndEpsilonClosure.Any(x => x.IsFinal)); if (toBeProcessed.StateName == "B") { var z = false; } // Note that we can check if the new state is not actually the same one - in case of a self-loop. newState = newState.Equals(toBeProcessed) ? toBeProcessed : newState; // If the resulting state has already been processed -> assign it to the variable. Else add it to the to be processed list. if (finishedStates.Contains(newState)) { newState = finishedStates.Single(x => x.Equals(newState)); } else if (!notProcessedYetStates.Contains(newState)) { notProcessedYetStates.Add(newState); currentStatesDict.Add(newState, statesAndEpsilonClosure); } toBeProcessed.AddTransition(new Transition(letter, toBeProcessed, newState)); } // If there is no states reachable by letter that means we need a sink. First verify if one doesn't already exist. If it does, add a transition // to it. Else create a new sink, make a self-transition for each letter to also ensure the sink is considered DFA and then add it directly // to the finished states list. else { var sink = finishedStates.SingleOrDefault(x => x.IsSink); if (sink == null) { sink = new State("Sink", false, false, true); foreach (var sinkLetter in this.Alphabet.AlphabetChars) { sink.AddTransition(new Transition(sinkLetter, sink, sink)); } finishedStates.Add(sink); } toBeProcessed.AddTransition(new Transition(letter, toBeProcessed, sink)); } } foreach (var finished in finishedStates) { // Make sure to update existing references to keep the transitions up to date. var transitionsTo = finished.Transitions.Where(x => x.TransitionTo.StateName == toBeProcessed.StateName); foreach (var trans in transitionsTo) { trans.TransitionTo = toBeProcessed; } } // Finally add the processed state to the final ones and remove it from the to be processed. Also clean up the dictionary. finishedStates.Add(toBeProcessed); notProcessedYetStates.Remove(toBeProcessed); currentStatesDict.Remove(toBeProcessed); } var automata = new FiniteAutomata(this.Comment, this.Alphabet, new List <State>(finishedStates), this.TestWords, true, false); automata.AcceptWords(); return(automata); }
public static string CreateNodeGraphImage(FiniteAutomata automata) { PrepareDotFile(automata); return(CreateAutomataGraph()); }