From 423e581529bb05d5a8f46cf061e53e93782e9459 Mon Sep 17 00:00:00 2001 From: norangebit Date: Sat, 18 May 2019 16:20:48 +0200 Subject: [PATCH] add graph - add interface for undirected and directed graph - add Graph and DiGraph - add interface for graph data - add adjacentMatrix and adjacentList - add detect directed cycles --- build.gradle.kts | 5 +- buildSrc/src/main/java/Config.kt | 2 +- .../it/norangeb/algorithms/graph/DiGraph.kt | 54 ++++++++ .../it/norangeb/algorithms/graph/Graph.kt | 44 +++++++ .../algorithms/graph/GraphInterface.kt | 37 ++++++ .../algorithms/graph/data/AdjacentList.kt | 57 ++++++++ .../algorithms/graph/data/AdjacentMatrix.kt | 50 +++++++ .../algorithms/graph/data/GraphData.kt | 33 +++++ .../graph/operations/DirectedCycle.kt | 88 +++++++++++++ .../norangeb/algorithms/graph/DiGraphTest.kt | 103 +++++++++++++++ .../it/norangeb/algorithms/graph/GraphTest.kt | 73 +++++++++++ .../algorithms/graph/data/AdjacentListTest.kt | 87 ++++++++++++ .../graph/data/AdjacentMatrixTest.kt | 79 +++++++++++ .../graph/operations/DirectedCycleTest.kt | 124 ++++++++++++++++++ 14 files changed, 832 insertions(+), 4 deletions(-) create mode 100644 src/main/kotlin/it/norangeb/algorithms/graph/DiGraph.kt create mode 100644 src/main/kotlin/it/norangeb/algorithms/graph/Graph.kt create mode 100644 src/main/kotlin/it/norangeb/algorithms/graph/GraphInterface.kt create mode 100644 src/main/kotlin/it/norangeb/algorithms/graph/data/AdjacentList.kt create mode 100644 src/main/kotlin/it/norangeb/algorithms/graph/data/AdjacentMatrix.kt create mode 100644 src/main/kotlin/it/norangeb/algorithms/graph/data/GraphData.kt create mode 100644 src/main/kotlin/it/norangeb/algorithms/graph/operations/DirectedCycle.kt create mode 100644 src/test/kotlin/it/norangeb/algorithms/graph/DiGraphTest.kt create mode 100644 src/test/kotlin/it/norangeb/algorithms/graph/GraphTest.kt create mode 100644 src/test/kotlin/it/norangeb/algorithms/graph/data/AdjacentListTest.kt create mode 100644 src/test/kotlin/it/norangeb/algorithms/graph/data/AdjacentMatrixTest.kt create mode 100644 src/test/kotlin/it/norangeb/algorithms/graph/operations/DirectedCycleTest.kt diff --git a/build.gradle.kts b/build.gradle.kts index 7381e5d..ee3cb65 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -23,11 +23,10 @@ dependencies { testImplementation(Config.Libs.kluent) testImplementation(Config.Libs.mockk) testImplementation(Config.Libs.jetbrainJunit) - // testImplementation(Config.Libs.spekDsl) + testImplementation(Config.Libs.spekDsl) testRuntimeOnly(Config.Libs.junitEngine) - // testRuntimeOnly(Config.Libs.kotlinReflect) - // testRuntimeOnly(Config.Libs.spekRunner) + testRuntimeOnly(Config.Libs.spekRunner) } tasks.withType { diff --git a/buildSrc/src/main/java/Config.kt b/buildSrc/src/main/java/Config.kt index fa733f7..400d416 100644 --- a/buildSrc/src/main/java/Config.kt +++ b/buildSrc/src/main/java/Config.kt @@ -29,7 +29,7 @@ object Config { val kluent = "1.49" val koin = "1.0.2" val junit = "5.4.2" - val spek = "2.0.1" + val spek = "2.0.2" val kotlin = "1.3.30" val mockk = "1.9.3" val gson = "2.8.5" diff --git a/src/main/kotlin/it/norangeb/algorithms/graph/DiGraph.kt b/src/main/kotlin/it/norangeb/algorithms/graph/DiGraph.kt new file mode 100644 index 0000000..3714974 --- /dev/null +++ b/src/main/kotlin/it/norangeb/algorithms/graph/DiGraph.kt @@ -0,0 +1,54 @@ +/* + * MIT License + * + * Copyright (c) 2019 norangebit + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + */ + +package it.norangeb.algorithms.graph + +import it.norangeb.algorithms.graph.data.AdjacentList +import it.norangeb.algorithms.graph.data.GraphData + +class DiGraph( + private val data: GraphData = AdjacentList() +) : DirectedGraph { + override fun vertexNumber(): Int = data.vertexNumber() + + override fun edgeNumber(): Int = data.edgeNumber() + + override fun addEdge(firstVertex: Int, secondVertex: Int) = data.addEdge(firstVertex, secondVertex) + + override fun adjacentVertex(vertex: Int): Collection = data.adjacent(vertex) + + override fun reverse(): DirectedGraph { + val reverse = DiGraph() + + (0 until data.vertexNumber()) + .forEach { from -> + data.adjacent(from).forEach { to -> + reverse.addEdge(to, from) + } + } + + return reverse + } +} \ No newline at end of file diff --git a/src/main/kotlin/it/norangeb/algorithms/graph/Graph.kt b/src/main/kotlin/it/norangeb/algorithms/graph/Graph.kt new file mode 100644 index 0000000..8ad6f6c --- /dev/null +++ b/src/main/kotlin/it/norangeb/algorithms/graph/Graph.kt @@ -0,0 +1,44 @@ +/* + * MIT License + * + * Copyright (c) 2019 norangebit + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + */ + +package it.norangeb.algorithms.graph + +import it.norangeb.algorithms.graph.data.AdjacentList +import it.norangeb.algorithms.graph.data.GraphData + +class Graph( + private val data: GraphData = AdjacentList() +) : UndirectedGraph { + override fun vertexNumber(): Int = data.vertexNumber() + + override fun edgeNumber(): Int = data.edgeNumber() / 2 + + override fun addEdge(firstVertex: Int, secondVertex: Int) { + data.addEdge(firstVertex, secondVertex) + data.addEdge(secondVertex, firstVertex) + } + + override fun adjacentVertex(vertex: Int): Collection = data.adjacent(vertex) +} \ No newline at end of file diff --git a/src/main/kotlin/it/norangeb/algorithms/graph/GraphInterface.kt b/src/main/kotlin/it/norangeb/algorithms/graph/GraphInterface.kt new file mode 100644 index 0000000..441f738 --- /dev/null +++ b/src/main/kotlin/it/norangeb/algorithms/graph/GraphInterface.kt @@ -0,0 +1,37 @@ +/* + * MIT License + * + * Copyright (c) 2019 norangebit + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + */ + +package it.norangeb.algorithms.graph + +interface UndirectedGraph { + fun vertexNumber(): Int + fun edgeNumber(): Int + fun addEdge(firstVertex: Int, secondVertex: Int) + fun adjacentVertex(vertex: Int): Collection +} + +interface DirectedGraph : UndirectedGraph { + fun reverse(): DirectedGraph +} \ No newline at end of file diff --git a/src/main/kotlin/it/norangeb/algorithms/graph/data/AdjacentList.kt b/src/main/kotlin/it/norangeb/algorithms/graph/data/AdjacentList.kt new file mode 100644 index 0000000..55506bd --- /dev/null +++ b/src/main/kotlin/it/norangeb/algorithms/graph/data/AdjacentList.kt @@ -0,0 +1,57 @@ +/* + * MIT License + * + * Copyright (c) 2019 norangebit + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + */ + +package it.norangeb.algorithms.graph.data + +import java.util.TreeMap +import kotlin.math.max + +class AdjacentList : GraphData { + private val graph = TreeMap>() + private var edgeNumber = 0 + private var maxVertexId = 0 + + override fun addEdge(firstVertex: Int, secondVertex: Int) { + graph.computeIfAbsent(firstVertex) { mutableListOf() } + + graph[firstVertex]?.add(secondVertex) + + computeMaxVertexId(firstVertex, secondVertex) + edgeNumber++ + } + + override fun adjacent(vertex: Int): Collection { + return graph[vertex] ?: listOf() + } + + override fun vertexNumber(): Int = maxVertexId + 1 + + override fun edgeNumber(): Int = edgeNumber + + private fun computeMaxVertexId(firstVertex: Int, secondVertex: Int) { + val max = max(firstVertex, secondVertex) + maxVertexId = max(max, maxVertexId) + } +} \ No newline at end of file diff --git a/src/main/kotlin/it/norangeb/algorithms/graph/data/AdjacentMatrix.kt b/src/main/kotlin/it/norangeb/algorithms/graph/data/AdjacentMatrix.kt new file mode 100644 index 0000000..1f288e9 --- /dev/null +++ b/src/main/kotlin/it/norangeb/algorithms/graph/data/AdjacentMatrix.kt @@ -0,0 +1,50 @@ +/* + * MIT License + * + * Copyright (c) 2019 norangebit + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + */ + +package it.norangeb.algorithms.graph.data + +class AdjacentMatrix(private val vertexNumber: Int) : GraphData { + private val graph = Array(vertexNumber) { + Array(vertexNumber) { false } + } + + override fun addEdge(firstVertex: Int, secondVertex: Int) { + graph[firstVertex][secondVertex] = true + } + + override fun adjacent(vertex: Int): Collection { + return graph[vertex] + .mapIndexed { index, b -> if (b) index else -1 } + .filter { it >= 0 } + } + + override fun vertexNumber(): Int = vertexNumber + + override fun edgeNumber(): Int { + return graph + .map { it.count { it } } + .sum() + } +} \ No newline at end of file diff --git a/src/main/kotlin/it/norangeb/algorithms/graph/data/GraphData.kt b/src/main/kotlin/it/norangeb/algorithms/graph/data/GraphData.kt new file mode 100644 index 0000000..49e3b75 --- /dev/null +++ b/src/main/kotlin/it/norangeb/algorithms/graph/data/GraphData.kt @@ -0,0 +1,33 @@ +/* + * MIT License + * + * Copyright (c) 2019 norangebit + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + */ + +package it.norangeb.algorithms.graph.data + +interface GraphData { + fun addEdge(firstVertex: Int, secondVertex: Int) + fun adjacent(vertex: Int): Collection + fun vertexNumber(): Int + fun edgeNumber(): Int +} \ No newline at end of file diff --git a/src/main/kotlin/it/norangeb/algorithms/graph/operations/DirectedCycle.kt b/src/main/kotlin/it/norangeb/algorithms/graph/operations/DirectedCycle.kt new file mode 100644 index 0000000..800af6f --- /dev/null +++ b/src/main/kotlin/it/norangeb/algorithms/graph/operations/DirectedCycle.kt @@ -0,0 +1,88 @@ +/* + * MIT License + * + * Copyright (c) 2019 norangebit + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + */ + +package it.norangeb.algorithms.graph.operations + +import it.norangeb.algorithms.graph.DirectedGraph +import it.norangeb.algorithms.graph.UndirectedGraph +import java.util.Stack + +class DirectedCycle(graph: DirectedGraph) { + var cycle: MutableCollection? = null + private val graphInfo = Array(graph.vertexNumber()) { VertexInfo() } + + init { + (0 until graph.vertexNumber()) + .forEach { + if (!graphInfo[it].visited) + dfs(graph, it) + } + } + + fun hasCycle(): Boolean = cycle != null + + private fun dfs(graph: UndirectedGraph, vertex: Int) { + graphInfo[vertex].visited = true + graphInfo[vertex].onStack = true + + graph.adjacentVertex(vertex) + .forEach { + when { + hasCycle() -> return@forEach + !graphInfo[it].visited -> exploreChild(graph, vertex, it) + graphInfo[it].onStack -> cycle = makeCycle(vertex, it) + } + } + + graphInfo[vertex].onStack = false + } + + private fun makeCycle(start: Int, end: Int): MutableCollection { + val cycle = Stack() + + var currentVertex = start + + while (currentVertex != end) { + cycle.add(currentVertex) + currentVertex = graphInfo[currentVertex].previously + } + + cycle.add(end) + cycle.add(start) + + return cycle + } + + private fun exploreChild(graph: UndirectedGraph, parent: Int, child: Int) { + graphInfo[child].previously = parent + dfs(graph, child) + } + + data class VertexInfo( + var visited: Boolean = false, + var onStack: Boolean = false, + var previously: Int = -1 + ) +} \ No newline at end of file diff --git a/src/test/kotlin/it/norangeb/algorithms/graph/DiGraphTest.kt b/src/test/kotlin/it/norangeb/algorithms/graph/DiGraphTest.kt new file mode 100644 index 0000000..4e3138e --- /dev/null +++ b/src/test/kotlin/it/norangeb/algorithms/graph/DiGraphTest.kt @@ -0,0 +1,103 @@ +/* + * MIT License + * + * Copyright (c) 2019 norangebit + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + */ + +package it.norangeb.algorithms.graph + +import org.amshove.kluent.`should be equal to` +import org.amshove.kluent.`should equal` +import org.spekframework.spek2.Spek +import org.spekframework.spek2.style.gherkin.Feature + +object DiGraphTest : Spek({ + Feature("directed graph") { + val graph by memoized { DiGraph() } + + Scenario("add edges") { + When("add edge 1 -> 4") { + graph.addEdge(1, 4) + } + + Then("number of edge should equal to 1") { + graph.edgeNumber() `should be equal to` 1 + } + + Then("adjacent of 1 should equal listOf(4)") { + graph.adjacentVertex(1) `should equal` listOf(4) + } + + Then("adjacent of 4 should be empty") { + graph.adjacentVertex(4).isEmpty() `should be equal to` true + } + + When("add edge 1 -> 3, 3 -> 4, 1 -> 5") { + graph.addEdge(1, 3) + graph.addEdge(3, 4) + graph.addEdge(1, 5) + } + + Then("number of edge should equal to 4") { + graph.edgeNumber() `should be equal to` 4 + } + + Then("adjacent of 1 should equal listOf(3, 4, 5)") { + graph.adjacentVertex(1).sorted() `should equal` listOf(3, 4, 5) + } + + Then("adjacent of 3 should equal listOf(4)") { + graph.adjacentVertex(3) `should equal` listOf(4) + } + } + + Scenario("reverse test") { + val diGraph by memoized { DiGraph() } + + Given("directed graph 0 -> 2, 0 -> 4, 1 -> 0, 1 -> 2, 4 -> 2") { + diGraph.addEdge(0, 2) + diGraph.addEdge(0, 4) + diGraph.addEdge(1, 0) + diGraph.addEdge(1, 2) + diGraph.addEdge(4, 2) + } + + lateinit var result: DirectedGraph + + When("compute the reversed graph") { + result = diGraph.reverse() + } + + Then("adjacent of 0 should euqals listOf(1)") { + result.adjacentVertex(0) `should equal` listOf(1) + } + + Then("adjacent of 1 should be empty") { + result.adjacentVertex(1).isEmpty() `should be equal to` true + } + + Then("adjacent of 2 should equal listOf(0, 1, 4)") { + result.adjacentVertex(2).sorted() `should equal` listOf(0, 1, 4) + } + } + } +}) \ No newline at end of file diff --git a/src/test/kotlin/it/norangeb/algorithms/graph/GraphTest.kt b/src/test/kotlin/it/norangeb/algorithms/graph/GraphTest.kt new file mode 100644 index 0000000..6bac6a3 --- /dev/null +++ b/src/test/kotlin/it/norangeb/algorithms/graph/GraphTest.kt @@ -0,0 +1,73 @@ +/* + * MIT License + * + * Copyright (c) 2019 norangebit + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + */ + +package it.norangeb.algorithms.graph + +import org.amshove.kluent.`should be equal to` +import org.amshove.kluent.`should equal` +import org.spekframework.spek2.Spek +import org.spekframework.spek2.style.gherkin.Feature + +object GraphTest : Spek({ + Feature("undirected graph") { + val graph by memoized { Graph() } + + Scenario("add edges") { + When("add edge 1 - 4") { + graph.addEdge(1, 4) + } + + Then("number of edge should equal to 1") { + graph.edgeNumber() `should be equal to` 1 + } + + Then("adjacent of 1 should equal listOf(4)") { + graph.adjacentVertex(1) `should equal` listOf(4) + } + + Then("adjacent of 4 should equal listOf(1)") { + graph.adjacentVertex(4) `should equal` listOf(1) + } + + When("add edge 1 - 3, 3 - 4, 1 - 5") { + graph.addEdge(1, 3) + graph.addEdge(3, 4) + graph.addEdge(1, 5) + } + + Then("number of edge should equal to 4") { + graph.edgeNumber() `should be equal to` 4 + } + + Then("adjacent of 1 should equal listOf(3, 4, 5)") { + graph.adjacentVertex(1).sorted() `should equal` listOf(3, 4, 5) + } + + Then("adjacent of 3 should equal listOf(1, 4)") { + graph.adjacentVertex(3).sorted() `should equal` listOf(1, 4) + } + } + } +}) \ No newline at end of file diff --git a/src/test/kotlin/it/norangeb/algorithms/graph/data/AdjacentListTest.kt b/src/test/kotlin/it/norangeb/algorithms/graph/data/AdjacentListTest.kt new file mode 100644 index 0000000..aadadc7 --- /dev/null +++ b/src/test/kotlin/it/norangeb/algorithms/graph/data/AdjacentListTest.kt @@ -0,0 +1,87 @@ +/* + * MIT License + * + * Copyright (c) 2019 norangebit + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + */ + +package it.norangeb.algorithms.graph.data + +import org.amshove.kluent.`should be equal to` +import org.amshove.kluent.`should contain` +import org.amshove.kluent.`should equal` +import org.spekframework.spek2.Spek +import org.spekframework.spek2.style.gherkin.Feature + +object AdjacentListTest : Spek({ + Feature("adjacent list graph data") { + val graphData by memoized { AdjacentList() } + + Scenario("add edges") { + When("add edge 1 -> 7") { + graphData.addEdge(1, 7) + } + + Then("adjacent of 1 should contains 7") { + graphData.adjacent(1) `should contain` 7 + } + + Then("adjacent of 7 should be empty") { + graphData.adjacent(7).isEmpty() `should be equal to` true + } + + Then("edgeNumber should be equal to 1") { + graphData.edgeNumber() `should be equal to` 1 + } + + Then("vertexNumber should be equal to 8") { + graphData.vertexNumber() `should be equal to` 8 + } + + When("add edge 1 -> 8, 7 -> 9, 1 -> 3, 3 -> 1") { + graphData.addEdge(1, 8) + graphData.addEdge(7, 9) + graphData.addEdge(1, 3) + graphData.addEdge(3, 1) + } + + Then("adjacent of 1 should equal listOf(3, 7, 8)") { + graphData.adjacent(1).sorted() `should equal` listOf(3, 7, 8) + } + + Then("adjacent of 7 should equal listOf(9)") { + graphData.adjacent(7) `should equal` listOf(9) + } + + Then("adjacent of 3 should equal listOf(1)") { + graphData.adjacent(3) `should equal` listOf(1) + } + + Then("edgeNumber should be equal to 5") { + graphData.edgeNumber() `should be equal to` 5 + } + + Then("vertexNumber should be equal to 10") { + graphData.vertexNumber() `should be equal to` 10 + } + } + } +}) \ No newline at end of file diff --git a/src/test/kotlin/it/norangeb/algorithms/graph/data/AdjacentMatrixTest.kt b/src/test/kotlin/it/norangeb/algorithms/graph/data/AdjacentMatrixTest.kt new file mode 100644 index 0000000..4d5f3f0 --- /dev/null +++ b/src/test/kotlin/it/norangeb/algorithms/graph/data/AdjacentMatrixTest.kt @@ -0,0 +1,79 @@ +/* + * MIT License + * + * Copyright (c) 2019 norangebit + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + */ + +package it.norangeb.algorithms.graph.data + +import org.amshove.kluent.`should be equal to` +import org.amshove.kluent.`should contain` +import org.amshove.kluent.`should equal` +import org.spekframework.spek2.Spek +import org.spekframework.spek2.style.gherkin.Feature + +object AdjacentMatrixTest : Spek({ + Feature("adjacent matrix graph data") { + val graphData by memoized { AdjacentMatrix(10) } + + Scenario("add edges") { + When("add edge 1 -> 7") { + graphData.addEdge(1, 7) + } + + Then("adjacent of 1 should contains 7") { + graphData.adjacent(1) `should contain` 7 + } + + Then("adjacent of 7 should be empty") { + graphData.adjacent(7).isEmpty() `should be equal to` true + } + + Then("edgeNumber should be equal to 1") { + graphData.edgeNumber() `should be equal to` 1 + } + + When("add edge 1 -> 8, 7 -> 9, 1 -> 3, 3 -> 1") { + graphData.addEdge(1, 8) + graphData.addEdge(7, 9) + graphData.addEdge(1, 3) + graphData.addEdge(3, 1) + } + + Then("adjacent of 1 should equal listOf(3, 7, 8)") { + graphData.adjacent(1) `should equal` listOf(3, 7, 8) + } + + Then("adjacent of 7 should equal listOf(9)") { + graphData.adjacent(7) `should equal` listOf(9) + } + + Then("adjacent of 3 should equal listOf(1)") { + graphData.adjacent(3) `should equal` listOf(1) + } + + Then("edgeNumber should be equal to 5") { + graphData.edgeNumber() `should be equal to` 5 + } + } + } +}) \ No newline at end of file diff --git a/src/test/kotlin/it/norangeb/algorithms/graph/operations/DirectedCycleTest.kt b/src/test/kotlin/it/norangeb/algorithms/graph/operations/DirectedCycleTest.kt new file mode 100644 index 0000000..ca20c05 --- /dev/null +++ b/src/test/kotlin/it/norangeb/algorithms/graph/operations/DirectedCycleTest.kt @@ -0,0 +1,124 @@ +/* + * MIT License + * + * Copyright (c) 2019 norangebit + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + */ + +package it.norangeb.algorithms.graph.operations + +import it.norangeb.algorithms.graph.DiGraph +import org.amshove.kluent.`should be equal to` +import org.amshove.kluent.`should equal` +import org.spekframework.spek2.Spek +import org.spekframework.spek2.style.gherkin.Feature + +object DirectedCycleTest : Spek({ + Feature("directed cycle") { + Scenario("try to find a cycle into DAG") { + val dag by memoized { DiGraph() } + + Given("a directed acyclic graph") { + dag.addEdge(0, 1) + dag.addEdge(0, 6) + dag.addEdge(1, 2) + dag.addEdge(2, 3) + dag.addEdge(3, 4) + dag.addEdge(5, 4) + dag.addEdge(5, 6) + } + + lateinit var directedCycleResult: DirectedCycle + + When("try to find a cycle") { + directedCycleResult = DirectedCycle(dag) + } + + Then("has cycle should be false") { + directedCycleResult.hasCycle() `should be equal to` false + } + + Then("cycle should be null") { + directedCycleResult.cycle `should equal` null + } + } + + Scenario("try to find a cycle into cyclic graph") { + val cyclicGraph by memoized { DiGraph() } + + Given("a cyclic graph") { + cyclicGraph.addEdge(0, 1) + cyclicGraph.addEdge(0, 6) + cyclicGraph.addEdge(1, 2) + cyclicGraph.addEdge(2, 3) + cyclicGraph.addEdge(3, 4) + cyclicGraph.addEdge(3, 5) + cyclicGraph.addEdge(5, 0) + cyclicGraph.addEdge(5, 4) + cyclicGraph.addEdge(5, 6) + } + + lateinit var directedCycleResult: DirectedCycle + + When("try to find a cycle") { + directedCycleResult = DirectedCycle(cyclicGraph) + } + + Then("hasCycle should be true") { + directedCycleResult.hasCycle() `should be equal to` true + } + + Then("cycle should equal listOf(5, 3, 2, 1, 0, 5)") { + directedCycleResult.cycle `should equal` listOf(5, 3, 2, 1, 0, 5) + } + } + + Scenario("try to find a cycle into cyclic graph with cycle not on root") { + val cyclicGraph by memoized { DiGraph() } + + Given("a cyclic graph") { + cyclicGraph.addEdge(0, 1) + cyclicGraph.addEdge(0, 3) + cyclicGraph.addEdge(1, 3) + cyclicGraph.addEdge(4, 0) + cyclicGraph.addEdge(4, 2) + cyclicGraph.addEdge(4, 5) + cyclicGraph.addEdge(4, 6) + cyclicGraph.addEdge(5, 7) + cyclicGraph.addEdge(7, 4) + } + + lateinit var directedCycleResult: DirectedCycle + + When("try to find a cycle") { + directedCycleResult = DirectedCycle(cyclicGraph) + } + + Then("hasCycle should be true") { + directedCycleResult.hasCycle() `should be equal to` true + } + + Then("cycle should equal listOf(7, 5, 4, 7)") { + directedCycleResult.cycle `should equal` listOf(7, 5, 4, 7) + } + } + } +}) \ No newline at end of file