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

it.unive.lisa.analysis.string.fsa.SimpleAutomaton Maven / Gradle / Ivy

The newest version!
package it.unive.lisa.analysis.string.fsa;

import it.unive.lisa.analysis.numeric.Interval;
import it.unive.lisa.util.datastructures.automaton.Automaton;
import it.unive.lisa.util.datastructures.automaton.CyclicAutomatonException;
import it.unive.lisa.util.datastructures.automaton.State;
import it.unive.lisa.util.datastructures.automaton.Transition;
import it.unive.lisa.util.datastructures.regex.Atom;
import it.unive.lisa.util.datastructures.regex.RegularExpression;
import it.unive.lisa.util.numeric.IntInterval;
import it.unive.lisa.util.numeric.MathNumber;
import it.unive.lisa.util.numeric.MathNumberConversionException;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Optional;
import java.util.Set;
import java.util.SortedSet;
import java.util.TreeSet;
import java.util.stream.Collectors;

/**
 * A class that describes an generic automaton(dfa, nfa, epsilon nfa) using a
 * standard alphabet of single characters.
 *
 * @author Simone Leoni
 * @author Vincenzo Arceri
 * @author Luca Negrini
 */
public final class SimpleAutomaton extends Automaton {

	@Override
	public SimpleAutomaton singleString(
			String string) {
		return new SimpleAutomaton(string);
	}

	@Override
	public SimpleAutomaton unknownString() {
		SortedSet newStates = new TreeSet<>();
		SortedSet> newGamma = new TreeSet<>();
		State initialState = new State(0, true, true);

		newStates.add(initialState);

		for (char alphabet = '!'; alphabet <= '~'; ++alphabet)
			newGamma.add(new Transition<>(initialState, initialState, new StringSymbol(alphabet)));

		return new SimpleAutomaton(newStates, newGamma);
	}

	@Override
	public SimpleAutomaton emptyLanguage() {
		SortedSet newStates = new TreeSet<>();
		State initialState = new State(0, true, false);
		newStates.add(initialState);

		return new SimpleAutomaton(newStates, Collections.emptySortedSet());
	}

	@Override
	public SimpleAutomaton emptyString() {
		return new SimpleAutomaton("");
	}

	@Override
	public SimpleAutomaton from(
			SortedSet states,
			SortedSet> transitions) {
		return new SimpleAutomaton(states, transitions);
	}

	@Override
	public StringSymbol epsilon() {
		return StringSymbol.EPSILON;
	}

	@Override
	public StringSymbol concat(
			StringSymbol first,
			StringSymbol second) {
		return first.concat(second);
	}

	@Override
	public RegularExpression symbolToRegex(
			StringSymbol symbol) {
		return new Atom(symbol.getSymbol());
	}

	/**
	 * Builds a new automaton with given {@code states} and {@code transitions}.
	 *
	 * @param states      the set of states of the new automaton
	 * @param transitions the set of the transitions of the new automaton
	 */
	public SimpleAutomaton(
			SortedSet states,
			SortedSet> transitions) {
		super(states, transitions);
	}

	/**
	 * Builds a new automaton that accepts a given string.
	 * 
	 * @param s the only string accepted by the automaton.
	 */
	public SimpleAutomaton(
			String s) {
		super();

		if (s.isEmpty())
			states.add(new State(0, true, true));
		else {
			State last = new State(0, true, false);
			State next;
			states.add(last);
			for (int i = 0; i < s.length(); ++i) {
				if (i != s.length() - 1)
					next = new State(i + 1, false, false);
				else
					next = new State(i + 1, false, true);

				transitions.add(new Transition<>(last, next, new StringSymbol(s.charAt(i))));
				last = next;
				states.add(last);
			}
		}

		deterministic = Optional.of(true);
		minimized = Optional.of(true);
	}

	/**
	 * Computes all the automaton transitions to validate a given string
	 * {@code str}.
	 *
	 * @param str String that has to be checked.
	 * 
	 * @return a boolean value that indicates either if {@code str} has been
	 *             accepted or not.
	 */
	public boolean validateString(
			String str) {
		// stores all the possible states reached by the automaton after each
		// input char
		Set currentStates = epsilonClosure(getInitialStates());

		for (int i = 0; i < str.length(); ++i) {
			String c = "" + str.charAt(i);

			// stores temporally the new currentStates
			Set newCurr = new TreeSet<>();
			for (State s : currentStates) {

				// stores all the states reached after char computation
				Set dest = transitions.stream()
						.filter(t -> t.getSource().equals(s) && t.getSymbol().getSymbol().equals(c))
						.map(Transition::getDestination).collect(Collectors.toSet());
				if (!dest.isEmpty()) {
					dest = epsilonClosure(dest);
					newCurr.addAll(dest);
				}
			}
			currentStates = newCurr;
		}

		// checks if there is at least one final state in the set of possible
		// reached states at the end of the validation process
		return currentStates.stream().anyMatch(State::isFinal);
	}

	/**
	 * Yields a new automaton where all occurrences of strings recognized by
	 * {@code toReplace} are replaced with the automaton {@code str}, assuming
	 * that {@code toReplace} is finite (i.e., no loops nor top-transitions).
	 * The resulting automaton is then collapsed.
*
* * @param toReplace the automaton recognizing the strings to replace * @param str the automaton that must be used as replacement * * @return the replaced automaton * * @throws CyclicAutomatonException if {@code toReplace} contains loops */ public SimpleAutomaton replace( SimpleAutomaton toReplace, SimpleAutomaton str) throws CyclicAutomatonException { if (this.hasCycle() || toReplace.hasCycle() || str.hasCycle()) return unknownString(); SimpleAutomaton result = emptyLanguage(); for (String a : getLanguage()) for (String b : toReplace.getLanguage()) for (String c : str.getLanguage()) if (a.contains(b)) result = result.union(new SimpleAutomaton(a.replace(b, c))); return result; } /** * Yields a new automaton where leading whitespaces have been removed from * {@code this}. * * @return a new automaton where leading whitespaces have been removed from * {@code this} */ public SimpleAutomaton trimLeft() { SimpleAutomaton result = copy(); Set> toAdd = new HashSet<>(); Set> toRemove = new HashSet<>(); while (true) { for (Transition t : result.getOutgoingTransitionsFrom(result.getInitialState())) if (t.getSymbol().getSymbol().equals(" ")) { toRemove.add(t); toAdd.add(new Transition(t.getSource(), t.getDestination(), StringSymbol.EPSILON)); } result.removeTransitions(toRemove); result.getTransitions().addAll(toAdd); result.minimize(); boolean b = false; for (Transition t : result.getOutgoingTransitionsFrom(result.getInitialState())) { if (t.getSymbol().getSymbol().equals(" ")) b = true; } if (!b) break; } return result; } /** * Yields a new automaton where trailing whitespaces have been removed from * {@code this}. * * @return a new automaton where trailing whitespaces have been removed from * {@code this} */ public SimpleAutomaton trimRight() { SimpleAutomaton result = copy(); Set> toAdd = new HashSet<>(); Set> toRemove = new HashSet<>(); while (true) { for (State qf : result.getFinalStates()) { for (Transition t : result.getIngoingTransitionsFrom(qf)) if (t.getSymbol().getSymbol().equals(" ")) { toRemove.add(t); toAdd.add( new Transition(t.getSource(), t.getDestination(), StringSymbol.EPSILON)); } } result.removeTransitions(toRemove); result.getTransitions().addAll(toAdd); result.minimize(); boolean b = false; for (State qf : result.getFinalStates()) { for (Transition t : result.getIngoingTransitionsFrom(qf)) { if (t.getSymbol().equals(new StringSymbol(" "))) b = true; } } if (!b) break; } return result; } /** * Yields a new automaton where leading and trailing whitespaces have been * removed from {@code this}. * * @return a new automaton where leading trailing whitespaces have been * removed from {@code this} */ public SimpleAutomaton trim() { return this.toRegex().trimRight().simplify().trimLeft().simplify().toAutomaton(this); } /** * Yields a new automaton instance recognizing each string of {@code this} * automaton repeated k-times, with k belonging to {@code intv}. * * @param i the interval * * @return a new automaton instance recognizing each string of {@code this} * automaton repeated k-times, with k belonging to {@code intv} * * @throws MathNumberConversionException if {@code intv} is iterated but is * not finite */ public SimpleAutomaton repeat( Interval i) throws MathNumberConversionException { if (equals(emptyString())) return this; else if (hasCycle()) return star(); MathNumber high = i.interval.getHigh(); MathNumber low = i.interval.getLow(); SimpleAutomaton epsilon = emptyString(); if (low.isMinusInfinity()) { if (high.isPlusInfinity()) return epsilon.union(auxRepeat(new IntInterval(MathNumber.ONE, high), getInitialState(), new TreeSet>(), emptyLanguage())); if (high.isZero()) return emptyString(); else return epsilon.union(auxRepeat(new IntInterval(MathNumber.ONE, high), getInitialState(), new TreeSet>(), emptyLanguage())); } long lowInt = low.toLong(); if (high.isPlusInfinity()) { // need exception if (lowInt < 0) return epsilon.union(auxRepeat(new IntInterval(MathNumber.ONE, high), getInitialState(), new TreeSet>(), emptyLanguage())); if (low.isZero()) return epsilon.union(auxRepeat(new IntInterval(MathNumber.ONE, high), getInitialState(), new TreeSet>(), emptyLanguage())); if (lowInt > 0) return epsilon.union(auxRepeat(i.interval, getInitialState(), new TreeSet>(), emptyLanguage())); } long highInt = high.toLong(); if (lowInt < 0) { if (highInt < 0) return emptyLanguage(); if (high.isZero()) return emptyString(); else return epsilon.union(auxRepeat(new IntInterval(MathNumber.ONE, high), getInitialState(), new TreeSet>(), emptyLanguage())); } if (low.isZero()) { if (high.isZero()) return emptyString(); if (highInt > 0) return epsilon.union(auxRepeat(new IntInterval(MathNumber.ONE, high), getInitialState(), new TreeSet>(), emptyLanguage())); } if (lowInt > 0 && highInt > 0) return auxRepeat(i.interval, getInitialState(), new TreeSet>(), emptyLanguage()); return emptyLanguage(); } private SimpleAutomaton auxRepeat( IntInterval i, State currentState, SortedSet> delta, SimpleAutomaton result) throws MathNumberConversionException { if (currentState.isFinal()) { SortedSet states = new TreeSet<>(); for (State s : getStates()) { if (!s.equals(currentState)) states.add(new State(s.getId(), s.isInitial(), false)); else states.add(new State(s.getId(), s.isInitial(), s.isFinal())); } SimpleAutomaton temp = new SimpleAutomaton(states, delta); SimpleAutomaton tempResult = temp.copy(); for (long k = 1; k < i.getLow().toLong(); k++) tempResult = tempResult.connectAutomaton(temp, tempResult.getFinalStates(), false); if (i.getHigh().isPlusInfinity()) { tempResult = tempResult.connectAutomaton(temp.copy().star(), tempResult.getFinalStates(), true); } else { for (long k = i.getLow().toLong(); k < i.getHigh().toLong(); k++) tempResult = tempResult.connectAutomaton(temp, tempResult.getFinalStates(), true); } tempResult = tempResult.minimize(); result = result.union(tempResult); } for (Transition t : getOutgoingTransitionsFrom(currentState)) { SortedSet> clone = new TreeSet>(delta); clone.add(t); result = auxRepeat(i, t.getDestination(), clone, result).union(result); } return result; } private SimpleAutomaton connectAutomaton( SimpleAutomaton second, SortedSet connectOn, boolean b) { SortedSet> delta = new TreeSet<>(); SortedSet states = new TreeSet<>(); HashMap firstMapping = new HashMap<>(); HashMap secondMapping = new HashMap<>(); int c = 0; if (equals(emptyString())) { return second; } if (second.equals(emptyString())) { return this; } for (State s : getStates()) { State newState = null; if (b) { newState = new State(c++, s.isInitial(), s.isFinal()); } else { if (connectOn.contains(s)) { newState = new State(c++, s.isInitial(), false); } else { newState = new State(c++, s.isInitial(), s.isFinal()); } } states.add(newState); firstMapping.put(s, newState); } for (Transition t : getTransitions()) { delta.add(new Transition<>(firstMapping.get(t.getSource()), firstMapping.get(t.getDestination()), t.getSymbol())); } if (second.getStates().size() == 1 && second.getInitialState().isFinal()) { for (Transition t : second.getOutgoingTransitionsFrom(second.getInitialState())) { for (State s : connectOn) { State newState = new State(firstMapping.get(s).getId(), s.isInitial(), true); states.remove(firstMapping.get(s)); states.add(newState); delta.add(new Transition<>(newState, newState, t.getSymbol())); } second.minimize(); } } else { for (State s : second.getStates()) { State newState = new State(c++, s.isInitial(), s.isFinal()); states.add(newState); secondMapping.put(s, newState); } states.remove(secondMapping.get(second.getInitialState())); secondMapping.remove(second.getInitialState()); for (Transition t : second.getTransitions()) { if (t.getSource().equals(second.getInitialState()) || t.getDestination().equals(second.getInitialState()) || !secondMapping.containsKey(t.getSource()) || !secondMapping.containsKey(t.getDestination())) continue; if (!t.getSource().isInitial() && !t.getDestination().isInitial()) { delta.add(new Transition<>(secondMapping.get(t.getSource()), secondMapping.get(t.getDestination()), t.getSymbol())); } if (t.getSource().isInitial()) { for (State s : connectOn) { if (states.contains(secondMapping.get(t.getSource()))) delta.add(new Transition<>(secondMapping.get(t.getSource()), firstMapping.get(s), t.getSymbol())); } } } for (Transition t : second.getOutgoingTransitionsFrom(second.getInitialState())) { for (State s : connectOn) { delta.add(new Transition<>(firstMapping.get(s), secondMapping.get(t.getDestination()), t.getSymbol())); } } } return new SimpleAutomaton(states, delta); } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy