package com.googlecode.gaal.vis;
import java.util.Arrays;
import java.util.Iterator;
import java.util.Map;
import java.util.SortedMap;
import java.util.TreeMap;
import com.googlecode.gaal.data.api.IntSequence;
import com.googlecode.gaal.data.api.SymbolTable;
import com.googlecode.gaal.suffix.api.EmbeddedSuffixTree.EmbeddedInterval;
import com.googlecode.gaal.suffix.api.EnhancedSuffixArray;
import com.googlecode.gaal.suffix.api.IntervalTree;
import com.googlecode.gaal.suffix.api.IntervalTree.Interval;
import com.googlecode.gaal.suffix.api.LinearizedSuffixTree;
import com.googlecode.gaal.suffix.api.LinearizedSuffixTree.BinaryInterval;
import com.googlecode.gaal.suffix.api.SuffixArray;
import com.googlecode.gaal.suffix.api.SuffixTree.Node;
import com.googlecode.gaal.vis.api.Drawing;
import com.googlecode.gaal.vis.api.NodeStyle;
import com.googlecode.gaal.vis.impl.TikzConstants;
public class TableVisualizer<S> {
private final IntSequence sequence;
private final SymbolTable<S> symbolTable;
private Drawing drawing;
private Map<Interval, Integer> depthNodes;
private int[] usedCells;
private int maxDepth;
public TableVisualizer(IntSequence sequence, SymbolTable<S> symbolTable) {
this.sequence = sequence;
this.symbolTable = symbolTable;
}
public void visualizeSequence(Drawing drawing) {
visualizeSubsequence(drawing, -1, -1);
}
public void visualizeSubsequence(Drawing drawing, int... indices) {
int index = 0;
for (int i = 0; i < sequence.size(); i++) {
if (indices[index] == i) {
drawing.drawCell(i, 0, symbolTable.toToken(sequence.get(i)).toString(), Integer.toString(i),
TikzConstants.BLUE_CELL);
if (index < indices.length - 1) {
index++;
}
} else
drawing.drawCell(i, 0, symbolTable.toToken(sequence.get(i)).toString(), Integer.toString(i),
TikzConstants.LIGHT_BLUE_CELL);
}
}
public void visualizeComplexSubsequence(Drawing drawing, int... indices) {
int index = 0;
for (int i = 0; i < sequence.size(); i++) {
if (indices[index] == i) {
drawing.drawCell(i, 0, symbolTable.toToken(sequence.get(i)).toString(), Integer.toString(i),
TikzConstants.CELL_STYLES[indices[index + 1]]);
if (index < indices.length - 2) {
index += 2;
}
} else
drawing.drawCell(i, 0, symbolTable.toToken(sequence.get(i)).toString(), Integer.toString(i),
TikzConstants.LIGHT_BLUE_CELL);
}
}
public void visualizeSuffixes(Drawing drawing) {
for (int i = 0; i < sequence.size(); i++) {
IntSequence subSequence = sequence.subSequence(i, sequence.size());
drawing.drawCell(0, i, Integer.toString(i), TikzConstants.RED_CELL);
for (int j = 0; j < subSequence.size(); j++) {
drawing.drawCell(j + 1, i, symbolTable.toToken(subSequence.get(j)).toString(), Integer.toString(i + j),
TikzConstants.LIGHT_BLUE_CELL);
}
}
}
public void visualizeEmbeddedSuffixes(Drawing drawing, SuffixArray sa, Interval interval, int windowSize) {
IntSequence indices = interval.indices();
int lcp = interval.lcp();
for (int i = 0; i < interval.size(); i++) {
int start = indices.get(i);
drawing.drawCell(0, i, Integer.toString(start), TikzConstants.RED_CELL);
IntSequence subSequence = sequence.subSequence(start, sequence.size());
for (int k = 0; k < subSequence.size(); k++) {
if (k < lcp) {
drawing.drawCell(k + 1, i, symbolTable.toToken(subSequence.get(k)).toString(),
Integer.toString(start + k), TikzConstants.LIGHT_BLUE_CELL);
} else {
drawing.drawCell(k + 1, i, symbolTable.toToken(subSequence.get(k)).toString(),
Integer.toString(start + k), TikzConstants.BLUE_CELL);
}
}
}
}
public void visualizeWindow(Drawing drawing, SuffixArray sa, Interval interval, int windowSize) {
IntSequence indices = interval.indices();
int lcp = interval.lcp();
for (int i = 0; i < interval.size(); i++) {
int start = indices.get(i) + lcp;
drawing.drawCell(0, i, Integer.toString(start), TikzConstants.RED_CELL);
IntSequence subSequence = sequence.subSequence(start, sequence.size());
for (int k = 0; k < subSequence.size(); k++) {
if (k < windowSize) {
drawing.drawCell(k + 1, i, symbolTable.toToken(subSequence.get(k)).toString(),
Integer.toString(start + k), TikzConstants.GREEN_CELL);
} else {
drawing.drawCell(k + 1, i, symbolTable.toToken(subSequence.get(k)).toString(),
Integer.toString(start + k), TikzConstants.LIGHT_BLUE_CELL);
}
}
}
}
public void visualizeEmbeddedSuffixesInWindow(Drawing drawing, SuffixArray sa, Interval interval, int windowSize) {
IntSequence indices = interval.indices();
int lcp = interval.lcp();
int[] inverseSuffixTable = sa.getInverseSuffixTable();
SortedMap<Integer, Integer> embeddedSuffixTableIndices = new TreeMap<Integer, Integer>();
int counter = 0;
for (int i = 0; i < interval.size(); i++) {
int start = indices.get(i) + lcp;
for (int j = start; j < start + windowSize && j < sequence.size(); j++) {
drawing.drawCell(0, counter, Integer.toString(j), TikzConstants.RED_CELL);
drawing.drawCell(1, counter, Integer.toString(inverseSuffixTable[j]), TikzConstants.GREEN_CELL);
IntSequence subSequence = sequence.subSequence(j, sequence.size());
Integer startIndex = embeddedSuffixTableIndices.get(inverseSuffixTable[j]);
if (startIndex == null || startIndex < start) {
embeddedSuffixTableIndices.put(inverseSuffixTable[j], start);
}
for (int k = 0; k < subSequence.size(); k++) {
drawing.drawCell(k + 2, counter, symbolTable.toToken(subSequence.get(k)).toString(),
Integer.toString(j + k), TikzConstants.LIGHT_BLUE_CELL);
}
counter++;
}
}
}
public void visualizeEmbeddedSuffixTable(Drawing drawing, SuffixArray sa) {
int[] inverseSuffixTable = sa.getInverseSuffixTable();
int[] suffixTable = sa.getSuffixTable();
for (int i = 0; i < suffixTable.length; i++) {
IntSequence subSequence = sequence.subSequence(suffixTable[i], sequence.size());
drawing.drawCell(0, i, String.format("%d", suffixTable[i]), TikzConstants.RED_CELL);
drawing.drawCell(1, i, Integer.toString(inverseSuffixTable[suffixTable[i]]), TikzConstants.GREEN_CELL);
for (int j = 0; j < subSequence.size(); j++) {
drawing.drawCell(j + 2, i, symbolTable.toToken(subSequence.get(j)).toString(),
Integer.toString(suffixTable[i] + j), TikzConstants.LIGHT_BLUE_CELL);
}
}
}
public void visualizeLcpInSuffixTable(Drawing drawing, SuffixArray sa) {
int[] suffixTable = sa.getSuffixTable();
int[] lcpTable = sa.getLcpTable();
for (int i = 0; i < suffixTable.length; i++) {
IntSequence subSequence = sequence.subSequence(suffixTable[i], sequence.size());
int lcp = lcpTable[i];
drawing.drawCell(0, i, Integer.toString(lcp), TikzConstants.GREEN_CELL);
NodeStyle style = null;
if (lcp > 0)
style = TikzConstants.CELL_STYLES[lcp];
for (int j = 0; j < subSequence.size(); j++) {
NodeStyle cellStyle;
if (j < lcp) {
cellStyle = style;
} else {
cellStyle = TikzConstants.LIGHT_BLUE_CELL;
}
drawing.drawCell(j + 1, i, symbolTable.toToken(subSequence.get(j)).toString(),
Integer.toString(suffixTable[i] + j), cellStyle);
}
}
}
public void visualizeIndexTable(Drawing drawing, int length, String label, int labelLength, NodeStyle style) {
for (int i = 0; i < length; i++) {
drawing.drawCell(i, 0, Integer.toString(i), style);
}
if (labelLength != 0)
drawing.drawBox(label, labelLength);
else
drawing.drawBox(label);
}
public void visualizeTable(Drawing drawing, int[] table, String label, int labelLength, NodeStyle style) {
for (int i = 0; i < table.length; i++) {
drawing.drawCell(i, 0, Integer.toString(table[i]), style);
}
if (labelLength != 0)
drawing.drawBox(label, labelLength);
else
drawing.drawBox(label);
}
public void visualizeTable(Drawing drawing, String[] table, String label, Integer labelLength, NodeStyle style) {
for (int i = 0; i < table.length; i++) {
drawing.drawCell(i, 0, table[i], style);
}
if (labelLength != 0)
drawing.drawBox(label, labelLength);
else
drawing.drawBox(label);
}
public void visualizeSuffixTable(Drawing drawing, SuffixArray sa) {
int[] suffixTable = sa.getSuffixTable();
for (int i = 0; i < suffixTable.length; i++) {
IntSequence subSequence = sequence.subSequence(suffixTable[i], sequence.size());
drawing.drawCell(0, i, String.format("%d", suffixTable[i]), TikzConstants.RED_CELL);
for (int j = 0; j < subSequence.size(); j++) {
drawing.drawCell(j + 1, i, symbolTable.toToken(subSequence.get(j)).toString(),
Integer.toString(suffixTable[i] + j), TikzConstants.LIGHT_BLUE_CELL);
}
}
}
public void visualizeInterval(Drawing drawing, Interval interval, int length, int windowSize) {
int[] styleArray = new int[sequence.size()];
Arrays.fill(styleArray, -1);
markInterval(interval, null, styleArray, length, windowSize);
NodeStyle style = null;
for (int i = 0; i < styleArray.length; i++) {
if (styleArray[i] != -1) {
style = TikzConstants.CELL_STYLES[styleArray[i]];
} else {
style = TikzConstants.LIGHT_BLUE_CELL;
}
drawing.drawCell(i, 0, symbolTable.toToken(sequence.get(i)).toString(), Integer.toString(i), style);
}
}
private void markInterval(Interval interval, EmbeddedInterval embeddedInterval, int[] styleArray, int length,
int windowSize) {
IntSequence indices = interval.indices();
int lcp = interval.lcp();
if (length < 1) {
length = lcp;
}
for (int i = 0; i < indices.size(); i++) {
int style = i;
if (embeddedInterval != null) {
IntSequence embeddedIndices = embeddedInterval.indices();
int minDistance = windowSize + 1;
for (int k = 0; k < embeddedIndices.size(); k++) {
int distance = embeddedIndices.get(k) - indices.get(i) - lcp;
if (distance >= 0 && distance < minDistance) {
minDistance = distance;
}
}
if (minDistance > windowSize) {
continue;
}
style = styleArray[indices.get(i) + lcp + minDistance];
}
for (int j = 0; j < length; j++) {
styleArray[indices.get(i) + j] = style;
}
}
if (interval instanceof EmbeddedInterval) {
embeddedInterval = (EmbeddedInterval) interval;
markInterval(embeddedInterval.getEmbeddingInterval(), embeddedInterval, styleArray, -1, windowSize);
}
}
public <E extends Interval, T extends SuffixArray & IntervalTree<E>> int[] visualizeChildTable(Drawing drawing,
T tree, Map<Interval, Integer> depthNodes, int[] totalUsedCells, int maxDepth) {
if (depthNodes.size() != 0) {
this.drawing = drawing;
this.depthNodes = depthNodes;
this.maxDepth = maxDepth;
this.usedCells = new int[sequence.size()];
// System.out.println(depthNodes);
if (tree instanceof EnhancedSuffixArray) {
visualizeChild((EnhancedSuffixArray) tree, ((EnhancedSuffixArray) tree).top(), 1);
} else if (tree instanceof LinearizedSuffixTree) {
visualizeChild((LinearizedSuffixTree) tree, ((LinearizedSuffixTree) tree).top(), 1);
}
for (int i = 0; i < usedCells.length; i++) {
if (usedCells[i] == 0)
drawing.drawCell(i, 0, "", TikzConstants.LIGHT_BLUE_CELL);
else
totalUsedCells[i] = usedCells[i];
}
} else {
int[] childTable = null;
if (tree instanceof EnhancedSuffixArray) {
childTable = ((EnhancedSuffixArray) tree).getChildTable();
} else if (tree instanceof LinearizedSuffixTree) {
childTable = ((LinearizedSuffixTree) tree).getChildTable();
}
for (int i = 0; i < childTable.length; i++) {
if (i == childTable.length - 1) {
drawing.drawCell(i, 0, "", TikzConstants.LIGHT_BLUE_CELL);
} else {
if (totalUsedCells[i] == 1) {
drawing.drawCell(i, 0, Integer.toString(childTable[i]), TikzConstants.RED_CELL);
} else {
drawing.drawCell(i, 0, Integer.toString(childTable[i]), TikzConstants.GREEN_CELL);
}
}
}
}
return totalUsedCells;
}
private void visualizeChild(EnhancedSuffixArray esa, Node node, int depth) {
if (maxDepth == 0) {
visualizeCell(esa, esa.top(), true, false);
} else {
Iterator<Node> iterator = node.childIterator();
boolean isFirst = true;
while (iterator.hasNext()) {
node = iterator.next();
if (depth == maxDepth) {
// System.err.println("depth=" + depth + " " + node + " "
// + isFirst + " " + !iterator.hasNext());
if (iterator.hasNext()) {
visualizeCell(esa, node, false, isFirst);
} else {
visualizeCell(esa, node, true, isFirst);
}
} else if (depth < maxDepth) {
visualizeChild(esa, node, depth + 1);
}
if (isFirst)
isFirst = false;
}
}
}
private void visualizeCell(EnhancedSuffixArray esa, Interval node, boolean isLast, boolean isFirst) {
int child = -1;
int next = -1;
if (!node.isTerminal()) {
if (isLast) {
child = node.left();
} else if (isFirst) {
child = node.right();
} else {
child = node.right();
}
}
if (!isLast && !isFirst) {
next = node.left();
}
if (next != -1) {
int number = drawing.drawCell(next, 0, Integer.toString(esa.getChildTable()[next]), TikzConstants.RED_CELL);
usedCells[next] = 1;
String anchor = "south";
if (node.right() > next)
anchor = "west";
// System.out.println(node);
drawing.drawEdge(number, "north", depthNodes.get(node), anchor, TikzConstants.RED_DOTTED_EDGE);
}
if (child != -1) {
int number = drawing.drawCell(child, 0, Integer.toString(esa.getChildTable()[child]),
TikzConstants.GREEN_CELL);
usedCells[child] = 2;
String anchor = "south";
if (node.left() < child)
anchor = "east";
else if (node.right() > child)
anchor = "west";
drawing.drawEdge(number, "north", depthNodes.get(node), anchor, TikzConstants.GREEN_DOTTED_EDGE);
}
}
private void visualizeChild(LinearizedSuffixTree lst, BinaryInterval node, int depth) {
if (maxDepth == 0) {
visualizeCell(lst, lst.top(), false);
} else {
if (!node.isTerminal()) {
BinaryInterval left = node.leftChild();
BinaryInterval right = node.rightChild();
if (depth == maxDepth) {
visualizeCell(lst, left, true);
visualizeCell(lst, right, false);
} else if (depth < maxDepth) {
visualizeChild(lst, left, depth + 1);
visualizeChild(lst, right, depth + 1);
}
}
}
}
private void visualizeCell(LinearizedSuffixTree lst, BinaryInterval node, boolean isFirst) {
int child;
if (isFirst) {
child = node.right();
} else {
child = node.left();
}
if (!node.isTerminal()) {
int number = drawing.drawCell(child, 0, Integer.toString(lst.getChildTable()[child]),
TikzConstants.GREEN_CELL);
usedCells[child] = 2;
String anchor = "south";
if (node.left() < child)
anchor = "east";
else if (node.right() > child)
anchor = "west";
drawing.drawEdge(number, "north", depthNodes.get(node), anchor, TikzConstants.GREEN_DOTTED_EDGE);
}
}
public <E extends Interval, T extends SuffixArray & IntervalTree<E>> void visualizeSuffixIntervals(Drawing drawing,
T sa, Map<Interval, Integer> styleMap) {
int[] suffixTable = sa.getSuffixTable();
Iterator<? extends Interval> iterator = sa.preorderIterator();
boolean[][] usedCells = new boolean[suffixTable.length][sequence.size()];
while (iterator.hasNext()) {
Interval node = iterator.next();
Integer style = styleMap.get(node);
if (style != null) {
NodeStyle nodeStyle = TikzConstants.CELL_STYLES[style];
for (int i = node.left(); i <= node.right(); i++) {
IntSequence subSequence = sequence.subSequence(suffixTable[i], suffixTable[i] + node.lcp());
for (int j = 0; j < subSequence.size(); j++) {
if (!usedCells[i][j]) {
usedCells[i][j] = true;
drawing.drawCell(i, j, symbolTable.toToken(subSequence.get(j)).toString(), nodeStyle);
}
}
}
}
}
for (int i = 0; i < suffixTable.length; i++) {
IntSequence subSequence = sequence.subSequence(suffixTable[i], sequence.size());
for (int j = 0; j < subSequence.size(); j++) {
if (!usedCells[i][j]) {
drawing.drawCell(i, j, symbolTable.toToken(subSequence.get(j)).toString(),
TikzConstants.LIGHT_BLUE_CELL);
}
}
}
}
}