Skip to content
This repository has been archived by the owner on Jan 1, 2023. It is now read-only.

Commit

Permalink
Starting graph classes.
Browse files Browse the repository at this point in the history
  • Loading branch information
gchallen committed Oct 18, 2021
1 parent bac7e00 commit 67626a6
Show file tree
Hide file tree
Showing 5 changed files with 272 additions and 3 deletions.
1 change: 1 addition & 0 deletions .java-version
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
openjdk64-16
22 changes: 19 additions & 3 deletions build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,17 +1,23 @@
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile

group = "com.github.cs125-illinois"
version = "2021.8.1"
version = "2021.10.0"

plugins {
kotlin("jvm") version "1.5.30"
kotlin("jvm") version "1.5.31"
`maven-publish`
id("org.jmailen.kotlinter") version "3.5.1"
id("org.jmailen.kotlinter") version "3.6.0"
id("com.github.ben-manes.versions") version "0.39.0"
id("io.gitlab.arturbosch.detekt") version "1.18.1"
}
repositories {
mavenCentral()
maven("https://maven.pkg.jetbrains.space/public/p/kotlinx-html/maven")
}
dependencies {
testImplementation("io.kotest:kotest-runner-junit5:4.6.3")
testImplementation("org.slf4j:slf4j-simple:1.7.32")
}
tasks.dependencyUpdates {
fun String.isNonStable() = !(
listOf("RELEASE", "FINAL", "GA").any { toUpperCase().contains(it) }
Expand All @@ -30,3 +36,13 @@ publishing {
detekt {
buildUponDefaultConfig = true
}
tasks.withType<KotlinCompile> {
kotlinOptions {
jvmTarget = JavaVersion.VERSION_16.toString()
}
}
tasks.withType<Test> {
useJUnitPlatform()
enableAssertions = true
jvmArgs("-ea", "-Xmx1G", "-Xss256k", "-Dfile.encoding=UTF-8")
}
185 changes: 185 additions & 0 deletions src/main/java/cs125/graphs/UndirectedGraph.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,185 @@
package cs125.graphs

class Node<T>(val value: T, private val index: Int, val neighbors: MutableSet<Node<T>> = mutableSetOf()) {
override fun toString() = "Node $index ($value)"
}

class InputNode<T>(var value: T)

private fun <T> find(node: Node<T>, visited: MutableSet<Node<T>>) {
visited += node
for (neighbor in node.neighbors) {
if (neighbor !in visited) {
find(neighbor, visited)
}
}
}

private fun <T> find(node: Node<T>): Set<Node<T>> {
val nodes = mutableSetOf<Node<T>>()
find(node, nodes)
return nodes
}

fun <T> Map<InputNode<T>, Set<InputNode<T>>>.toGraph(): Node<T> {
check(isNotEmpty()) { "Graph is empty" }
val mapping = keys.mapIndexed { index, key -> key to Node(key.value, index) }.toMap()
forEach { (key, values) ->
check(mapping[key] != null) { "Missing mapping for key in graph creation" }
mapping[key]!!.neighbors.addAll(values.map {
mapping[it] ?: error("Missing mapping for value in graph creation")
})
}

mapping.values.forEach {
check(it !in it.neighbors) { "Graph contains a self-edge" }
check(find(it) == mapping.values.toSet()) { "Graph is not connected" }
for (node in it.neighbors) {
check(it in node.neighbors) { "Graph is not undirected" }
}
}
return mapping.values.first()
}

fun <T> singleNodeGraph(value: T) = mapOf(InputNode(value) to setOf<InputNode<T>>()).toGraph()

fun <T> twoNodeGraph(first: T, second: T): Node<T> {
val mapping = mapOf(first to InputNode(first), second to InputNode(second))
return mapOf(
mapping[first]!! to setOf(mapping[second]!!),
mapping[second]!! to setOf(mapping[first]!!)
).toGraph()
}

fun <T> circleGraph(list: List<T>): Node<T> {
require(list.size >= 2) { "List has fewer than two elements" }
val mapping = list.associateWith {
InputNode(it)
}
val edges = mapping.values.associateWith { mutableSetOf<InputNode<T>>() }
for (i in 0 until (list.size - 1)) {
edges[mapping[list[i]]]!! += mapping[list[i + 1]]!!
edges[mapping[list[i + 1]]]!! += mapping[list[i]]!!
}
edges[mapping[list[0]]]!! += mapping[list[list.size - 1]]!!
edges[mapping[list[list.size - 1]]]!! += mapping[list[0]]!!
return edges.toGraph()
}

fun <T> fullyConnectedGraph(list: List<T>): Node<T> {
require(list.size >= 2) { "List has fewer than two elements" }
val mapping = list.associateWith {
InputNode(it)
}
val edges = mapping.values.associateWith { mutableSetOf<InputNode<T>>() }
for (i in list.indices) {
for (j in list.indices - i) {
edges[mapping[list[i]]]!! += mapping[list[j]]!!
edges[mapping[list[j]]]!! += mapping[list[i]]!!
}
}
return edges.toGraph()
}

/*
class UndirectedGraph<T>(inputEdges: Map<InputNode<T>, Set<InputNode<T>>>) {
private val edges: Map<Node, Set<Node>>
val node: Node
init {
check(inputEdges.isNotEmpty()) { "Graph is empty" }
val edgeMapping = inputEdges.keys.mapIndexed { index, key ->
key to Node(key, index)
}.toMap()
edges = inputEdges.map { (key, values) ->
check(edgeMapping[key] != null) { "Missing mapping for key in graph creation" }
edgeMapping[key]!! to values.map { value ->
edgeMapping[value] ?: error("Missing mapping for value during graph creation")
}.toSet()
}.toMap()
for ((first, neighbors) in edges) {
check(first !in neighbors) { "Graph contains a self-edge" }
for (second in neighbors) {
check(second.connectedTo(first)) { "Graph is not undirected" }
}
check(first.find() == edges.keys) { "Graph is not connected" }
}
node = edges.keys.first()
}
inner class Node(var value: T, private val index: Int) {
constructor(graphNode: InputNode<T>, index: Int) : this(graphNode.value, index)
val neighbors: Set<Node>
get() = edges[this]!!
internal fun connectedTo(second: Node) = edges[this]?.contains(second) ?: false
internal fun find(): Set<Node> {
val nodes = mutableSetOf<Node>()
find(nodes)
return nodes
}
private fun find(visited: MutableSet<Node>) {
visited += this
for (neighbor in neighbors) {
if (neighbor !in visited) {
neighbor.find(visited)
}
}
}
override fun toString() = "Node $index ($value)"
}
companion object {
@JvmStatic
fun <T> singleNodeGraph(value: T) = UndirectedGraph(mapOf(InputNode(value) to setOf()))
@JvmStatic
fun <T> twoNodeGraph(first: T, second: T): UndirectedGraph<T> {
val mapping = mapOf(first to InputNode(first), second to InputNode(second))
return UndirectedGraph(
mapOf(
mapping[first]!! to setOf(mapping[second]!!),
mapping[second]!! to setOf(mapping[first]!!)
)
)
}
@JvmStatic
fun <T> circleGraph(list: List<T>): UndirectedGraph<T> {
require(list.size >= 2) { "List has fewer than two elements" }
val mapping = list.associateWith {
InputNode(it)
}
val edges = mapping.values.associateWith { mutableSetOf<InputNode<T>>() }
for (i in 0 until (list.size - 1)) {
edges[mapping[list[i]]]!! += mapping[list[i + 1]]!!
edges[mapping[list[i + 1]]]!! += mapping[list[i]]!!
}
edges[mapping[list[0]]]!! += mapping[list[list.size - 1]]!!
edges[mapping[list[list.size - 1]]]!! += mapping[list[0]]!!
return UndirectedGraph(edges)
}
@JvmStatic
fun <T> fullyConnectedGraph(list: List<T>): UndirectedGraph<T> {
require(list.size >= 2) { "List has fewer than two elements" }
val mapping = list.associateWith {
InputNode(it)
}
val edges = mapping.values.associateWith { mutableSetOf<InputNode<T>>() }
for (i in list.indices) {
for (j in list.indices - i) {
edges[mapping[list[i]]]!! += mapping[list[j]]!!
edges[mapping[list[j]]]!! += mapping[list[i]]!!
}
}
return UndirectedGraph(edges)
}
}
}
*/
33 changes: 33 additions & 0 deletions src/test/java/com/example/Example.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package com.example;

import cs125.graphs.Node;

import java.util.HashSet;
import java.util.Set;

public class Example {
public static <T> int size(Node<T> graph) {
Set<Node<T>> visited = new HashSet<>();
traverse(graph, visited);
return visited.size();
}

private static <T> void traverse(Node<T> node, Set<Node<T>> visited) {
visited.add(node);
for (Node<T> neighbor : node.getNeighbors()) {
if (!(visited.contains(neighbor))) {
traverse(neighbor, visited);
}
}
}

public static int sum(Node<Integer> graph) {
Set<Node<Integer>> visited = new HashSet<>();
traverse(graph, visited);
int sum = 0;
for (Node<Integer> node : visited) {
sum += node.getValue();
}
return sum;
}
}
34 changes: 34 additions & 0 deletions src/test/kotlin/TestUndirectedGraph.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import com.example.Example
import cs125.graphs.circleGraph
import cs125.graphs.fullyConnectedGraph
import cs125.graphs.singleNodeGraph
import cs125.graphs.twoNodeGraph
import io.kotest.core.spec.style.StringSpec
import io.kotest.matchers.shouldBe

class TestUndirectedGraph : StringSpec({
"it should create a single-node graph" {
singleNodeGraph(8).also {
Example.size(it) shouldBe 1
Example.sum(it) shouldBe 8
}
}
"it should create a two-node graph" {
twoNodeGraph(8, 16).also {
Example.size(it) shouldBe 2
Example.sum(it) shouldBe 24
}
}
"it should create a circular graph" {
circleGraph((0..31).toList()).also {
Example.size(it) shouldBe 32
Example.sum(it) shouldBe (0..31).sum()
}
}
"it should create a fully-connected graph" {
fullyConnectedGraph((32..63).toList()).also {
Example.size(it) shouldBe 32
Example.sum(it) shouldBe (32..63).sum()
}
}
})

0 comments on commit 67626a6

Please sign in to comment.