All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.leibnizcenter.nfa.NFA Maven / Gradle / Ivy

The newest version!
package org.leibnizcenter.nfa;

import com.github.krukow.clj_ds.PersistentList;
import com.google.common.collect.*;

import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * Immutable NFA
 * 

* Created by maarten on 15-6-16. */ @SuppressWarnings("WeakerAccess") public class NFA { public final Map>> transitions; public final Set states; public final Multimap statesThatAllowEvent; private NFA(Builder builder) { this.states = ImmutableSet.copyOf(builder.states); ImmutableMap.Builder>> immTransitions = new ImmutableMap.Builder<>(); // O(transitions.numberOfBranches()) builder.transitions.forEach((state, eventToTransitionMap) -> { final ImmutableMultimap.Builder> eventToTransitionMapBuilder = new ImmutableMultimap.Builder<>(); eventToTransitionMap.forEach(eventToTransitionMapBuilder::putAll); immTransitions.put(state, eventToTransitionMapBuilder.build()); }); this.transitions = immTransitions.build(); // O(transitions.numberOfBranches()) ImmutableMultimap.Builder immStatesThatAllowEvent = new ImmutableMultimap.Builder<>(); builder.transitions.forEach((state, eventMap) -> eventMap.forEach((event, transitions) -> immStatesThatAllowEvent.put(event, state) ) ); statesThatAllowEvent = immStatesThatAllowEvent.build(); // // Sanity check: // // O(transitions.numberOfBranches()) builder.transitions.forEach((state, eventMap) -> { assert (eventMap.values().stream() .flatMap(Collection::stream) .map(Transition::getFrom) .filter(st4t3 -> !state.equals(st4t3)) .limit(1).count() == 0) : "Error in map from state " + state + " to transitions. This is a bug."; } ); } @SuppressWarnings("unused") public PossibleStateTransitionPaths getTransitions(S start, LinkedList events) { final List events1 = ImmutableList.copyOf(events); // O(n) return precomputePaths(events) .get(start) .get(events1); } public Stream apply(S start, LinkedList events) { final PossibleStateTransitionPaths transitions = getTransitions(start, events); return transitions.applyRecursive(); } /** * O(path.numberOfBranches() * states.numberOfBranches() * transitions.numberOfBranches()) * * @param event Input events to use for computing all possible paths along the NFA * @return A map from starting states to a map of input events to an enumeration of possible branches */ public Map, PossibleStateTransitionPaths>> precomputePaths(LinkedList event) { PersistentList postFixPath = com.github.krukow.clj_lang.PersistentList.create((Iterable) new ArrayList(0)); // O(1) Map, PossibleStateTransitionPaths>> precomputedPaths = new HashMap<>(states.size());// O(1) while (event.size() > 0) { // O(path.numberOfBranches()) * E lastEvent = event.removeLast(); // O(1) postFixPath = postFixPath.plus(lastEvent); // O(1) // TODO filter only those states that *can* be reached through the previous action for (S state : statesThatAllowEvent.get(lastEvent)) { // O(states.numberOfBranches()) * Map, PossibleStateTransitionPaths> pathsForEvents = precomputedPaths.getOrDefault(state, new HashMap<>());//O(1) final Collection> possibleTransitions = getTransitions(state, lastEvent); // O(1) PossibleStateTransitionPaths possibleBranches; if (postFixPath.size() == 1) { possibleBranches = new PossibleStateTransitionPaths<>(state, possibleTransitions, postFixPath, null); // O(1) } else { final PersistentList furtherEvents = postFixPath.minus(); ImmutableMap.Builder> restPaths = ImmutableMap.builder(); // O(1) possibleTransitions.stream() // O(possibleTransitions.numberOfBranches()) .map(Transition::getTo) .distinct() .forEach(possibleTargetState -> { PossibleStateTransitionPaths restBranches = precomputedPaths.get(possibleTargetState).get(furtherEvents); assert restBranches != null : "Possible branches must have been calculated for state " + possibleTargetState; restPaths.put(possibleTargetState, restBranches); }); possibleBranches = new PossibleStateTransitionPaths<>( state, possibleTransitions, postFixPath, restPaths.build() ); // O(possibleTransitions.numberOfBranches()) } assert !pathsForEvents.containsKey(postFixPath) : "Already computed possible paths for " + postFixPath + "?!"; pathsForEvents.put(postFixPath, possibleBranches); // O(1) precomputedPaths.putIfAbsent(state, pathsForEvents);// O(1) } } return precomputedPaths; } public Collection> getTransitions(S from, E event) { final Multimap> eventTransitionMultimap = transitions.get(from); if (eventTransitionMultimap != null) return eventTransitionMultimap.get(event); else return Collections.emptySet(); } public Set getStates() { return states; } public StateContainer start(S state) { return new StateContainer(Collections.singletonList(state)); } @SuppressWarnings("unused") public Collection getStatesThatAllowEvent(E e) { return statesThatAllowEvent.get(e); } public static class Builder { private final Set states; private final Map>>> transitions; public Builder() { this.states = new HashSet<>(50); transitions = new HashMap<>(50); } @SuppressWarnings("unused") public Builder addStates(Collection states) { this.states.addAll(states); return this; } public Builder addState(S states) { this.states.add(states); return this; } /** * Will automatically add states if they've not been added separately. * * @param from From state * @param event Transition event * @param to To state * @return This builder */ public Builder addTransition(S from, E event, S to) { states.add(from); states.add(to); addTransition(new Transition<>(event, from, to), from, event); return this; } /** * Will automatically add states if they've not been added separately. * * @param transitionsToAdd List of transitions. Implicit states will be added if necessary. * @return This builder */ public Builder addTransitions(Collection transitionsToAdd) { transitionsToAdd.forEach(this::addTransition); return this; } /** * Will automatically add states if they've not been added separately. * * @param transition Implicit states will be added if necessary. * @return This builder */ public Builder addTransition(Transition transition) { S from = transition.from; S to = transition.to; E event = transition.event; states.add(from); states.add(to); addTransition(transition, from, event); return this; } private void addTransition(Transition transition, S from, E event) { Map>> eventsForState = transitions.getOrDefault(from, new HashMap<>()); Set> transitionsForEvent = eventsForState.getOrDefault(event, new HashSet<>()); transitionsForEvent.add(transition); eventsForState.putIfAbsent(event, transitionsForEvent); transitions.putIfAbsent(from, eventsForState); } public NFA build() { return new NFA<>(this); } } public class StateContainer { public Collection states; public StateContainer(Collection ses) { states = ses; } public StateContainer andThen(E e) { states = states.stream() .flatMap(from -> getTransitions(from, e).stream().map(transition -> {//noinspection unchecked e.accept(transition.getFrom(), transition.getTo()); return transition; })).map(Transition::getTo).collect(Collectors.toList()); return this; } public Stream getState() { return states.stream(); } } }