FIR tree gen: extract interface/class solver to generators
This commit is contained in:
@@ -0,0 +1,165 @@
|
||||
/*
|
||||
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.generators.util
|
||||
|
||||
/*
|
||||
* Assume that we have element `E` with parents `P1, P2`
|
||||
*
|
||||
* Introduce variables E, P1, P2. If variable `X` is true then `X` is class, otherwise it is interface
|
||||
*
|
||||
* Build 2SAT function for it: (E || !P1) && (E || !P2) && (!P1 || !P2)
|
||||
* Simple explanation:
|
||||
* if `P1` is a class then `E` also should be a class
|
||||
* if `P1` is a class then `P2` can not be a class (because both of them a parents of E`
|
||||
*/
|
||||
|
||||
interface Node {
|
||||
val parents: List<Node>
|
||||
val origin: Node
|
||||
}
|
||||
|
||||
fun solveGraphForClassVsInterface(
|
||||
elements: List<Node>, requiredInterfaces: Collection<Node>, requiredClasses: Collection<Node>,
|
||||
): List<Boolean> {
|
||||
val elementMapping = ElementMapping(elements)
|
||||
val solution = solve2sat(elements, elementMapping)
|
||||
processRequirementsFromConfig(solution, elementMapping, requiredInterfaces, requiredClasses)
|
||||
return solution
|
||||
}
|
||||
|
||||
private class ElementMapping(val elements: Collection<Node>) {
|
||||
private val varToElements: Map<Int, Node> = elements.mapIndexed { index, element -> 2 * index to element.origin }.toMap() +
|
||||
elements.mapIndexed { index, element -> 2 * index + 1 to element }.toMap()
|
||||
private val elementsToVar: Map<Node, Int> = elements.mapIndexed { index, element -> element.origin to index }.toMap()
|
||||
|
||||
operator fun get(element: Node): Int = elementsToVar.getValue(element)
|
||||
operator fun get(index: Int): Node = varToElements.getValue(index)
|
||||
|
||||
val size: Int = elements.size
|
||||
}
|
||||
|
||||
private fun processRequirementsFromConfig(
|
||||
solution: MutableList<Boolean>,
|
||||
elementMapping: ElementMapping,
|
||||
requiredInterfaces: Collection<Node>,
|
||||
requiredClasses: Collection<Node>,
|
||||
) {
|
||||
fun forceParentsToBeInterfaces(element: Node) {
|
||||
val origin = element.origin
|
||||
val index = elementMapping[origin]
|
||||
if (!solution[index]) return
|
||||
solution[index] = false
|
||||
origin.parents.forEach { forceParentsToBeInterfaces(it) }
|
||||
}
|
||||
|
||||
fun forceInheritorsToBeClasses(element: Node) {
|
||||
val queue = ArrayDeque<Node>()
|
||||
queue.add(element)
|
||||
while (queue.isNotEmpty()) {
|
||||
val e = queue.removeFirst().origin
|
||||
val index = elementMapping[e]
|
||||
if (solution[index]) continue
|
||||
solution[index] = true
|
||||
for (inheritor in elementMapping.elements) {
|
||||
if (e in inheritor.parents.map { it.origin }) {
|
||||
queue.add(inheritor)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
requiredInterfaces.forEach(::forceParentsToBeInterfaces)
|
||||
requiredClasses.forEach(::forceInheritorsToBeClasses)
|
||||
}
|
||||
|
||||
private fun solve2sat(elements: Collection<Node>, elementsToVar: ElementMapping): MutableList<Boolean> {
|
||||
val (g, gt) = buildGraphs(elements, elementsToVar)
|
||||
|
||||
val used = g.indices.mapTo(mutableListOf()) { false }
|
||||
val order = mutableListOf<Int>()
|
||||
val comp = g.indices.mapTo(mutableListOf()) { -1 }
|
||||
val n = g.size
|
||||
|
||||
fun dfs1(v: Int) {
|
||||
used[v] = true
|
||||
for (to in g[v]) {
|
||||
if (!used[to]) {
|
||||
dfs1(to)
|
||||
}
|
||||
}
|
||||
order += v
|
||||
}
|
||||
|
||||
fun dfs2(v: Int, cl: Int) {
|
||||
comp[v] = cl
|
||||
for (to in gt[v]) {
|
||||
if (comp[to] == -1) {
|
||||
dfs2(to, cl)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (i in g.indices) {
|
||||
if (!used[i]) {
|
||||
dfs1(i)
|
||||
}
|
||||
}
|
||||
|
||||
var j = 0
|
||||
for (i in g.indices) {
|
||||
val v = order[n - i - 1]
|
||||
if (comp[v] == -1) {
|
||||
dfs2(v, j++)
|
||||
}
|
||||
}
|
||||
|
||||
val res = (1..elements.size).mapTo(mutableListOf()) { false }
|
||||
|
||||
for (i in 0 until n step 2) {
|
||||
if (comp[i] == comp[i + 1]) {
|
||||
throw IllegalStateException("Somehow there is no solution. Please contact with @dmitriy.novozhilov")
|
||||
}
|
||||
res[i / 2] = comp[i] > comp[i + 1]
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
|
||||
private fun buildGraphs(elements: Collection<Node>, elementMapping: ElementMapping): Pair<List<List<Int>>, List<List<Int>>> {
|
||||
val g = (1..elementMapping.size * 2).map { mutableListOf<Int>() }
|
||||
val gt = (1..elementMapping.size * 2).map { mutableListOf<Int>() }
|
||||
|
||||
fun Int.direct(): Int = this
|
||||
fun Int.invert(): Int = this + 1
|
||||
|
||||
fun extractIndex(element: Node) = elementMapping[element] * 2
|
||||
|
||||
for (element in elements) {
|
||||
val elementVar = extractIndex(element)
|
||||
for (parent in element.parents) {
|
||||
val parentVar = extractIndex(parent.origin)
|
||||
// parent -> element
|
||||
g[parentVar.direct()] += elementVar.direct()
|
||||
g[elementVar.invert()] += parentVar.invert()
|
||||
}
|
||||
for (i in 0 until element.parents.size) {
|
||||
for (j in i + 1 until element.parents.size) {
|
||||
val firstParentVar = extractIndex(element.parents[i].origin)
|
||||
val secondParentVar = extractIndex(element.parents[j].origin)
|
||||
// firstParent -> !secondParent
|
||||
g[firstParentVar.direct()] += secondParentVar.invert()
|
||||
g[secondParentVar.direct()] += firstParentVar.invert()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (from in g.indices) {
|
||||
for (to in g[from]) {
|
||||
gt[to] += from
|
||||
}
|
||||
}
|
||||
return g to gt
|
||||
}
|
||||
Reference in New Issue
Block a user