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

commonMain.com.javiersc.kotlin.stdlib.graph.Graph.kt Maven / Gradle / Ivy

The newest version!
package com.javiersc.kotlin.stdlib.graph

public interface Graph : Map, List>> {

    public var renderer: (Any?.() -> String)

    public val circularVertexes: Map>
        get() {
            val circularDependencies: MutableMap> = mutableMapOf()
            val visited: MutableSet> = mutableSetOf()

            for (vertex: Vertex in keys) {
                if (vertex !in visited) {
                    val path: MutableList> = mutableListOf()
                    vertex.deepFirstSearchCircularDependencies(visited, path, circularDependencies)
                }
            }

            return circularDependencies.mapValues { it.value.toList() }
        }

    public val hasCircularVertexes: Boolean
        get() = circularVertexes.isNotEmpty()

    public val duplicatedVertexes: Map
        get() = keys.groupingBy(Vertex::value).eachCount().filterValues { it > 1 }

    public val hasDuplicatedVertexes: Boolean
        get() = duplicatedVertexes.isNotEmpty()

    public val missingVertexes: Set

    public val hasMissingVertexes: Boolean
        get() = missingVertexes.isNotEmpty()

    public fun asString(): String = buildString {
        [email protected] { (vertex: Vertex, edges: List>) ->
            val value: String = renderer(vertex.value)
            val edgeString: String = edges.joinToString { renderer(it.destination.value) }
            appendLine("$value -> [$edgeString]")
        }
    }

    public fun contains(value: T): Boolean = keys.any { it.value == value }

    public fun contains(value: T, predicate: (T) -> Boolean): Boolean =
        keys.any { predicate(it.value) }

    public fun toGraph(): Graph = this

    public fun containsCircularVertexes(value: T): Boolean =
        circularVertexes[value]?.isNotEmpty() == true

    public fun doesNotContainsCircularVertexes(value: T): Boolean = !containsCircularVertexes(value)

    public fun vertexesFor(value: T, predicate: (T) -> Boolean = { it == value }): List {
        if (containsCircularVertexes(value)) return emptyList()
        val vertex: Vertex = keys.find { predicate(it.value) } ?: return emptyList()
        val edges: List> = this[vertex] ?: return emptyList()
        val destinationVertexes: Sequence> = edges.asSequence().map(Edge::destination)
        val destinations: Sequence = destinationVertexes.map(Vertex::value)
        return (destinations + destinationVertexes.flatMap { vertexesFor(it.value) }).toList()
    }

    public fun vertexesFor(vararg values: T): List = values.flatMap(::vertexesFor)

    public fun toGraphSortedByEdges(): Graph = buildGraph {
        val sortedGraph: MutableGraph = this
        val graph: Graph = this@Graph
        val remainingMap: MutableMap, List>> = graph.toMutableMap()
        while (remainingMap.isNotEmpty()) {
            val vertexesToBeRemoved: MutableList> = mutableListOf()
            for ((vertex: Vertex, edges: List>) in remainingMap) {
                if (edges.isEmpty()) {
                    sortedGraph.addVertex(vertex.value)
                    vertexesToBeRemoved.add(vertex)
                    println()
                }
                for (edge: Edge in edges) {
                    if (edge.destination.value in sortedGraph.keys.map(Vertex::value)) {
                        sortedGraph.addVertex(edge.source.value)
                        sortedGraph.addEdge(edge.source.value, edge.destination.value)
                        vertexesToBeRemoved.add(edge.source)
                        println()
                    }
                }
            }
            for (vertexToBeRemoved: Vertex in vertexesToBeRemoved) {
                remainingMap.remove(vertexToBeRemoved)
            }
            vertexesToBeRemoved.clear()
        }
    }

    public fun renderer(block: Any?.() -> String) {
        this.renderer = block
    }

    public data class Vertex(val index: Int, val value: T)

    public data class Edge(val source: Vertex, val destination: Vertex)

    private fun Vertex.deepFirstSearchCircularDependencies(
        visited: MutableSet>,
        path: MutableList>,
        circularDependencies: MutableMap>,
    ) {
        val map: Graph = this@Graph
        val vertex: Vertex = this
        visited.add(vertex)
        path.add(vertex)

        val edges: List> = map[vertex] ?: return

        for (edge: Edge in edges) {
            val destination: Vertex = edge.destination

            if (destination in path) {
                val circularDependency =
                    path.subList(path.indexOf(destination), path.size).map { it.value }
                circularDependencies
                    .getOrPut(circularDependency.first()) { mutableListOf() }
                    .addAll(circularDependency)
            } else if (destination !in visited) {
                destination.deepFirstSearchCircularDependencies(visited, path, circularDependencies)
            }
        }

        if (path.last() == vertex) {
            path.remove(vertex)
        }
    }
}

public class MutableGraph internal constructor() : Graph {

    override var renderer: (Any?.() -> String) = { this.toString() }

    private val _missingVertexes: MutableSet = mutableSetOf()
    override val missingVertexes: Set = _missingVertexes

    private val map: MutableMap, MutableList>> = mutableMapOf()

    override val entries: Set, List>>>
        get() = map.entries

    override val keys: Set>
        get() = map.keys

    override val size: Int
        get() = map.size

    override val values: Collection>>
        get() = map.values

    public fun addVertex(data: T): T {
        val vertex: Graph.Vertex = Graph.Vertex(this.count(), data)
        map[vertex] = mutableListOf()
        return data
    }

    public fun addEdge(source: T, destination: T) {
        val sourceVertex: Graph.Vertex? = keys.find { it.value == source }
        val destinationVertex: Graph.Vertex? = keys.find { it.value == destination }
        if (sourceVertex == null) _missingVertexes.add(source)
        if (destinationVertex == null) _missingVertexes.add(destination)
        if (sourceVertex != null && destinationVertex != null) {
            val edge: Graph.Edge = Graph.Edge(sourceVertex, destinationVertex)
            map[sourceVertex]?.add(edge)
        }
    }

    override fun isEmpty(): Boolean = map.isEmpty()

    override fun get(key: Graph.Vertex): List>? = map[key]

    override fun containsValue(value: List>): Boolean = map.containsValue(value)

    override fun containsKey(key: Graph.Vertex): Boolean = map.containsKey(key)

    override fun toString(): String = asString()
}

public fun  buildGraph(builderAction: MutableGraph.() -> Unit = {}): Graph =
    MutableGraph().apply(builderAction)

public fun  mutableGraphOf(builderAction: MutableGraph.() -> Unit = {}): MutableGraph =
    MutableGraph().apply(builderAction)




© 2015 - 2025 Weber Informatics LLC | Privacy Policy