Skip to content
Merged

Task #25

Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion app/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
plugins {
kotlin("jvm") version "2.1.10"
kotlin("plugin.serialization") version "2.1.10" // ← добавь эту строку
id("org.jetbrains.kotlin.plugin.compose") version "2.1.20"
id("org.jetbrains.compose") version "1.7.1"
}
Expand Down Expand Up @@ -28,6 +29,7 @@ dependencies {
implementation("org.jetbrains.exposed:exposed-dao:0.61.0")
implementation("org.jetbrains.exposed:exposed-jdbc:0.61.0")
implementation("com.github.JetBrains-Research:louvain:main-SNAPSHOT")
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.7.3")
}

tasks.test {
Expand All @@ -41,4 +43,4 @@ compose.desktop {
application {
mainClass = "MainKt"
}
}
}
6 changes: 3 additions & 3 deletions app/src/main/kotlin/model/algo/findBridges.kt
Original file line number Diff line number Diff line change
Expand Up @@ -41,9 +41,9 @@ fun findBridges(graph: Graph): Set<Edge> {
ret[adjacentVertex] ?: throw IllegalStateException(),
)
if ((ret[adjacentVertex] ?: throw IllegalStateException()) > (
timeIn[current]
?: throw IllegalStateException()
)
timeIn[current]
?: throw IllegalStateException()
)
) {
briges.add(
graph.getEdge(current, adjacentVertex) ?: throw IllegalStateException("No such vertex in graph"),
Expand Down
79 changes: 79 additions & 0 deletions app/src/main/kotlin/model/algo/findCrucialVertex.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
package model.algo

import model.graph.Graph
import model.graph.Vertex
import java.util.LinkedList
import java.util.Queue


fun Graph.computeBetweennessCentrality(): Map<Vertex, Double> {
val centrality = mutableMapOf<Vertex, Double>()
vertices.forEach { v -> centrality[v] = 0.0 }

for (s in vertices) {
val stack = mutableListOf<Vertex>()
val predecessors = mutableMapOf<Vertex, MutableList<Vertex>>()
val sigma = mutableMapOf<Vertex, Int>()
val distance = mutableMapOf<Vertex, Int>()

vertices.forEach { w -> predecessors[w] = mutableListOf() }
vertices.forEach { w -> sigma[w] = 0 }
vertices.forEach { w -> distance[w] = -1 }

sigma[s] = 1
distance[s] = 0

val queue: Queue<Vertex> = LinkedList()
queue.add(s)


while (queue.isNotEmpty()) {
val v = queue.poll()
stack.add(v)

val neighbors = if (isDirected) {
edges.filter { it.vertices.first == v }.map { it.vertices.second }
} else {
edges.filter { it.vertices.first == v || it.vertices.second == v }
.map { if (it.vertices.first == v) it.vertices.second else it.vertices.first }
}

for (w in neighbors) {
distance[w]?.let {
if (it < 0) {
queue.add(w)
distance[w] = distance[v]!! + 1
}
}
if (distance[w] == distance[v]!! + 1) {
sigma[w] = sigma[w]!! + sigma[v]!!
predecessors[w]!!.add(v)
}
}
}

val delta = mutableMapOf<Vertex, Double>()
vertices.forEach { w -> delta[w] = 0.0 }

while (stack.isNotEmpty()) {
val w = stack.removeAt(stack.size - 1)
for (v in predecessors[w] ?: emptyList()) {
delta[v] = delta[v]!! + (sigma[v]!!.toDouble() / sigma[w]!!.toDouble()) * (1 + delta[w]!!)
}
if (w != s) {
centrality[w] = centrality[w]!! + delta[w]!!
}
}
}

return centrality
}


fun Graph.findCrucialVertices(): List<Vertex> {
val centrality = computeBetweennessCentrality()
if (centrality.isEmpty()) return emptyList()

val maxCentrality = centrality.maxOfOrNull { it.value } ?: return emptyList()
return centrality.filter { it.value == maxCentrality }.keys.toList()
}
43 changes: 43 additions & 0 deletions app/src/main/kotlin/model/algo/findCycles.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package model.algo

import model.graph.Graph
import model.graph.Vertex

fun Graph.findCyclesStartingFrom(startVertexId: Int): List<List<Vertex>> {
val cycles = mutableListOf<List<Vertex>>()
val startVertex = getVertex(startVertexId) ?: return emptyList()

fun dfs(
current: Vertex,
path: MutableList<Vertex>,
recStack: MutableSet<Vertex>
) {
path.add(current)
recStack.add(current)

val neighbors = if (isDirected) {
edges.filter { it.vertices.first == current }.map { it.vertices.second }
} else {
edges
.filter { it.vertices.first == current || it.vertices.second == current }
.map { if (it.vertices.first == current) it.vertices.second else it.vertices.first }
}

for (neighbor in neighbors) {
if (neighbor == startVertex && path.size > 2) {
// Цикл завершён: начинается и заканчивается в startVertex
cycles.add((path + neighbor).toList())
} else if (neighbor !in recStack) {
dfs(neighbor, path, recStack)
}
}

// Backtracking
path.removeAt(path.size - 1)
recStack.remove(current)
}

dfs(startVertex, mutableListOf(), mutableSetOf())

return cycles
}
25 changes: 25 additions & 0 deletions app/src/main/kotlin/model/io/json/GraphJson.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package model.io.json

import kotlinx.serialization.Serializable


@Serializable
data class GraphJson(
val isDirected:Boolean,
val vertices :List<VertexData>,
val edges: List<EdgeData>,
)
@Serializable
data class VertexData(
val id: Int,
val label: String,
)
@Serializable
data class EdgeData(
val id: Long,
val from: Int,
val to: Int,
val weight: Long,
)


54 changes: 54 additions & 0 deletions app/src/main/kotlin/model/io/json/GraphRepository.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
package model.io.json

import kotlinx.serialization.encodeToString
import kotlinx.serialization.decodeFromString
import kotlinx.serialization.json.Json
import model.graph.Graph
import java.io.File

class JsonRepository {
val json = Json { prettyPrint = true }

fun writeToDisk(graph: Graph, filePath: String) {

val graphJson = GraphJson(
isDirected = graph.isDirected,
vertices = graph.vertices.map { vertex ->
VertexData(id = vertex.id, label = vertex.label)
},
edges = graph.edges.map { edge ->
EdgeData(
id = edge.id,
from = edge.vertices.first.id,
to = edge.vertices.second.id,
weight = edge.weight
)
}
)


val jsonString = json.encodeToString(graphJson)
File(filePath).writeText(jsonString)
}

fun readFromDisk(filePath: String): Graph {

val jsonString = File(filePath).readText()
val graphJson = json.decodeFromString<GraphJson>(jsonString)


val graph = Graph(isDirected = graphJson.isDirected)


graphJson.vertices.forEach { vertexData ->
graph.addVertex(vertexData.id, vertexData.label)
}


graphJson.edges.forEach { edgeData ->
graph.addEdge(edgeData.from, edgeData.to, edgeData.weight)
}

return graph
}
}
93 changes: 93 additions & 0 deletions app/src/main/kotlin/view/dialogs/CycleDetectionView.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
package view.dialogs

import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.material.*
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import androidx.compose.ui.window.Dialog
import viewmodel.graph.GraphViewModel

@Composable
fun CycleDetectionDialog(
graphViewModel: GraphViewModel,
onDismiss: () -> Unit,
onVertexSelected: (Int) -> Unit
) {
var selectedVertexId by remember { mutableStateOf<Int?>(null) }

Dialog(onDismissRequest = onDismiss) {
Surface(
modifier = Modifier
.width(400.dp)
.wrapContentHeight(),
shape = MaterialTheme.shapes.medium,
elevation = 8.dp
) {
Column(
modifier = Modifier.padding(16.dp),
horizontalAlignment = Alignment.CenterHorizontally
) {
Text(
"Find Cycles for Vertex",
style = MaterialTheme.typography.h6,
modifier = Modifier.padding(bottom = 16.dp)
)


LazyColumn(
modifier = Modifier
.height(200.dp)
.fillMaxWidth()
) {
items(graphViewModel.vertices.toList()) { vertexViewModel ->
Card(
modifier = Modifier
.fillMaxWidth()
.padding(4.dp)
.clickable {
selectedVertexId = vertexViewModel.origin.id
},
backgroundColor = if (selectedVertexId == vertexViewModel.origin.id)
MaterialTheme.colors.primary.copy(alpha = 0.1f)
else MaterialTheme.colors.surface,
elevation = 2.dp
) {
Text(
text = "Vertex ${vertexViewModel.origin.id}: ${vertexViewModel.origin.label}",
modifier = Modifier.padding(16.dp)
)
}
}
}

Spacer(modifier = Modifier.height(16.dp))

Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.SpaceBetween
) {
Button(onClick = onDismiss) {
Text("Cancel")
}

Button(
onClick = {
selectedVertexId?.let { vertexId ->
onVertexSelected(vertexId)
}
onDismiss()
},
enabled = selectedVertexId != null
) {
Text("Find Cycles")
}
}
}
}
}
}
Loading