All Downloads are FREE. Search and download functionalities are using the official Maven repository.

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