Skip to content

Commit

Permalink
new datatype rangeSet which represents a combination of multiple numb…
Browse files Browse the repository at this point in the history
…erRanges (e.g. a intRange with gaps)

new helper class Graph (not testet yet)
  • Loading branch information
norganos committed Dec 8, 2023
1 parent d926261 commit bdadb5d
Show file tree
Hide file tree
Showing 20 changed files with 1,080 additions and 58 deletions.
100 changes: 100 additions & 0 deletions lib/src/main/kotlin/de/linkel/aoc/utils/CommonMath.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
package de.linkel.aoc.utils

import java.math.BigDecimal
import java.math.BigInteger
import kotlin.math.abs
import kotlin.math.max
import kotlin.math.min

class CommonMath {
companion object {
fun lcm(a: Int, b: Int): Int {
if (a == 0 || b == 0) throw ArithmeticException("could not find LCM for $a and $b")
val max = max(abs(a), abs(b))
val min = min(abs(a), abs(b))
return generateSequence(max) { it + max }
.first { it % min == 0 }
}

fun gcd(a: Int, b: Int): Int {
if (a == 0 || b == 0) throw ArithmeticException("could not find GCD for $a and $b")
return (min(abs(a), abs(b)) downTo 1).first { f ->
a % f == 0 && b % f == 0
}
}

fun lcm(a: Long, b: Long): Long {
if (a == 0L || b == 0L) throw ArithmeticException("could not find LCM for $a and $b")
val max = max(abs(a), abs(b))
val min = min(abs(a), abs(b))
return generateSequence(max) { it + max }
.first { it % min == 0L }
}

fun gcd(a: Long, b: Long): Long {
if (a == 0L || b == 0L) throw ArithmeticException("could not find GCD for $a and $b")
return (min(abs(a), abs(b)) downTo 1L).first { f ->
a % f == 0L && b % f == 0L
}
}

fun lcm(a: BigDecimal, b: BigDecimal): BigDecimal {
if (a.isZero() || b.isZero()) throw ArithmeticException("could not find LCM for $a and $b")
val max = a.abs().max(b.abs())
val min = a.abs().min(b.abs())
return generateSequence(max) { it + max }
.first { (it % min).isZero() }
}

fun gcd(a: BigDecimal, b: BigDecimal): BigDecimal {
if (a.isZero() || b.isZero()) throw ArithmeticException("could not find GCD for $a and $b")
return generateSequence(a.abs().min(b.abs())) { if (it > BigDecimal.ONE) it - BigDecimal.ONE else null }
.first { f ->
(a % f).isZero() && (b % f).isZero()
}
}

fun lcm(a: BigInteger, b: BigInteger): BigInteger {
if (a.isZero() || b.isZero()) throw ArithmeticException("could not find LCM for $a and $b")
val max = a.abs().max(b.abs())
val min = a.abs().min(b.abs())
return generateSequence(max) { it + max }
.first { (it % min).isZero() }
}

fun gcd(a: BigInteger, b: BigInteger): BigInteger {
if (a.isZero() || b.isZero()) throw ArithmeticException("could not find GCD for $a and $b")
return generateSequence(a.abs().min(b.abs())) { if (it > BigInteger.ONE) it - BigInteger.ONE else null }
.first { f ->
(a % f).isZero() && (b % f).isZero()
}
}
}
}

fun BigDecimal.isZero(): Boolean = this.compareTo(BigDecimal.ZERO) == 0
fun BigInteger.isZero(): Boolean = this.compareTo(BigInteger.ZERO) == 0

fun lcm(a: Int, b: Int) = CommonMath.lcm(a, b)
fun lcm(a: Long, b: Long) = CommonMath.lcm(a, b)
fun lcm(a: BigDecimal, b: BigDecimal) = CommonMath.lcm(a, b)
fun lcm(a: BigInteger, b: BigInteger) = CommonMath.lcm(a, b)
fun gcd(a: Int, b: Int) = CommonMath.gcd(a, b)
fun gcd(a: Long, b: Long) = CommonMath.gcd(a, b)
fun gcd(a: BigDecimal, b: BigDecimal) = CommonMath.gcd(a, b)
fun gcd(a: BigInteger, b: BigInteger) = CommonMath.gcd(a, b)

fun primes(): Sequence<Int> {
return sequence {
yield(2)
val sieve = mutableSetOf(2)
var num = 3
while (true) {
if (sieve.none { num % it == 0 }) {
yield(num)
sieve.add(num)
}
num += 2
}
}
}
9 changes: 0 additions & 9 deletions lib/src/main/kotlin/de/linkel/aoc/utils/IntRangeMixIn.kt

This file was deleted.

114 changes: 114 additions & 0 deletions lib/src/main/kotlin/de/linkel/aoc/utils/graph/Graph.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
package de.linkel.aoc.utils.graph

data class Node<K>(
val id: K,
val edges: Map<K, Int>
)

class Graph<K>(
nodes: Set<Node<K>>
) {
private val network = nodes.associateBy { it.id }

fun dijkstra(start: K, isDest: (id: K) -> Boolean): List<K>? {
val max = this.network.size + 1
val weightMap = network.mapValues { e -> DijkstraNode(e.key, if (e.key == start) 0 else max, null) }.toMutableMap()
val points = weightMap.keys.toMutableSet()
var dest: DijkstraNode<K>? = null
while (points.isNotEmpty()) {
val point = points.minBy { weightMap[it]!!.distance }
val pointWeightData = weightMap[point]!!
points.remove(point)
network[point]!!.edges
.filter { it.key in points }
.forEach {
weightMap[it.key] = weightMap[it.key]!!.copy(distance = pointWeightData.distance + it.value, before = point)
}
if (isDest(point)) {
dest = pointWeightData
break
}
}
return if (dest != null) {
var prev: DijkstraNode<K>? = dest
val result = mutableListOf<K>()
while (prev != null) {
result.add(0, prev.id)
prev = prev.before?.let { weightMap[it] }
}
result.toList()
} else {
null
}
}

fun dfs(start: K, isDest: (id: K) -> Boolean): List<K>? {
return dfsStep(start, isDest, emptyList())
}

private fun dfsStep(pos: K, isDest: (id: K) -> Boolean, path: List<K>): List<K>? {
return if (isDest(pos)) path
else network[pos]!!.edges.entries
.sortedBy { it.value }
.filter { it.key !in path }
.firstNotNullOfOrNull {
dfsStep(it.key, isDest, path + listOf(it.key))
}
}

fun bfs(start: K, isDest: (id: K) -> Boolean): List<K>? {
if (isDest(start)) {
return listOf(start)
}
return bfsStep(start, isDest, emptyList())
}

private fun bfsStep(pos: K, isDest: (id: K) -> Boolean, path: List<K>): List<K>? {
return network[pos]!!.edges.entries
.sortedBy { it.value }
.filter { it.key !in path }
.let { possibleNext ->
possibleNext
.firstNotNullOfOrNull {
if (isDest(it.key)) path + listOf(it.key) else null
}
?: possibleNext
.firstNotNullOfOrNull {
bfsStep(it.key, isDest, path + listOf(it.key))
}
}
}

data class DijkstraNode<K>(
val id: K,
val distance: Int,
val before: K?
)
}

class GraphBuilder<K> {
private val nodes = mutableMapOf<K, Map<K, Int>>()

fun node(id: K): GraphBuilder<K> {
nodes[id] = nodes[id] ?: emptyMap()
return this
}

fun edge(from: K, to: K, weight: Int = 1, bidirectional: Boolean = false): GraphBuilder<K> {
nodes[from] = (nodes[from] ?: emptyMap()) + mapOf(to to weight)
if (bidirectional) {
nodes[to] = (nodes[to] ?: emptyMap()) + mapOf(from to weight)
} else {
nodes[to] = (nodes[to] ?: emptyMap())
}
return this
}

fun build(): Graph<K> {
return Graph(
nodes.entries
.map { Node(it.key, it.value)}
.toSet()
)
}
}
3 changes: 0 additions & 3 deletions lib/src/main/kotlin/de/linkel/aoc/utils/grid/Grid.kt
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
package de.linkel.aoc.utils.grid

import java.lang.IllegalArgumentException

class Grid<T: Any>(
origin: Point = Point(0,0),
dimension: Dimension = Dimension(0,0)
Expand Down Expand Up @@ -250,5 +248,4 @@ class Grid<T: Any>(
val distance: Int,
val before: Point?
)

}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package de.linkel.aoc.utils
package de.linkel.aoc.utils.mixins

fun <T> Sequence<T>.prepend(element: T): Sequence<T> {
return if (this is ConcatSequence<T>) {
Expand Down
18 changes: 18 additions & 0 deletions lib/src/main/kotlin/de/linkel/aoc/utils/mixins/IntRangeMixIn.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package de.linkel.aoc.utils.mixins

fun IntRange.intersects(other: IntRange): Boolean {
return (this.first <= other.first && this.last >= other.first) || (other.first <= this.first && other.last >= this.first)
}

fun IntRange.intersect(other: IntRange): IntRange {
return if (this.intersects(other)) IntRange(kotlin.math.max(this.first, other.first), kotlin.math.min(this.last, other.last))
else IntRange.EMPTY
}

fun IntRange.extend(front: Int = 0, back: Int = 0): IntRange {
return IntRange(this.first - front, this.last + back)
}

fun IntRange.move(offset: Int): IntRange {
return IntRange(this.first + offset, this.last + offset)
}
17 changes: 17 additions & 0 deletions lib/src/main/kotlin/de/linkel/aoc/utils/mixins/LongRangeMixIn.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package de.linkel.aoc.utils.mixins

fun LongRange.intersects(other: LongRange): Boolean {
return (this.first <= other.first && this.last >= other.first) || (other.first <= this.first && other.last >= this.first)
}

fun LongRange.intersect(other: LongRange): LongRange {
return LongRange(kotlin.math.max(this.first, other.first), kotlin.math.min(this.last, other.last))
}

fun LongRange.extend(front: Long = 0, back: Long = 0): LongRange {
return LongRange(this.first - front, this.last + back)
}

fun LongRange.move(offset: Long): LongRange {
return LongRange(this.first + offset, this.last + offset)
}
21 changes: 21 additions & 0 deletions lib/src/main/kotlin/de/linkel/aoc/utils/mixins/NumberIterables.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package de.linkel.aoc.utils.mixins

import de.linkel.aoc.utils.CommonMath
import java.math.BigDecimal

fun Iterable<BigDecimal>.sum() = this.reduce(BigDecimal::plus)
fun Sequence<BigDecimal>.sum(): BigDecimal = this.reduce(BigDecimal::times)

fun Iterable<BigDecimal>.product() = this.reduce(BigDecimal::times)
fun Sequence<BigDecimal>.product() = this.reduce(BigDecimal::times)
fun Iterable<Int>.product() = this.reduce(Int::times)
fun Sequence<Int>.product() = this.reduce(Int::times)
fun Iterable<Long>.product() = this.reduce(Long::times)
fun Sequence<Long>.product() = this.reduce(Long::times)

fun Iterable<BigDecimal>.lcm() = this.reduce(CommonMath::lcm)
fun Sequence<BigDecimal>.lcm() = this.reduce(CommonMath::lcm)
fun Iterable<Int>.lcm() = this.reduce(CommonMath::lcm)
fun Sequence<Int>.lcm() = this.reduce(CommonMath::lcm)
fun Iterable<Long>.lcm() = this.reduce(CommonMath::lcm)
fun Sequence<Long>.lcm() = this.reduce(CommonMath::lcm)
Loading

0 comments on commit bdadb5d

Please sign in to comment.