| /* |
| * 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 |
| } |