blob: dc1def3896d391bd6ebac7bbd79f48b1290eed0f [file]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.sysds.runtime.iogen;
import org.apache.sysds.lops.Lop;
import org.apache.sysds.runtime.matrix.data.Pair;
import java.util.ArrayList;
import java.util.BitSet;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
public class MappingTrie {
private MappingTrieNode root;
private int keyLevel;
private boolean inALine;
private int windowSize = 100;
public MappingTrie() {
this.root = new MappingTrieNode(MappingTrieNode.Type.INNER);
this.keyLevel = 0;
this.inALine = true;
}
public void setInALine(boolean inALine) {
this.inALine = inALine;
}
public void insert(String word, int rowIndex) {
ArrayList<Integer> tmpList = new ArrayList<>();
tmpList.add(rowIndex);
this.insert(word, tmpList);
}
public void reverseInsert(String word, int rowIndex) {
ArrayList<Integer> tmpList = new ArrayList<>();
tmpList.add(rowIndex);
this.insert(new StringBuilder(word).reverse().toString(), tmpList);
}
public void insert(String word, ArrayList<Integer> rowIndexes) {
MappingTrieNode newNode;
if(root.getChildren().containsKey(word))
newNode = root.getChildren().get(word);
else
newNode = new MappingTrieNode();
newNode.addRowIndex(rowIndexes);
root.getChildren().put(word, newNode);
}
public MappingTrieNode getFistMultiChildNode(MappingTrieNode node) {
if(node.getNodeType() == MappingTrieNode.Type.INNER && node.getChildren().size() == 1) {
String nkey = node.getChildren().keySet().iterator().next();
if(node.getChildren().get(nkey).getRowIndexes().size() > 1)
return node;
}
if(node.getChildren().size() == 1 && node.getNodeType() != MappingTrieNode.Type.END)
return getFistMultiChildNode(node.getChildren().get(node.getChildren().keySet().iterator().next()));
else
return node;
}
public void insertKeys(ArrayList<String> keys) {
MappingTrieNode currentNode = root;
int index = 0;
for(String key : keys) {
if(currentNode.getChildren().containsKey(key)) {
currentNode = currentNode.getChildren().get(key);
index++;
}
else
break;
}
MappingTrieNode newNode;
for(int i = index; i < keys.size(); i++) {
newNode = new MappingTrieNode();
currentNode.getChildren().put(keys.get(i), newNode);
currentNode = newNode;
}
}
public Set<String> getAllSubStringsOfStringContainIntersect(String str, BitSet bitSet) {
HashSet<String> result = new HashSet<>();
StringBuilder sb = new StringBuilder();
for(int i = 0; i < bitSet.size(); i++) {
if(bitSet.get(i))
sb.append(str.charAt(i));
else if(sb.length() > 0) {
getAllSubStrings(result, sb);
sb = new StringBuilder();
}
}
if(sb.length() > 0) {
getAllSubStrings(result, sb);
}
return result;
}
private void getAllSubStrings(HashSet<String> result, StringBuilder sb) {
if(sb.length() == 1)
result.add(sb.toString());
else {
for(int j = 1; j <= Math.min(sb.length(), windowSize); j++) {
for(int k = 0; k <= sb.length() - j; k++) {
result.add(sb.substring(k, k + j));
}
}
}
}
public String getIntersectOfChildren(MappingTrieNode node) {
if(node.getNodeType() == MappingTrieNode.Type.END || node.getChildren().size() == 0)
return null;
else {
Set<String> keys = node.getChildren().keySet();
if(keys.size() == 1) {
String[] splitText = keys.iterator().next().split(Lop.OPERAND_DELIMITOR, -1);
String str = splitText[0];
if(str.length() == 0 && splitText.length > 1)
str = splitText[1];
return String.valueOf(str.charAt(0));
}
Set<String> newKeys = new HashSet<>();
for(String k : keys) {
String[] splitText = k.split(Lop.OPERAND_DELIMITOR, -1);
String str = splitText[0];
if(str.length() == 0 && splitText.length > 1)
str = splitText[1];
newKeys.add(str);
}
keys = newKeys;
boolean flag = false;
int maxKeyLength = 0;
Set<Character> intersections = null;
for(String k : keys) {
if(flag) {
intersections.retainAll(k.chars().mapToObj(c -> (char) c).collect(Collectors.toSet()));
}
else {
intersections = k.chars().mapToObj(c -> (char) c).collect(Collectors.toSet());
flag = true;
}
// set max length of key
maxKeyLength = Math.max(maxKeyLength, k.length());
}
if(intersections == null || intersections.size() == 0)
return null;
else {
Set<String> subStringIntersection = new HashSet<>();
boolean subStringIntersectionFlag = false;
for(String k : keys) {
BitSet bitSets = new BitSet(maxKeyLength);
int i = 0;
for(Character character : k.toCharArray()) {
if(intersections.contains(character))
bitSets.set(i);
i++;
}
if(subStringIntersectionFlag) {
subStringIntersection.retainAll(getAllSubStringsOfStringContainIntersect(k, bitSets));
}
else {
subStringIntersection = getAllSubStringsOfStringContainIntersect(k, bitSets);
subStringIntersectionFlag = true;
}
}
if(subStringIntersection.size() == 1) {
return subStringIntersection.iterator().next();
}
else {
ArrayList<String> sortedList = (ArrayList<String>) subStringIntersection.stream()
.sorted((o1, o2) -> o2.length() - o1.length()).collect(Collectors.toList());
for(String ssi : sortedList) {
if(keyLevel == 0 && inALine) {
boolean flagBest = true;
for(String k : keys) {
if(!k.startsWith(ssi)) {
flagBest = false;
break;
}
}
if(flagBest)
return ssi;
}
else {
int lastCount = 0;
for(String k : keys) {
int beginPos = 0;
int count = 0;
do {
int index = k.indexOf(ssi, beginPos);
if(index != -1) {
count++;
beginPos = index + ssi.length();
}
else
break;
}
while(true);
// if(count==1)
// lastCount = 1;
// else {
// lastCount = 0;
// break;
// }
if(lastCount != 0 && lastCount != count) {
lastCount = 0;
break;
}
else if(lastCount == 0)
lastCount = count;
}
if(lastCount != 0)
return ssi;
}
}
return null;
}
}
}
}
public MappingTrieNode getRoot() {
return root;
}
public boolean reConstruct() {
MappingTrieNode node = getFistMultiChildNode(root);
String intersect = getIntersectOfChildren(node);
// prune nodes if they don't have any intersect char
if(intersect == null) {
node.getChildren().clear();
node.setNodeType(MappingTrieNode.Type.END);
return false;
}
else {
MappingTrieNode.Type intersectNodeType = MappingTrieNode.Type.INNER;
MappingTrie intersectTrie = new MappingTrie();
ArrayList<Integer> intersectRowIndexes = new ArrayList<>();
for(String k : node.getChildren().keySet()) {
String key = k.substring(k.indexOf(intersect) + intersect.length());
if(key.length() > 0 && !key.equals(Lop.OPERAND_DELIMITOR)) {
intersectTrie.insert(key, node.getChildren().get(k).getRowIndexes());
intersectRowIndexes.addAll(node.getChildren().get(k).getRowIndexes());
}
else
intersectNodeType = MappingTrieNode.Type.END;
}
// clear the node children
node.getChildren().clear();
// create an IGNORE node type and add it to the tree
MappingTrieNode ignoreNode = new MappingTrieNode(MappingTrieNode.Type.IGNORE);
node.getChildren().put(null, ignoreNode);
// create and add intersection node
MappingTrieNode intersectionNode = new MappingTrieNode(intersectNodeType);
intersectionNode.setChildren(intersectTrie.root.getChildren());
intersectionNode.setRowIndexes(intersectRowIndexes);
ignoreNode.getChildren().put(intersect, intersectionNode);
keyLevel++;
return true;
}
}
public ArrayList<ArrayList<String>> getAllSequentialKeys() {
ArrayList<ArrayList<Pair<String, ArrayList<Integer>>>> result = new ArrayList<>();
getAllSequentialKeys(root, result, new ArrayList<>());
// orders
ArrayList<Pair<Integer, Integer>> indexOrder = new ArrayList<>();
int index = 0;
for(ArrayList<Pair<String, ArrayList<Integer>>> k : result) {
int level = 0;
for(Pair<String, ArrayList<Integer>> n : k) {
if(n.getKey() != null) {
if(level == keyLevel - 1 || keyLevel == 0) {
indexOrder.add(new Pair<>(index, n.getValue().size()));
break;
}
level++;
}
}
index++;
}
List<Pair<Integer, Integer>> sortedList = indexOrder.stream()
.sorted((o1, o2) -> o2.getValue().compareTo(o1.getValue())).collect(Collectors.toList());
ArrayList<ArrayList<String>> keys = new ArrayList<>();
for(Pair<Integer, Integer> p : sortedList) {
ArrayList<Pair<String, ArrayList<Integer>>> k = result.get(p.getKey());
ArrayList<String> kl = new ArrayList<>();
int level = 0;
for(Pair<String, ArrayList<Integer>> n : k)
if(n.getKey() != null) {
if(level < keyLevel || keyLevel == 0) {
String[] splitText = n.getKey().split(Lop.OPERAND_DELIMITOR, -1);
String str = splitText[0];
if(str.length() == 0 && splitText.length > 1)
str = splitText[1];
kl.add(str);
level++;
}
else
break;
}
keys.add(kl);
}
ArrayList<ArrayList<String>> distinctKeys = new ArrayList<>();
HashSet<Integer> markedIndexes = new HashSet<>();
ArrayList<String> selected;
for(int i = 0; i < keys.size(); i++) {
if(markedIndexes.contains(i))
continue;
else {
selected = keys.get(i);
markedIndexes.add(i);
distinctKeys.add(selected);
}
for(int j = i + 1; j < keys.size(); j++) {
if(!markedIndexes.contains(j)) {
boolean flag = true;
for(int k = 0; k < selected.size(); k++) {
if(!selected.get(k).equals(keys.get(j).get(k))) {
flag = false;
break;
}
}
if(flag) {
markedIndexes.add(j);
}
}
}
}
// revert list and values of list
for(ArrayList<String> l : distinctKeys) {
Collections.reverse(l);
for(int i = 0; i < l.size(); i++) {
l.set(i, new StringBuilder(l.get(i)).reverse().toString());
}
}
return distinctKeys;
}
private void getAllSequentialKeys(MappingTrieNode node,
ArrayList<ArrayList<Pair<String, ArrayList<Integer>>>> result,
ArrayList<Pair<String, ArrayList<Integer>>> nodeKeys) {
if(node.getNodeType() == MappingTrieNode.Type.END) {
result.add(nodeKeys);
nodeKeys = new ArrayList<>();
}
else {
for(String k : node.getChildren().keySet()) {
MappingTrieNode child = node.getChildren().get(k);
ArrayList<Pair<String, ArrayList<Integer>>> tmpKeys = new ArrayList<>();
tmpKeys.addAll(nodeKeys);
tmpKeys.add(new Pair<>(k, child.getRowIndexes()));
getAllSequentialKeys(child, result, tmpKeys);
}
}
}
public void setWindowSize(int windowSize) {
this.windowSize = windowSize;
}
}