loggersoft.kotlin.utils.graph.AnalystImpl.kt Maven / Gradle / Ivy
/*
* Copyright (C) 2018 Alexander Kornilov ([email protected])
*
* Licensed 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 loggersoft.kotlin.utils.graph
import loggersoft.kotlin.utils.*
import java.util.*
import kotlin.NoSuchElementException
@Suppress("NOTHING_TO_INLINE")
internal class AnalystImpl(override val graph: Graph) : Analyst {
override fun sortTopological(): List> {
if (!graph.isOriented || graph.isEmpty) return Collections.emptyList>()
val graph = this.graph.cloneAsMutable()
val result = mutableListOf>()
while(!graph.isEmpty) {
val step = graph.vertexes.filter { it.endsIn.isEmpty() }.map { it.value }.toSet()
if (step.isEmpty()) return Collections.emptyList>()
step.forEach(graph::removeVertex)
result += step
}
return result
}
override fun hasPath(startFrom: V, endWith: V): Boolean {
val vertexes = vertexesList
return hasPath(vertexes.vertexIndex(startFrom), vertexes.vertexIndex(endWith))
}
override fun getPath(startFrom: V, endWith: V): Path? {
val vertexes = vertexesList
val startIndex = vertexes.vertexIndex(startFrom)
val endIndex = vertexes.vertexIndex(endWith)
if (!hasPath(startIndex, endIndex)) return null
val nodes = getNodes(startIndex) ?: return null
val edges = mutableListOf>()
var currentIndex = endIndex
var prevIndex = -1
while (currentIndex in 0 until nodes.size) {
val edge = nodes[currentIndex].edge
if (edge != null) edges.add(0, edge)
prevIndex = currentIndex
currentIndex = nodes[currentIndex].vertexIndex
}
return if (prevIndex == startIndex && edges.isNotEmpty()) PathImpl(startFrom, endWith, edges) else null
}
override fun getPaths(startFrom: V, endWith: V, limit: Int): List> {
if (!hasPath(startFrom, endWith)) return Collections.emptyList()
if (limit == 1) return getPath(startFrom, endWith).asList()
return mutableListOf>().apply {
val vertexes = vertexesList
traversePath(vertexes, vertexes.vertexIndex(startFrom), vertexes.vertexIndex(endWith), listOf(startFrom), listOf(), limit, this)
sort()
}
}
private val nodes: MutableMap>> = mutableMapOf()
private val cacheHint: CachedProperty.CacheHint = if (graph is MutableGraph) CachedProperty.CacheHint.None else CachedProperty.CacheHint.Immutable
private val isGraphDynamic = graph is MutableGraph || graph.dynamicEdgesCount > 0
private val vertexesList: Array> by CachedProperty(graph, cacheHint) { vertexes.toTypedArray() }
private var edges: Array?>>? = null
private val matrix: Array by CachedProperty(graph, cacheHint) {
createMatrix(vertexesList, { BooleanArray(it) }, { vertexes, index1, index2, item -> item[index2] = graph.isConnected(vertexes[index1].value, vertexes[index2].value) }).apply {
doubleLoop(size) { index1, index2 -> if (this[index1][index2]) for (n in 0 until size) if (this[n][index1]) this[n][index2] = true }
}
}
private inner class OutgoingEdges(val edge: Edge, val vertex: V) {
val vertexIndex: Int = vertexesList.vertexIndex(vertex)
operator fun component1() = edge
operator fun component2() = vertex
operator fun component3() = vertexIndex
}
private class Node(val weight: Double = Double.NaN,
val vertexIndex: Int = -1,
val edge: Edge? = null) {
val isEmpty: Boolean = weight.isNaN()
}
private inline fun Array>.vertexIndex(value: V): Int = indexOfFirst { it.value == value }.takeIf { it >= 0 } ?: throw NoSuchElementException()
private inline fun Any?.asList(): List = if (this != null) listOf(this as T) else Collections.emptyList()
private inline fun Vertex.getOutgoing(): Collection = startsIn.union(endsIn.filter { !it.isOriented }).map { OutgoingEdges(it, it.opposite(value)) }
private inline fun hasPath(startIndex: Int, endIndex: Int) = matrix[startIndex][endIndex]
private fun traversePath(vertexesList: Array>, currentIndex: Int, endIndex: Int, vertexes: List, edges: List>, limit: Int, paths: MutableList>) {
if (limit <= 0 || paths.size < limit) {
if (currentIndex == endIndex) {
paths += PathImpl(vertexes.first(), vertexesList[endIndex].value, edges)
} else if (hasPath(currentIndex, endIndex)) {
for (candidate in vertexesList[currentIndex].getOutgoing().filter { (_, vertex, vertexIndex) -> (vertexIndex == endIndex || hasPath(vertexIndex, endIndex)) && !vertexes.contains(vertex) }) {
traversePath(vertexesList, candidate.vertexIndex, endIndex, vertexes + candidate.vertex, edges + candidate.edge, limit, paths)
}
}
}
}
private inline fun doubleLoop(size: Int, iteration: (index1: Int, index2: Int) -> Unit) {
for (i in 0 until size) for (j in 0 until size) iteration(i, j)
}
private inline fun createMatrix(vertexes: Array>, make: (size: Int) -> T, process: (vertexes: Array>, index1: Int, index2: Int, item: T) -> Unit): Array =
Array(vertexes.size) { make(vertexes.size) }.apply { doubleLoop(size) { index1, index2 -> process(vertexes, index1, index2, this[index1]) } }
private fun getShortestEdgesMatrix(): Array?>> {
val cache = edges
if (!isGraphDynamic && cache != null) return cache
val result = createMatrix(vertexesList, { Array?>(it) { null} }, { vertexes, index1, index2, item ->
item[index2] = getShortestEdge(vertexes[index1].value, vertexes[index2].value, false, Edge.DefaultWeight)
})
edges = result
return result
}
private fun getNodes(vertexIndex: Int): Array>? {
if (!isGraphDynamic) {
val cache = nodes[vertexIndex]
if (cache != null) return cache
}
val edges = getShortestEdgesMatrix()
val result = Array>(edges.size) { if (it == vertexIndex) Node(0.0) else Node() }.apply {
repeat(size - 1) {
doubleLoop(size) { index1, index2 ->
val edge = edges[index1][index2]
if (edge != null && !this[index1].isEmpty) {
val probe = this[index1].weight + edge.weightOrDefault()
if (this[index2].isEmpty || this[index2].weight > probe) {
this[index2] = Node(probe, index1, edge)
}
}
}
}
}
doubleLoop(result.size) { index1, index2 ->
val edge = edges[index1][index2]
if (edge != null
&& !result[index1].isEmpty
&& !result[index2].isEmpty
&& result[index2].weight > result[index1].weight + edge.weightOrDefault()) {
return null
}
}
if (!isGraphDynamic) nodes[vertexIndex] = result
return result
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy