A Kotlin Multiplatform utility library for Advent of Code challenges, featuring efficient data structures, algorithms, and convenient extension functions.
Every December, thousands of developers solve Advent of Code puzzles. In Kotlin, that means writing the same GCD function, the same DSU boilerplate, and the same binary search wrapper β year after year. Kodvent gives you battle-tested, zero-dependency implementations so you can focus on the puzzle, not the plumbing.
// Before: writing it from scratch every year
fun gcd(a: Long, b: Long): Long = if (b == 0L) a else gcd(b, a % b)
fun lcm(a: Long, b: Long): Long = a / gcd(a, b) * b
val answer = cycleLengths.reduce(::lcm)
// After: just import and solve
import kodvent.math.*
val answer = cycleLengths.reduce(::lcm)- Zero dependencies β pure Kotlin, nothing extra to pull in
- Multiplatform β works on JVM, JS, Wasm, and Native
- Idiomatic API β operator overloads, infix functions, extension functions
- Published to Maven Central β one line in your
build.gradle.kts
- JVM (target 1.8+)
- JS (IR, Node.js & Browser)
- Wasm (wasmJs, wasmWasi)
- Native β Linux (x64, arm64), macOS (x64, arm64), Windows (mingwX64), iOS, watchOS, tvOS
| Module | What you get | Complexity |
|---|---|---|
| Math | gcd, lcm, Long.pow (binary exponentiation), modular pow, sqr |
O(log n) for pow |
| Disjoint Set Union | find, union, connected, count, isolate with path compression + union by rank |
O(Ξ±(n)) amortized |
| Segment Tree | Range queries (sum, min, max, gcd, β¦) and point updates with tree[i, j] syntax |
O(log n) query/update |
| String Algorithms | KMP prefix function, allIndicesOf for pattern matching, generic sequence support |
O(n + m) |
| Binary Search | partitionPoint (Int and Long), lowerBound, upperBound with multiple overloads |
O(log n) |
| Ternary Search | Find the maximum of a unimodal function on an interval | O(log((rβl)/Ξ΅)) |
| Map Counters | MutableMap<T, Int>.increment / decrement for frequency maps |
O(1) |
For full API details and parameter descriptions, see the KDoc reference.
Add the dependency to your project:
kotlin {
sourceSets {
commonMain {
dependencies {
implementation("io.github.dmitrynekrasov:kodvent:0.3.1")
}
}
}
}dependencies {
implementation("io.github.dmitrynekrasov:kodvent:0.3.1")
}dependencies {
implementation 'io.github.dmitrynekrasov:kodvent:0.3.1'
}<dependency>
<groupId>io.github.dmitrynekrasov</groupId>
<artifactId>kodvent-jvm</artifactId>
<version>0.3.1</version>
</dependency>val frequencies = mutableMapOf<Char, Int>()
// Count character occurrences
"hello world".forEach { char ->
frequencies.increment(char)
}
// frequencies now contains: {h=1, e=1, l=3, o=2, w=1, r=1, d=1}// Greatest common divisor and the least common multiple
val g = gcd(48, 18) // 6
val l = lcm(12, 18) // 36
// Simplify a fraction
val divisor = gcd(48, 180)
val simplified = "${48 / divisor}/${180 / divisor}" // "4/15"
// Binary exponentiation with infix syntax
val result = 2L pow 10L // 1024
// Modular exponentiation β useful for number theory problems
val modulo = 1000000007L
val big = 2L.pow(100L, modulo) // 976371285
// Squared distance without temporary variables
val distanceSquared = dx.sqr() + dy.sqr()// Detect cycles in a graph
val dsu = DisjointSetUnion(4)
dsu.union(0, 1)
dsu.union(1, 2)
dsu.union(2, 3)
val hasCycle = !dsu.union(0, 3) // true - cycle detected!
// Kruskal's algorithm for Minimum Spanning Tree
data class Edge(val from: Int, val to: Int, val weight: Int)
val edges = listOf(
Edge(0, 1, 4),
Edge(0, 2, 2),
Edge(1, 2, 1),
Edge(1, 3, 5)
)
val dsuMst = DisjointSetUnion(4)
val mst = edges.sortedBy { it.weight }.filter { dsuMst.union(it.from, it.to) }// Range sum queries with bracket syntax
val numbers = listOf(1, 3, 5, 7, 9, 11)
val sumTree = SegmentTree(numbers, Int::plus)
sumTree[1, 4] // 24 (sum of 3 + 5 + 7 + 9)
sumTree[2] = 15 // point update
sumTree[2] += 10 // compound assignment
// Works with any associative operation
val minTree = SegmentTree(data, ::minOf)
val maxTree = SegmentTree(data, ::maxOf)
val gcdTree = SegmentTree(values, ::gcd)
// Safe queries for bounds you can't guarantee
sumTree.getOrNull(0, 100) // null (out of bounds)
sumTree.getOrDefault(0, 100, 0) // 0// Find all occurrences of a pattern in text
val indices = "ababcababa".allIndicesOf("aba") // [0, 5, 7]
// Finds overlapping patterns
"aaaa".allIndicesOf("aa") // [0, 1, 2]
// Period detection via prefix function
val pi = "abababab".prefixFunction()
val period = "abababab".length - pi.last() // 2
// Generic prefix function works with any sequence
val numPi = prefixFunction(numbers.size, numbers::get)// Binary search on answer: maximize minimum distance
// LeetCode 1552: Magnetic Force Between Two Balls
fun canPlaceBalls(positions: IntArray, m: Int, minDistance: Int): Boolean {
var ballsPlaced = 1
var lastPosition = positions[0]
for (i in 1..positions.lastIndex) {
if (positions[i] - lastPosition >= minDistance) {
ballsPlaced++
lastPosition = positions[i]
}
}
return ballsPlaced >= m
}
val positions = intArrayOf(1, 2, 3, 4, 7)
val m = 3
positions.sort()
val partitionPoint = partitionPoint(0, positions.last() + 1) { distance ->
canPlaceBalls(positions, m, distance)
}
val maxMinDistance = partitionPoint - 1 // 3val list = listOf(1, 3, 3, 3, 5, 5, 7, 9, 9, 9, 9, 11)
val lower = list.lowerBound(9) // 7 - index of first 9
val upper = list.upperBound(9) // 11 - index after last 9
val count = upper - lower // 4
// For elements that don't exist, lower == upper
list.upperBound(8) - list.lowerBound(8) // 0// A rectangle has a fixed perimeter P = 40.
// Side lengths: x and (P/2 - x). Area = x * (20 - x).
// Find x that maximizes area, with x in [0, 20].
val halfP = 40.0 / 2
val x = ternarySearch(0.0, halfP) { x -> x * (halfP - x) }
// x = 10.0 (maximum area when the rectangle is a square)./gradlew build./gradlew testThe project includes comprehensive tests for all functions. Test reports are generated at build/reports/tests/test/index.html.
This project is licensed under the Apache License 2.0 β see the LICENSE file for details.
Contributions are welcome! Here's how to get started:
- Check open issues for things to work on
- Fork the repository and create a feature branch
- Make sure
./gradlew buildpasses (the project usesallWarningsAsErrorsandexplicitApi) - Submit a Pull Request with a clear description of your changes
