Skip to content

Commit

Permalink
Migrate the project to KMP
Browse files Browse the repository at this point in the history
  • Loading branch information
alexandrepiveteau committed Apr 23, 2023
1 parent c91f46f commit 0ea9ccc
Show file tree
Hide file tree
Showing 46 changed files with 2,287 additions and 45 deletions.
32 changes: 23 additions & 9 deletions build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,19 +1,33 @@
plugins {
kotlin("jvm") version "1.8.20"
kotlin("multiplatform") version "1.8.20"
id("org.jetbrains.kotlinx.binary-compatibility-validator") version "0.13.0"
id("org.jetbrains.dokka") version "1.8.10"
}

repositories { mavenCentral() }

dependencies {
implementation(kotlin("stdlib"))
testImplementation(kotlin("test"))
// For future benchmarking.
// testImplementation("com.google.guava:guava:31.1-jre")
}

kotlin {
explicitApi()
sourceSets.all { languageSettings.optIn("kotlin.contracts.ExperimentalContracts") }
jvm()
js(IR) {
browser()
nodejs()
}
sourceSets {
val commonMain by getting { dependencies { implementation(kotlin("stdlib-common")) } }
val commonTest by getting {
dependencies {
implementation(kotlin("test-common"))
implementation(kotlin("test-annotations-common"))
}
}
val jvmMain by getting
val jvmTest by getting { dependencies { implementation(kotlin("test-junit")) } }
val jsMain by getting
val jsTest by getting { dependencies { implementation(kotlin("test-js")) } }
all { languageSettings.optIn("kotlin.contracts.ExperimentalContracts") }
}
}

// For future benchmarking.
// testImplementation("com.google.guava:guava:31.1-jre")
2,198 changes: 2,198 additions & 0 deletions kotlin-js-store/yarn.lock

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package io.github.alexandrepiveteau.graphs
import io.github.alexandrepiveteau.graphs.util.packInts
import io.github.alexandrepiveteau.graphs.util.unpackInt1
import io.github.alexandrepiveteau.graphs.util.unpackInt2
import kotlin.jvm.JvmInline

/** A directed arc in a [UndirectedGraph] between two [Vertex]. */
@JvmInline
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package io.github.alexandrepiveteau.graphs
import io.github.alexandrepiveteau.graphs.util.packInts
import io.github.alexandrepiveteau.graphs.util.unpackInt1
import io.github.alexandrepiveteau.graphs.util.unpackInt2
import kotlin.jvm.JvmInline
import kotlin.math.max
import kotlin.math.min

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package io.github.alexandrepiveteau.graphs

import kotlin.jvm.JvmInline

/**
* A vertex in a [Graph], which is uniquely identified. Vertices can be compared to each other, and
* are totally ordered within a [Graph] instance.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package io.github.alexandrepiveteau.graphs

import kotlin.jvm.JvmInline

/** Creates a new array of the specified [size], with all elements initialized to zero. */
public fun VertexArray(size: Int): VertexArray = VertexArray(IntArray(size))

Expand Down Expand Up @@ -152,4 +154,26 @@ public fun VertexArray.binarySearch(
element: Vertex,
fromIndex: Int = 0,
toIndex: Int = size,
): Int = array.binarySearch(element.index, fromIndex, toIndex)
): Int {
if (fromIndex < 0) throw IndexOutOfBoundsException()
if (toIndex > size) throw IndexOutOfBoundsException()
if (fromIndex > toIndex) throw IllegalArgumentException()

var low = fromIndex
var high = toIndex - 1

while (low <= high) {
val mid = (low + high).ushr(1) // safe from overflows
val midVal = get(mid)
val cmp = midVal.index.compareTo(element.index)

if (cmp < 0) {
low = mid + 1
} else if (cmp > 0) {
high = mid - 1
} else {
return mid
} // key found
}
return -(low + 1) // key not found
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package io.github.alexandrepiveteau.graphs

import kotlin.jvm.JvmInline

/**
* Creates a new [VertexMap] with the given [size], with all elements initialized to the result of
* calling the specified [init] function on the index of each element.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import kotlin.test.assertTrue
class ArcTests {

@Test
fun `opposite arcs are not equal`() {
fun oppositeArcsAreNotEqual() {
val a = Vertex(0)
val b = Vertex(1)
val e = a arcTo b
Expand All @@ -17,7 +17,7 @@ class ArcTests {
}

@Test
fun `arcs are properly reversed`() {
fun arcsAreProperlyReversed() {
val a = Vertex(0)
val b = Vertex(1)
val e = a arcTo b
Expand All @@ -27,7 +27,7 @@ class ArcTests {
}

@Test
fun `arc contains both extremities`() {
fun arcContainsBothExtremities() {
val a = Vertex(0)
val b = Vertex(1)
val e = a arcTo b
Expand All @@ -36,7 +36,7 @@ class ArcTests {
}

@Test
fun `arc destructuring returns extremities in order`() {
fun arcDestructuringReturnsExtremitiesInOrder() {
val a = Vertex(0)
val b = Vertex(1)
val e = a arcTo b
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import kotlin.test.Test
class DirectedNetworkFlowsTests {

@Test
fun `no edges has empty flow`() {
fun noEdgesHasEmptyFlow() {
val capacities = buildDirectedNetwork {
addVertex()
addVertex()
Expand All @@ -22,7 +22,7 @@ class DirectedNetworkFlowsTests {
}

@Test
fun `single arc has non-empty flow`() {
fun singleArcHasNonEmptyFlow() {
val capacities = buildDirectedNetwork {
val (a, b) = addVertices()
addArc(a arcTo b, 1)
Expand All @@ -41,7 +41,7 @@ class DirectedNetworkFlowsTests {
}

@Test
fun `two arcs has minimum of capacities`() {
fun twoArcsHasMinimumOfCapacities() {
val capacities = buildDirectedNetwork {
val (a, b, c) = addVertices()
addArc(a arcTo b, 1)
Expand All @@ -62,7 +62,7 @@ class DirectedNetworkFlowsTests {
}

@Test
fun `three arcs has sum of capacities`() {
fun threeArcsHasSumOfCapacities() {
val capacities = buildDirectedNetwork {
val (a, b, c, d) = addVertices()
addArc(a arcTo b, 1)
Expand All @@ -87,7 +87,7 @@ class DirectedNetworkFlowsTests {
}

@Test
fun `wikipedia example has right total flow`() {
fun wikipediaExampleHasCorrectTotalFlow() {
// Source: https://en.wikipedia.org/wiki/Edmonds–Karp_algorithm
val capacities = buildDirectedNetwork {
val (a, b, c, d, e, f, g) = addVertices()
Expand All @@ -112,7 +112,7 @@ class DirectedNetworkFlowsTests {
}

@Test
fun `complete graphs`() {
fun completeGraphsHaveCorrectTotalFlow() {
for (count in 2 until Repeats) {
val capacities = buildDirectedNetwork {
val vertices = VertexArray(count) { addVertex() }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import kotlin.test.assertTrue
class EdgeTests {

@Test
fun `opposite edges are equal`() {
fun oppositeEdgesAreEqual() {
val a = Vertex(0)
val b = Vertex(1)
val e = a edgeTo b
Expand All @@ -17,7 +17,7 @@ class EdgeTests {
}

@Test
fun `edge contains both extremities`() {
fun edgeContainsBothExtremities() {
val a = Vertex(0)
val b = Vertex(1)
val e = a edgeTo b
Expand All @@ -26,7 +26,7 @@ class EdgeTests {
}

@Test
fun `other returns other edge extremity`() {
fun otherReturnsOtherEdgeExtremity() {
val a = Vertex(0)
val b = Vertex(1)
val e = a edgeTo b
Expand All @@ -36,7 +36,7 @@ class EdgeTests {
}

@Test
fun `any returns an extremity`() {
fun anyReturnsAnExtremity() {
val a = Vertex(1000)
val b = Vertex(1001)

Expand All @@ -45,7 +45,7 @@ class EdgeTests {
}

@Test
fun `other with an unknown vertex throws an IllegalArgumentException`() {
fun otherWithAnUnknownVertexThrowsAnIllegalArgumentException() {
val a = Vertex(0)
val b = Vertex(1)
val c = Vertex(2)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import kotlin.test.assertFalse
class UndirectedEmptyGraphTests {

@Test
fun `empty graph has no vertices nor edges`() {
fun emptyGraphHasNoVerticesNorEdges() {
val graph = UndirectedGraph.empty()

assertEquals(0, graph.size)
Expand All @@ -25,12 +25,12 @@ class UndirectedEmptyGraphTests {
class UndirectedCompleteGraphTests {

@Test
fun `complete graph has non-zero vertex count`() {
fun completeGraphHasNonZeroVertexCount() {
assertFailsWith<IllegalArgumentException> { UndirectedGraph.complete(-1) }
}

@Test
fun `complete graph has all edges`() {
fun completeGraphHasAllEdges() {
for (count in 0..Repeats) {
val graph = UndirectedGraph.complete(count)
for (i in 0 until count) {
Expand All @@ -46,7 +46,7 @@ class UndirectedCompleteGraphTests {
}

@Test
fun `complete graph has no self-edges`() {
fun completeGraphHasNoSelfEdges() {
for (count in 0..Repeats) {
val graph = UndirectedGraph.complete(count)
for (i in 0 until count) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,15 @@ import kotlin.test.assertEquals
class SCCTests {

@Test
fun `empty graph has no scc`() {
fun emptyGraphHasNoScc() {
val graph = UndirectedGraph.empty()
val (scc, map) = graph.scc()
assertEquals(0, scc.size)
assertEquals(0, map.size)
}

@Test
fun `complete graph has one scc`() {
fun completeGraphHasOneScc() {
for (count in 1..Repeats) {
val graph = UndirectedGraph.complete(count)
val (scc, map) = graph.scc()
Expand All @@ -25,7 +25,7 @@ class SCCTests {
}

@Test
fun `disjoint graph with n vertices has n scc`() {
fun disjointGraphWithNVerticesHasNScc() {
for (count in 1..Repeats) {
val graph = buildUndirectedGraph { repeat(count) { addVertex() } }
val (scc, map) = graph.scc()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import kotlin.test.assertFalse
class UndirectedGraphDFSTests {

@Test
fun `forEachVertex iterates exactly once over each vertex`() {
fun forEachVertexIteratesExactlyOnceOverEachVertex() {
for (count in 0..Repeats) {
val graph = UndirectedGraph.complete(count)
val visited = BooleanArray(count)
Expand All @@ -21,15 +21,15 @@ class UndirectedGraphDFSTests {
}

@Test
fun `bfs on singleton graph visits one vertex`() {
fun bfsOnSingletonGraphVisitsOneVertex() {
val graph = UndirectedGraph.complete(1)
var visited = 0
graph.forEachVertexBreadthFirst(graph[0]) { visited++ }
assertEquals(1, visited)
}

@Test
fun `bfs on line graph visits all vertices`() {
fun bfsOnLineGraphVisitsAllVertices() {
for (count in 1..Repeats) {
val graph = buildUndirectedGraph {
var last = addVertex()
Expand All @@ -46,15 +46,15 @@ class UndirectedGraphDFSTests {
}

@Test
fun `dfs on singleton graph visits one vertex`() {
fun dfsOnSingletonGraphVisitsOneVertex() {
val graph = UndirectedGraph.complete(1)
var visited = 0
graph.forEachVertexDepthFirst(graph[0]) { visited++ }
assertEquals(1, visited)
}

@Test
fun `dfs on line graph visits all vertices`() {
fun dfsOnLineGraphVisitsAllVertices() {
for (count in 1..Repeats) {
val graph = buildUndirectedGraph {
var last = addVertex()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,21 +8,21 @@ import kotlin.test.assertFailsWith
class UndirectedNetworkTraversalTests {

@Test
fun `spfa on empty network throws exception`() {
fun spfaOnEmptyNetworkThrowsException() {
assertFailsWith<NoSuchVertexException> {
UndirectedNetwork.empty().shortestPathFasterAlgorithm(Vertex(0))
}
}

@Test
fun `spfa on singleton associates distance zero`() {
fun spfaOnSingletonAssociatesDistanceZero() {
val graph = buildUndirectedNetwork { addVertex() }
val spfa = graph.shortestPathFasterAlgorithm(graph[0])
assertEquals(0, spfa[0])
}

@Test
fun `spfa on complete graph associates distance one except for source`() {
fun spfaOnCompleteGraphAssociatesDistanceOneExceptForSource() {
for (count in 1 until Repeats) {
val graph = UndirectedNetwork.complete(count, 1)
val spfa = graph.shortestPathFasterAlgorithm(graph[0])
Expand All @@ -34,7 +34,7 @@ class UndirectedNetworkTraversalTests {
}

@Test
fun `spfa computes minimum distance on simple graph`() {
fun spfaComputesMinimumDistanceOnSimpleGraph() {
val graph = buildUndirectedNetwork {
val (a, b, c, d, e) = addVertices()
addEdge(a edgeTo b, 1)
Expand Down
Loading

0 comments on commit 0ea9ccc

Please sign in to comment.