/* * 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 val origin: Node } fun solveGraphForClassVsInterface( elements: List, requiredInterfaces: Collection, requiredClasses: Collection, ): List { val elementMapping = ElementMapping(elements) val solution = solve2sat(elements, elementMapping) processRequirementsFromConfig(solution, elementMapping, requiredInterfaces, requiredClasses) return solution } private class ElementMapping(val elements: Collection) { private val varToElements: Map = elements.mapIndexed { index, element -> 2 * index to element.origin }.toMap() + elements.mapIndexed { index, element -> 2 * index + 1 to element }.toMap() private val elementsToVar: Map = 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, elementMapping: ElementMapping, requiredInterfaces: Collection, requiredClasses: Collection, ) { 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() 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, elementsToVar: ElementMapping): MutableList { val (g, gt) = buildGraphs(elements, elementsToVar) val used = g.indices.mapTo(mutableListOf()) { false } val order = mutableListOf() 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, elementMapping: ElementMapping): Pair>, List>> { val g = (1..elementMapping.size * 2).map { mutableListOf() } val gt = (1..elementMapping.size * 2).map { mutableListOf() } 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 }