package bgu.bio.ds.automata;
import java.util.Arrays;
import bgu.bio.util.alphabet.constrain.ConstrainedAlphabet;
import dk.brics.automaton.Automaton;
import dk.brics.automaton.RegExp;
import dk.brics.automaton.State;
import dk.brics.automaton.Transition;
/**
* The Class TransitionTable.
*/
public class TransitionTable {
/** The constrained alphabet to be used in the table. */
private ConstrainedAlphabet constrainedAlphabet;
/** The regular expression over {@link #constrainedAlphabet} letters. */
private String expression;
/** The transition table holding all the transitions over the regular expression {@link #expression}. the first dimension is the hashed char of
* from the {@link #constrainedAlphabet}, the second and third dimension are the origin, destination states (respectively)
* */
private int[][][] transitionTable; // [hash char][origin][destination]
/** The accepting states in the table. */
private int[] acceptingStates;
/** The starting state in the table. */
private int startingState;
/** The number of states the table (the depth of dimensions 2 and 3 in {@link #transitionTable}). */
private int numOfStates;
/** equal to {@link #constrainedAlphabet} number of letters. */
private int alphabetSize;
/**
* Instantiates a new transition table.
*
* @param expression the regular expression
* @param constrainedAlphabet the constrained alphabet to be used
*/
public TransitionTable(String expression, ConstrainedAlphabet constrainedAlphabet) {
this.constrainedAlphabet = constrainedAlphabet;
this.expression = expression;
this.alphabetSize = this.constrainedAlphabet.size();
build();
}
/**
* Builds the transition table data.
*/
public void build() {
RegExp regexp = new RegExp(this.expression);
Automaton automata = regexp.toAutomaton(true);
numOfStates = automata.getNumberOfStates();
//System.out.println("Number of states " + numOfStates);
State[] states = new State[numOfStates];
automata.getStates().toArray(states);
//Get Accepting states and starting state
this.startingState = indexOf(automata.getInitialState(), states);
this.buildAcceptingStates(states);
//initialize the transition table
this.transitionTable = new int[this.alphabetSize][numOfStates][numOfStates];
int[][] transitionTableLastDestination = new int[this.alphabetSize][numOfStates];
for(int origin=0;origin<numOfStates;origin++){
for(Transition t : states[origin].getTransitions()){
int destination = indexOf(t.getDest(), states);
/* Calculate transition characters */
/* transitions have a range that needs to be covered */
String transString1=transitionCharacters(t.getMin(), t.getMax());
/* Calculate the transition */
for (int idx1=0; idx1<transString1.length();idx1++){
char tchar=transString1.charAt(idx1);
// For [charIndex, destinationState, originState]
int lastDest = transitionTableLastDestination[constrainedAlphabet.encode(tchar)][origin];
this.transitionTable[constrainedAlphabet.encode(tchar)][origin][lastDest] = destination;
transitionTableLastDestination[constrainedAlphabet.encode(tchar)][origin]++;
}
}
}
//Trim all the arrays
trimDestinations(transitionTableLastDestination);
}
/**
* Trim destinations in the {@link #transitionTable}. trim the third dimension of the table according to the {@link #expression}.
*
* @param transitionTableLastDestination the transition table last destination
*/
private void trimDestinations(int[][] transitionTableLastDestination) {
for (int i=0;i<this.alphabetSize;i++)
{
for (int j=0;j<this.numOfStates;j++)
{
int[] newArr = new int[transitionTableLastDestination[i][j]];
for (int k = 0; k < newArr.length; k++) {
newArr[k] = this.transitionTable[i][j][k];
}
this.transitionTable[i][j] = newArr;
}
}
}
/**
* Builds the accepting states.
*
* @param states the states in the automaton
*/
private void buildAcceptingStates(State[] states) {
int[] acc = new int[numOfStates];
int pos = 0;
for (int i=0;i<states.length;i++)
{
if (states[i].isAccept()){
acc[pos] = i;
pos++;
}
}
this.acceptingStates = new int[pos];
for (int i=0;i<pos;i++)
{
this.acceptingStates[i] = acc[i];
}
}
/**
* Gets the transitions.
*
* @param c - a hashed character from the constraint alphabet
*
* @return the array of transitions ([origin][destination]) in the automaton that have the character c.
*/
public int[][] getTransitions(int c)
{
return this.transitionTable[c];
}
/**
* Gets the accepting states id's in the table.
*
* @return the accepting states id's
*/
public int[] getAcceptingStates()
{
return this.acceptingStates;
}
/**
* Gets the starting state.
*
* @return the starting state
*/
public int getStartingState()
{
return this.startingState;
}
/**
* Index of state in the array of states.
*
* @param state the state
* @param stateArray the state array
*
* @return the int
*/
protected final int indexOf(State state,State[] stateArray)
{
for (int i=0;i<stateArray.length;i++)
{
if (state.equals(stateArray[i]))
return i;
}
return -1;
}
/**
* Transition characters.
*
* @param transitionCharacterMin2 the transition character min2
* @param transitionCharacterMax2 the transition character max2
*
* @return the string
*/
protected final String transitionCharacters(char transitionCharacterMin2, char transitionCharacterMax2){
String transString="";
/* Deal with "don't care" transitions */
if (transitionCharacterMin2=='\u0000' || transitionCharacterMax2=='\uffff')
/* Deal with "don't care" transitions */
transString=this.constrainedAlphabet.lettersToString();
else
/* Deal with normal transitions */
for (char tchar2=transitionCharacterMin2;tchar2<=transitionCharacterMax2;tchar2++)
transString=transString+tchar2;
return transString;
}
/* (non-Javadoc)
* @see java.lang.Object#toString()
*/
@Override
public String toString() {
StringBuilder builder = new StringBuilder();
for (int i=0;i<this.alphabetSize;i++)
{
for (int j=0;j<this.numOfStates;j++)
{
int len = this.transitionTable[i][j].length;
if (len>0){
builder.append("<"+this.constrainedAlphabet.decode(i)+","+j+"> [");
}
for (int x=0;x<len;x++)
{
builder.append(" " + this.transitionTable[i][j][x]);
}
if (len>0){
builder.append("]\n");
}
}
}
builder.append("Starting state is: " + this.startingState + "\n");
builder.append("Accepting states are: " + Arrays.toString(this.acceptingStates) + "\n");
return builder.toString();
}
/**
* Gets the number of states in the automaton.
*
* @return the number of states in the automaton.
*/
public int getNumOfStates() {
return numOfStates;
}
/**
* Gets the alphabet size.
*
* @return the alphabet size
*/
public int getAlphabetSize() {
return alphabetSize;
}
}