
dk.brics.automaton.ShuffleOperations Maven / Gradle / Ivy
/*
* dk.brics.automaton
*
* Copyright (c) 2001-2011 Anders Moeller
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package dk.brics.automaton;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
/**
* Automata operations involving shuffling.
*/
final public class ShuffleOperations {
private ShuffleOperations() {}
/**
* Returns an automaton that accepts the shuffle (interleaving) of
* the languages of the given automata.
* As a side-effect, both automata are determinized, if not already deterministic.
* Never modifies the input automata languages.
*
* Complexity: quadratic in number of states (if already deterministic).
*
*
- Author:
- Torben Ruby
* <[email protected]>
*/
public static Automaton shuffle(Automaton a1, Automaton a2) {
a1.determinize();
a2.determinize();
Transition[][] transitions1 = Automaton.getSortedTransitions(a1.getStates());
Transition[][] transitions2 = Automaton.getSortedTransitions(a2.getStates());
Automaton c = new Automaton();
LinkedList worklist = new LinkedList();
HashMap newstates = new HashMap();
State s = new State();
c.initial = s;
StatePair p = new StatePair(s, a1.initial, a2.initial);
worklist.add(p);
newstates.put(p, p);
while (worklist.size() > 0) {
p = worklist.removeFirst();
p.s.accept = p.s1.accept && p.s2.accept;
Transition[] t1 = transitions1[p.s1.number];
for (int n1 = 0; n1 < t1.length; n1++) {
StatePair q = new StatePair(t1[n1].to, p.s2);
StatePair r = newstates.get(q);
if (r == null) {
q.s = new State();
worklist.add(q);
newstates.put(q, q);
r = q;
}
p.s.transitions.add(new Transition(t1[n1].min, t1[n1].max, r.s));
}
Transition[] t2 = transitions2[p.s2.number];
for (int n2 = 0; n2 < t2.length; n2++) {
StatePair q = new StatePair(p.s1, t2[n2].to);
StatePair r = newstates.get(q);
if (r == null) {
q.s = new State();
worklist.add(q);
newstates.put(q, q);
r = q;
}
p.s.transitions.add(new Transition(t2[n2].min, t2[n2].max, r.s));
}
}
c.deterministic = false;
c.removeDeadTransitions();
c.checkMinimizeAlways();
return c;
}
/**
* Returns a string that is an interleaving of strings that are accepted by
* ca
but not by a
. If no such string
* exists, null is returned. As a side-effect, a
is determinized,
* if not already deterministic. Only interleavings that respect
* the suspend/resume markers (two BMP private code points) are considered if the markers are non-null.
* Also, interleavings never split surrogate pairs.
*
* Complexity: proportional to the product of the numbers of states (if a
* is already deterministic).
*/
public static String shuffleSubsetOf(Collection ca, Automaton a, Character suspend_shuffle, Character resume_shuffle) {
if (ca.size() == 0)
return null;
if (ca.size() == 1) {
Automaton a1 = ca.iterator().next();
if (a1.isSingleton()) {
if (a.run(a1.singleton))
return null;
else
return a1.singleton;
}
if (a1 == a)
return null;
}
a.determinize();
Transition[][][] ca_transitions = new Transition[ca.size()][][];
int i = 0;
for (Automaton a1 : ca)
ca_transitions[i++] = Automaton.getSortedTransitions(a1.getStates());
Transition[][] a_transitions = Automaton.getSortedTransitions(a.getStates());
TransitionComparator tc = new TransitionComparator(false);
ShuffleConfiguration init = new ShuffleConfiguration(ca, a);
LinkedList pending = new LinkedList();
Set visited = new HashSet();
pending.add(init);
visited.add(init);
while (!pending.isEmpty()) {
ShuffleConfiguration c = pending.removeFirst();
boolean good = true;
for (int i1 = 0; i1 < ca.size(); i1++)
if (!c.ca_states[i1].accept) {
good = false;
break;
}
if (c.a_state.accept)
good = false;
if (good) {
StringBuilder sb = new StringBuilder();
while (c.prev != null) {
sb.append(c.min);
c = c.prev;
}
StringBuilder sb2 = new StringBuilder();
for (int j = sb.length() - 1; j >= 0; j--)
sb2.append(sb.charAt(j));
return sb2.toString();
}
Transition[] ta2 = a_transitions[c.a_state.number];
for (int i1 = 0; i1 < ca.size(); i1++) {
if (c.shuffle_suspended)
i1 = c.suspended1;
loop: for (Transition t1 : ca_transitions[i1][c.ca_states[i1].number]) {
List lt = new ArrayList();
int j = Arrays.binarySearch(ta2, t1, tc);
if (j < 0)
j = -j - 1;
if (j > 0 && ta2[j - 1].max >= t1.min)
j--;
while (j < ta2.length) {
Transition t2 = ta2[j++];
char min = t1.min;
char max = t1.max;
if (t2.min > min)
min = t2.min;
if (t2.max < max)
max = t2.max;
if (min <= max) {
add(suspend_shuffle, resume_shuffle, pending, visited, c, i1, t1, t2, min, max);
lt.add(new Transition(min, max, null));
} else
break;
}
Transition[] at = lt.toArray(new Transition[lt.size()]);
Arrays.sort(at, tc);
char min = t1.min;
for (int k = 0; k < at.length; k++) {
if (at[k].min > min)
break;
if (at[k].max >= t1.max)
continue loop;
min = (char)(at[k].max + 1);
}
ShuffleConfiguration nc = new ShuffleConfiguration(c, i1, t1.to, min);
StringBuilder sb = new StringBuilder();
ShuffleConfiguration b = nc;
while (b.prev != null) {
sb.append(b.min);
b = b.prev;
}
StringBuilder sb2 = new StringBuilder();
for (int m = sb.length() - 1; m >= 0; m--)
sb2.append(sb.charAt(m));
if (c.shuffle_suspended)
sb2.append(BasicOperations.getShortestExample(nc.ca_states[c.suspended1], true));
for (i1 = 0; i1 < ca.size(); i1++)
if (!c.shuffle_suspended || i1 != c.suspended1)
sb2.append(BasicOperations.getShortestExample(nc.ca_states[i1], true));
return sb2.toString();
}
if (c.shuffle_suspended)
break;
}
}
return null;
}
private static void add(Character suspend_shuffle, Character resume_shuffle,
LinkedList pending, Set visited,
ShuffleConfiguration c, int i1, Transition t1, Transition t2, char min, char max) {
final char HIGH_SURROGATE_BEGIN = '\uD800';
final char HIGH_SURROGATE_END = '\uDBFF';
if (suspend_shuffle != null && min <= suspend_shuffle && suspend_shuffle <= max && min != max) {
if (min < suspend_shuffle)
add(suspend_shuffle, resume_shuffle, pending, visited, c, i1, t1, t2, min, (char)(suspend_shuffle - 1));
add(suspend_shuffle, resume_shuffle, pending, visited, c, i1, t1, t2, suspend_shuffle, suspend_shuffle);
if (suspend_shuffle < max)
add(suspend_shuffle, resume_shuffle, pending, visited, c, i1, t1, t2, (char)(suspend_shuffle + 1), max);
} else if (resume_shuffle != null && min <= resume_shuffle && resume_shuffle <= max && min != max) {
if (min < resume_shuffle)
add(suspend_shuffle, resume_shuffle, pending, visited, c, i1, t1, t2, min, (char)(resume_shuffle - 1));
add(suspend_shuffle, resume_shuffle, pending, visited, c, i1, t1, t2, resume_shuffle, resume_shuffle);
if (resume_shuffle < max)
add(suspend_shuffle, resume_shuffle, pending, visited, c, i1, t1, t2, (char)(resume_shuffle + 1), max);
} else if (min < HIGH_SURROGATE_BEGIN && max >= HIGH_SURROGATE_BEGIN) {
add(suspend_shuffle, resume_shuffle, pending, visited, c, i1, t1, t2, min, (char)(HIGH_SURROGATE_BEGIN - 1));
add(suspend_shuffle, resume_shuffle, pending, visited, c, i1, t1, t2, HIGH_SURROGATE_BEGIN, max);
} else if (min <= HIGH_SURROGATE_END && max > HIGH_SURROGATE_END) {
add(suspend_shuffle, resume_shuffle, pending, visited, c, i1, t1, t2, min, HIGH_SURROGATE_END);
add(suspend_shuffle, resume_shuffle, pending, visited, c, i1, t1, t2, (char)(HIGH_SURROGATE_END + 1), max);
} else {
ShuffleConfiguration nc = new ShuffleConfiguration(c, i1, t1.to, t2.to, min);
if (suspend_shuffle != null && min == suspend_shuffle) {
nc.shuffle_suspended = true;
nc.suspended1 = i1;
} else if (resume_shuffle != null && min == resume_shuffle)
nc.shuffle_suspended = false;
if (min >= HIGH_SURROGATE_BEGIN && min <= HIGH_SURROGATE_BEGIN) {
nc.shuffle_suspended = true;
nc.suspended1 = i1;
nc.surrogate = true;
}
if (!visited.contains(nc)) {
pending.add(nc);
visited.add(nc);
}
}
}
static class ShuffleConfiguration {
ShuffleConfiguration prev;
State[] ca_states;
State a_state;
char min;
int hash;
boolean shuffle_suspended;
boolean surrogate;
int suspended1;
@SuppressWarnings("unused")
private ShuffleConfiguration() {}
ShuffleConfiguration(Collection ca, Automaton a) {
ca_states = new State[ca.size()];
int i = 0;
for (Automaton a1 : ca)
ca_states[i++] = a1.getInitialState();
a_state = a.getInitialState();
computeHash();
}
ShuffleConfiguration(ShuffleConfiguration c, int i1, State s1, char min) {
prev = c;
ca_states = c.ca_states.clone();
a_state = c.a_state;
ca_states[i1] = s1;
this.min = min;
computeHash();
}
ShuffleConfiguration(ShuffleConfiguration c, int i1, State s1, State s2, char min) {
prev = c;
ca_states = c.ca_states.clone();
a_state = c.a_state;
ca_states[i1] = s1;
a_state = s2;
this.min = min;
if (!surrogate) {
shuffle_suspended = c.shuffle_suspended;
suspended1 = c.suspended1;
}
computeHash();
}
@Override
public boolean equals(Object obj) {
if (obj instanceof ShuffleConfiguration) {
ShuffleConfiguration c = (ShuffleConfiguration)obj;
return shuffle_suspended == c.shuffle_suspended &&
surrogate == c.surrogate &&
suspended1 == c.suspended1 &&
Arrays.equals(ca_states, c.ca_states) &&
a_state == c.a_state;
}
return false;
}
@Override
public int hashCode() {
return hash;
}
private void computeHash() {
hash = 0;
for (int i = 0; i < ca_states.length; i++)
hash ^= ca_states[i].hashCode();
hash ^= a_state.hashCode() * 100;
if (shuffle_suspended || surrogate)
hash += suspended1;
}
}
}