Skip to content
Merged
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
2 changes: 1 addition & 1 deletion .github/workflows/build.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
name: Library Test
name: Application Test
on:
push:
pull_request:
Expand Down
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
[![CodeFactor](https://www.codefactor.io/repository/github/spbu-coding-2024/graphs-team-3/badge)](https://www.codefactor.io/repository/github/spbu-coding-2024/graphs-team-3)

![example workflow](https://github.com/spbu-coding-2024/graphs-team-3/actions/workflows/build.yml/badge.svg)
![Kotlin](https://img.shields.io/badge/Kotlin-2.1.10-blue.svg)
![Gradle](https://img.shields.io/badge/Gradle-8.13-brightgreen.svg)
![Java](https://img.shields.io/badge/Java-21-brightgreen.svg)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package model.algo.fordBellman
package model.algo

import model.graph.Edge
import model.graph.Graph
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package model.algo.findBridges
package model.algo

import model.graph.Edge
import model.graph.Graph
Expand Down
22 changes: 13 additions & 9 deletions app/src/main/kotlin/model/algo/findCommunities.kt
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,18 @@ import model.graph.Graph
import model.graph.Vertex
import org.jetbrains.research.ictl.louvain.getPartition

fun findCommunities(graph: Graph, depth: Int = 2): Map<Int, List<Vertex>> {
val links = graph.edges.map {
EdgeLink(
from = it.vertices.first.id,
to = it.vertices.second.id,
weight = it.weight.toDouble()
)
}
fun findCommunities(
graph: Graph,
depth: Int = 2,
): Map<Int, List<Vertex>> {
val links =
graph.edges.map {
EdgeLink(
from = it.vertices.first.id,
to = it.vertices.second.id,
weight = it.weight.toDouble(),
)
}

val partition = getPartition(links, depth).toMutableMap()

Expand All @@ -28,6 +32,6 @@ fun findCommunities(graph: Graph, depth: Int = 2): Map<Int, List<Vertex>> {
return partition.entries
.groupBy(
keySelector = { it.value },
valueTransform= { idToVertex.getValue(it.key) }
valueTransform = { idToVertex.getValue(it.key) },
)
}
14 changes: 9 additions & 5 deletions app/src/main/kotlin/model/algo/findMST.kt
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package model.algo

import model.graph.Graph
import model.graph.Edge
import model.graph.Graph

fun findMST(graph: Graph): Set<Edge> {
require(!graph.isDirected) { "MST is only implemented for undirected graphs" }
Expand All @@ -10,9 +10,10 @@ fun findMST(graph: Graph): Set<Edge> {
val mstEdges = mutableSetOf<Edge>()

val verticesList = graph.vertices.toList()
val idToIndex = verticesList
.mapIndexed { index, vertex -> vertex.id to index }
.toMap()
val idToIndex =
verticesList
.mapIndexed { index, vertex -> vertex.id to index }
.toMap()

val parents = IntArray(verticesList.size) { -1 }

Expand All @@ -22,7 +23,10 @@ fun findMST(graph: Graph): Set<Edge> {
return parents[vertexId]
}

fun unionSets(firstId: Int, secondId: Int) {
fun unionSets(
firstId: Int,
secondId: Int,
) {
val rootFirst = findRoot(firstId)
val rootSecond = findRoot(secondId)
if (rootFirst == rootSecond) return
Expand Down
10 changes: 7 additions & 3 deletions app/src/main/kotlin/model/algo/findSCC.kt
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ fun findSCC(graph: Graph): Set<Set<Vertex>> {

fun dfs(vertex: Vertex) {
visited += vertex
for (neighbor in adjacencyList[vertex]?: throw IllegalStateException()) {
for (neighbor in adjacencyList[vertex] ?: throw IllegalStateException()) {
if (neighbor !in visited) dfs(neighbor)
}
stack.addFirst(vertex)
Expand All @@ -30,10 +30,14 @@ fun findSCC(graph: Graph): Set<Set<Vertex>> {
for (vertex in adjacencyList.keys) if (vertex !in visited) dfs(vertex)

visited.clear()
fun findComponents(vertex: Vertex, component: MutableSet<Vertex>) {

fun findComponents(
vertex: Vertex,
component: MutableSet<Vertex>,
) {
visited += vertex
component += vertex
for (neighbor in revAdjacencyList[vertex]?: throw IllegalStateException()) {
for (neighbor in revAdjacencyList[vertex] ?: throw IllegalStateException()) {
if (neighbor !in visited) findComponents(neighbor, component)
}
}
Expand Down
150 changes: 85 additions & 65 deletions app/src/main/kotlin/model/io/sqlite/sqliteRepository.kt
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,12 @@ package model.io.sqlite
import model.graph.*
import org.jetbrains.exposed.dao.id.IntIdTable
import org.jetbrains.exposed.sql.*
import org.jetbrains.exposed.sql.transactions.transaction
import org.jetbrains.exposed.sql.SqlExpressionBuilder.eq
import org.jetbrains.exposed.sql.transactions.transaction

class SqliteRepository(dbPath: String = "../graphs.db") {

class SqliteRepository(
dbPath: String = "../graphs.db",
) {
object Graphs : IntIdTable("graphs") {
val name = varchar("name", 52)
val isDirected = bool("isDirected")
Expand All @@ -27,71 +28,85 @@ class SqliteRepository(dbPath: String = "../graphs.db") {
val weight = long("weight")
}

private val db: Database = Database.connect(
url = "jdbc:sqlite:$dbPath?foreign_keys=ON",
driver = "org.sqlite.JDBC"
)
private val db: Database =
Database.connect(
url = "jdbc:sqlite:$dbPath?foreign_keys=ON",
driver = "org.sqlite.JDBC",
)

init {
transaction(db) {
SchemaUtils.create(Graphs, Vertices, Edges)
}
}

fun save(g: Graph, name: String? = null): Int = transaction(db) {
val gId = Graphs.insertAndGetId {
it[this.name] = name ?: "Graph_${System.currentTimeMillis()}"
it[this.isDirected] = g.isDirected
}.value
fun save(
g: Graph,
name: String? = null,
): Int =
transaction(db) {
val gId =
Graphs
.insertAndGetId {
it[this.name] = name ?: "Graph_${System.currentTimeMillis()}"
it[this.isDirected] = g.isDirected
}.value

Vertices.batchInsert(g.vertices) { vertex ->
this[Vertices.graph] = gId
this[Vertices.label] = vertex.label
this[Vertices.origId] = vertex.id
}

Vertices.batchInsert(g.vertices) { vertex ->
this[Vertices.graph] = gId
this[Vertices.label] = vertex.label
this[Vertices.origId] = vertex.id
}
Edges.batchInsert(g.edges) { edge ->
this[Edges.graph] = gId
this[Edges.fromId] = edge.vertices.first.id
this[Edges.toId] = edge.vertices.second.id
this[Edges.weight] = edge.weight
this[Edges.origId] = edge.id
}

Edges.batchInsert(g.edges) { edge ->
this[Edges.graph] = gId
this[Edges.fromId] = edge.vertices.first.id
this[Edges.toId] = edge.vertices.second.id
this[Edges.weight] = edge.weight
this[Edges.origId] = edge.id
gId
}

gId
}

fun delete(gId: Int) = transaction(db) {
Graphs.deleteWhere { id eq gId }
}

fun read(gId: Int): Graph = transaction(db) {
val directed = Graphs
.selectAll()
.where { Graphs.id eq gId }
.single()
.let { it[Graphs.isDirected] }

val g = Graph(isDirected = directed)

Vertices
.selectAll()
.where { Vertices.graph eq gId }
.forEach { row ->
g.addVertex(row[Vertices.origId], row[Vertices.label])
}

Edges
.selectAll()
.where { Edges.graph eq gId }
.forEach { row ->
g.addEdge(row[Edges.fromId], row[Edges.toId], row[Edges.weight])
}
fun delete(gId: Int) =
transaction(db) {
Graphs.deleteWhere { id eq gId }
}

g
}
fun read(gId: Int): Graph =
transaction(db) {
val directed =
Graphs
.selectAll()
.where { Graphs.id eq gId }
.single()
.let { it[Graphs.isDirected] }

val g = Graph(isDirected = directed)

Vertices
.selectAll()
.where { Vertices.graph eq gId }
.forEach { row ->
g.addVertex(row[Vertices.origId], row[Vertices.label])
}

Edges
.selectAll()
.where { Edges.graph eq gId }
.forEach { row ->
g.addEdge(row[Edges.fromId], row[Edges.toId], row[Edges.weight])
}

g
}

fun update(gId: Int, g: Graph, newName: String? = null) = transaction(db) {
fun update(
gId: Int,
g: Graph,
newName: String? = null,
) = transaction(db) {
Graphs.update({ Graphs.id eq gId }) {
it[isDirected] = g.isDirected
if (newName != null) it[name] = newName
Expand All @@ -115,14 +130,19 @@ class SqliteRepository(dbPath: String = "../graphs.db") {
}
}

fun listGraphs(filter: String = ""): List<Pair<Int, String>> = transaction(db) {
val q = if (filter.isBlank())
Graphs.selectAll()
else
Graphs.selectAll()
.where { Graphs.name like "%$filter%" }

q.orderBy(Graphs.name to SortOrder.ASC)
.map { it[Graphs.id].value to it[Graphs.name] }
}
fun listGraphs(filter: String = ""): List<Pair<Int, String>> =
transaction(db) {
val q =
if (filter.isBlank()) {
Graphs.selectAll()
} else {
Graphs
.selectAll()
.where { Graphs.name like "%$filter%" }
}

q
.orderBy(Graphs.name to SortOrder.ASC)
.map { it[Graphs.id].value to it[Graphs.name] }
}
}
14 changes: 7 additions & 7 deletions app/src/main/kotlin/view/dialogs/SqliteDeleteDialog.kt
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,11 @@ import androidx.compose.material.*
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.outlined.Warning
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.unit.dp
import androidx.compose.ui.Alignment
import androidx.compose.ui.graphics.vector.rememberVectorPainter
import androidx.compose.ui.unit.dp
import viewmodel.colors.ColorTheme

@Composable
Expand All @@ -32,31 +32,31 @@ fun SqliteDeleteDialog(
Text("Delete \"$graphName\"?")
}
},
text = { Text("This action cannot be undone")},
text = { Text("This action cannot be undone") },
buttons = {
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.End,
verticalAlignment = Alignment.CenterVertically
verticalAlignment = Alignment.CenterVertically,
) {
TextButton(
modifier = Modifier.padding(end = 16.dp),
onClick = {
open = false
onDismiss()
}
},
) { Text("Cancel", color = ColorTheme.TextColor) }
Button(
modifier = Modifier.padding(end = 16.dp),
onClick = {
onConfirm()
open = false
},
colors = ButtonDefaults.buttonColors(ColorTheme.rejectColor)
colors = ButtonDefaults.buttonColors(ColorTheme.rejectColor),
) { Text("Delete") }
}
},
modifier = Modifier.clip(RoundedCornerShape(percent = 5))
modifier = Modifier.clip(RoundedCornerShape(percent = 5)),
)
}
}
16 changes: 9 additions & 7 deletions app/src/main/kotlin/view/dialogs/SqliteOverwriteDialog.kt
Original file line number Diff line number Diff line change
Expand Up @@ -33,33 +33,35 @@ fun OverwriteDialog(
}
},
text = {
Text("Graph \"$graphName\" will be overwritten. Continue?") },
Text("Graph \"$graphName\" will be overwritten. Continue?")
},
buttons = {
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.End,
verticalAlignment = Alignment.CenterVertically
verticalAlignment = Alignment.CenterVertically,
) {
TextButton(
modifier = Modifier.padding(end = 16.dp),
onClick = {
open = false
onDismiss()
}
},
) { Text("Cancel", color = ColorTheme.TextColor) }
Button(
modifier = Modifier.padding(end = 16.dp),
onClick = {
onConfirm()
open = false
},
colors = ButtonDefaults.buttonColors(ColorTheme.rejectColor)
colors = ButtonDefaults.buttonColors(ColorTheme.rejectColor),
) { Text("Continue") }
}
},
modifier = Modifier
.fillMaxWidth()
.clip(RoundedCornerShape(percent = 5))
modifier =
Modifier
.fillMaxWidth()
.clip(RoundedCornerShape(percent = 5)),
)
}
}
Loading