This repository has been archived by the owner on Jan 1, 2023. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
5 changed files
with
272 additions
and
3 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
openjdk64-16 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) | ||
} | ||
} | ||
} | ||
*/ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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() | ||
} | ||
} | ||
}) |