package org.jetbrains.ruby.codeInsight.types.signature;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.ruby.codeInsight.types.signature.contractTransition.ContractTransition;
import org.jetbrains.ruby.codeInsight.types.signature.contractTransition.ReferenceContractTransition;
import org.jetbrains.ruby.codeInsight.types.signature.contractTransition.TypedContractTransition;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
public class RSignatureContract implements SignatureContract {
private int myNumberOfCalls = 0;
public boolean locked = false;
@NotNull
private final RSignatureContractNode startContractNode;
@NotNull
private final List<ParameterInfo> myArgsInfo;
private final List<List<RSignatureContractNode>> levels;
private final RSignatureContractNode termNode;
private RSignatureContractNode createNodeAndAddToLevels(Integer index) {
RSignatureContractNode newNode;
if (index < getSize())
newNode = new RSignatureContractNode(RSignatureContractNode.ContractNodeType.argNode);
else
newNode = new RSignatureContractNode(RSignatureContractNode.ContractNodeType.returnNode);
while (levels.size() <= index)
levels.add(new ArrayList<>());
levels.get(index).add(newNode);
return newNode;
}
public List<ParameterInfo> getParamInfoList() {
return myArgsInfo;
}
private RSignatureContractNode getTermNode() {
return termNode;
}
public RSignatureContract(RSignature signature) {
levels = new ArrayList<>();
termNode = new RSignatureContractNode(RSignatureContractNode.ContractNodeType.returnTypeNode);
myArgsInfo = signature.getArgsInfo();
startContractNode = this.createNodeAndAddToLevels(0);
addRSignature(signature);
}
public RSignatureContract(@NotNull List<ParameterInfo> argsInfo,
@NotNull RSignatureContractNode startContractNode,
@NotNull RSignatureContractNode termNode,
@NotNull List<List<RSignatureContractNode>> levels) {
this.startContractNode = startContractNode;
myArgsInfo = argsInfo;
this.levels = levels;
this.termNode = termNode;
// TODO recalculate mask
}
@NotNull
@Override
public RSignatureContractNode getStartNode() {
return startContractNode;
}
@NotNull
@Override
public List<ParameterInfo> getArgsInfo() {
return myArgsInfo;
}
private int getSize() {
return myArgsInfo.size();
}
public int getNodeCount() {
return levels.stream().map(List::size).reduce(0, (a, b) -> a + b);
}
@NotNull
ContractTransition calculateTransition(@NotNull List<String> argTypes, int argIndex, String type) {
final int mask = getNewMask(argTypes, argIndex, type);
if (mask > 0)
return new ReferenceContractTransition(mask);
else
return new TypedContractTransition(type);
}
public void addRSignature(RSignature signature) {
myNumberOfCalls++;
RSignatureContractNode currNode = startContractNode;
String returnType = signature.getReturnTypeName();
RSignatureContractNode termNode = getTermNode();
final List<String> argsTypes = signature.getArgsTypes();
for (int argIndex = 0; argIndex < argsTypes.size(); argIndex++) {
final String type = argsTypes.get(argIndex);
final ContractTransition transition = calculateTransition(signature.getArgsTypes(), argIndex, type);
if (!currNode.containsKey(transition)) {
final RSignatureContractNode newNode = createNodeAndAddToLevels(argIndex + 1);
currNode.addLink(transition, newNode);
currNode = newNode;
} else {
currNode = currNode.goByTransition(transition);
}
}
final ContractTransition transition = calculateTransition(signature.getArgsTypes(), signature.getArgsTypes().size(), returnType);
currNode.addLink(transition, termNode);
}
private int getNewMask(@NotNull List<String> argsTypes, int argIndex, @NotNull String type) {
int tempMask = 0;
for (int i = argIndex - 1; i >= 0; i--) {
tempMask <<= 1;
if (argsTypes.get(i).equals(type)) {
tempMask |= 1;
}
}
return tempMask;
}
public void minimization() {
int numberOfLevels = levels.size();
for (int i = numberOfLevels - 1; i > 0; i--) {
List<RSignatureContractNode> level = levels.get(i);
HashMap<RSignatureContractNode, RSignatureContractNode> representatives = new HashMap<>();
List<RSignatureContractNode> uselessVertices = new ArrayList<>();
for (RSignatureContractNode node : level) {
representatives.put(node, node);
}
for (int v1 = 0; v1 < level.size(); v1++) {
for (int v2 = v1 + 1; v2 < level.size(); v2++) {
RSignatureContractNode vertex1 = level.get(v1);
RSignatureContractNode vertex2 = level.get(v2);
boolean isSame = vertex1.getTransitions().size() == vertex2.getTransitions().size();
for (ContractTransition transition : vertex1.getTransitionKeys()) {
if (!vertex2.containsKey(transition) || vertex1.goByTransition(transition) != vertex2.goByTransition(transition)) {
isSame = false;
}
}
if (isSame) {
RSignatureContractNode vertex1presenter = representatives.get(vertex1);
representatives.put(vertex2, vertex1presenter);
uselessVertices.add(vertex2);
}
}
}
List<RSignatureContractNode> prevLevel = levels.get(i - 1);
if (uselessVertices.size() > 0) {
for (RSignatureContractNode node : prevLevel) {
for (ContractTransition transition : node.getTransitionKeys()) {
RSignatureContractNode child = node.goByTransition(transition);
node.addLink(transition, representatives.get(child));
}
}
}
level.removeAll(uselessVertices);
}
}
public List<List<RSignatureContractNode>> getLevels() {
return levels;
}
public int getNumberOfCalls() {
return myNumberOfCalls;
}
void mergeDFS(RSignatureContractNode node1, RSignatureContractNode node2) {
for (ContractTransition transition : node2.getTransitionKeys()) {
if (node1.getTransitionKeys().contains(transition)) {
mergeDFS(node1.goByTransition(transition), node2.goByTransition(transition));
} else {
node1.addLink(transition, node2.goByTransition(transition));
}
}
}
public void merge(RSignatureContract additive) {
mergeDFS(startContractNode, additive.getStartNode());
minimization();
}
}