Skip to content

Commit

Permalink
new mixins for combinations/permutations, package mixins renamed to i…
Browse files Browse the repository at this point in the history
…terables.

still some tests missing
  • Loading branch information
norganos committed Dec 13, 2023
1 parent bdadb5d commit e9a8447
Show file tree
Hide file tree
Showing 22 changed files with 576 additions and 22 deletions.
14 changes: 14 additions & 0 deletions lib/src/main/kotlin/de/linkel/aoc/utils/StringMixIn.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package de.linkel.aoc.utils

fun CharSequence.replaceIndex(idx: Int, char: Char): CharSequence {
if (idx < 0 || idx >= length)
throw IndexOutOfBoundsException("index $idx is out of bounds 0..${length-1}")
val sb = StringBuilder()
sb.appendRange(this, 0, idx)
sb.append(char)
sb.appendRange(this, idx + 1, length)
return sb
}

fun String.replaceIndex(idx: Int, char: Char): String =
(this as CharSequence).replaceIndex(idx, char).toString()
24 changes: 23 additions & 1 deletion lib/src/main/kotlin/de/linkel/aoc/utils/graph/Graph.kt
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ data class Node<K>(
)

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

Expand Down Expand Up @@ -79,6 +79,28 @@ class Graph<K>(
}
}

fun subGraphs(): Iterable<Graph<K>> {
val results = mutableListOf<Set<Node<K>>>()
val all = network.keys.toMutableSet()
while (all.isNotEmpty()) {
val first = all.first()
val current = mutableSetOf<K>()
val queue = mutableListOf(first)
while (queue.isNotEmpty()) {
val node = queue.removeAt(0)
current.add(node)
queue.addAll(
network[node]!!.edges.keys
.filter { it !in current }
)
}
results.add(current.map { network[it]!! }.toSet())
all.removeAll(current)
}
return results
.map { Graph(it) }
}

data class DijkstraNode<K>(
val id: K,
val distance: Int,
Expand Down
46 changes: 46 additions & 0 deletions lib/src/main/kotlin/de/linkel/aoc/utils/grid/Segment.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package de.linkel.aoc.utils.grid

import kotlin.math.abs
import kotlin.math.sign

data class Segment(
val x: Int,
val y: Int
) {
operator fun plus(v: Vector): Segment {
return copy(
x = x + v.deltaX,
y = y + v.deltaY
)
}
operator fun minus(p: Segment): Vector {
return Vector(
deltaX = x - p.x,
deltaY = y - p.y
)
}

operator fun rangeTo(p: Segment): List<Segment> {
if (p == this) {
return listOf(p)
}
val vector = p - this
return if (vector.deltaY == 0) {
(0 .. abs(vector.deltaX))
.map { i -> this.copy(
x = x + (i) * vector.deltaX.sign
)}
} else if (vector.deltaX == 0) {
(0 .. abs(vector.deltaY))
.map { i -> this.copy(
y = y + (i) * vector.deltaY.sign
)}
} else {
throw IllegalArgumentException("rangeTo only works in straight lines")
}
}

override fun toString(): String {
return "$x/$y"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
package de.linkel.aoc.utils.iterables

fun <T> List<T>.combinationPairs(
withSelf: Boolean = false,
withMirrors: Boolean = false
): Sequence<Pair<T,T>> {
val size = this.size
val list = this
return sequence {
(0 until size)
.forEach { i ->
((if (withMirrors) 0 else i) until size)
.forEach { j ->
if (withSelf || i != j) {
yield(Pair(list[i], list[j]))
}
}
}
}
}

fun <A, B> List<A>.combineWith(other: List<B>): Sequence<Pair<A, B>> {
val list = this
return sequence {
list
.forEach { a ->
other.forEach { b ->
yield(Pair(a, b))
}
}
}
}

fun <T> List<List<T>>.combinations(): Sequence<List<T>> {
val outer = this
return sequence {
val sizes = outer.map { it.size }
val indices = outer.map { 0 }.toMutableList()
while (indices.mapIndexed { i, o -> o < sizes[i] }.all { it }) {
yield(
indices.mapIndexed { i,o -> outer[i][o] }
)
if (outer.indices
.firstOrNull { i ->
indices[i] = indices[i] + 1
if (indices[i] >= sizes[i]) {
indices[i] = 0
false
} else true
} == null) {
break
}
}
}
}

fun <T> List<T>.permutations(maxSize: Int = -1): Sequence<List<T>> {
val list = this
return sequence {
if (list.isEmpty() || maxSize == 0) {
yield(emptyList())
} else {
list.indices.forEach { i ->
val item = list[i]
list.withoutIndex(i).permutations(maxSize - 1)
.forEach {
yield(it + item)
}
}
}
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package de.linkel.aoc.utils.mixins
package de.linkel.aoc.utils.iterables

fun <T> Sequence<T>.prepend(element: T): Sequence<T> {
return if (this is ConcatSequence<T>) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package de.linkel.aoc.utils.iterables


fun <T> List<T>.withoutIndex(blacklist: IntRange) = this.filterIndexed { idx, _ -> idx !in blacklist }
fun <T> List<T>.withoutIndex(index: Int) = this.filterIndexed { idx, _ -> idx != index }
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package de.linkel.aoc.utils.mixins
package de.linkel.aoc.utils.iterables

fun IntRange.intersects(other: IntRange): Boolean {
return (this.first <= other.first && this.last >= other.first) || (other.first <= this.first && other.last >= this.first)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package de.linkel.aoc.utils.mixins
package de.linkel.aoc.utils.iterables

fun LongRange.intersects(other: LongRange): Boolean {
return (this.first <= other.first && this.last >= other.first) || (other.first <= this.first && other.last >= this.first)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package de.linkel.aoc.utils.mixins
package de.linkel.aoc.utils.iterables

import de.linkel.aoc.utils.CommonMath
import java.math.BigDecimal
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package de.linkel.aoc.utils.iterables


fun <T> Sequence<T>.split(predicate: (T) -> Boolean): Sequence<List<T>> {
val input = this
return sequence {
val buffer = mutableListOf<T>()
input.forEach { element ->
if (predicate(element)) {
yield(buffer.toList())
buffer.clear()
} else {
buffer.add(element)
}
}
if (buffer.isNotEmpty()) {
yield(buffer.toList())
}
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package de.linkel.aoc.utils.rangeset

import de.linkel.aoc.utils.mixins.ConcatIterator
import de.linkel.aoc.utils.iterables.ConcatIterator

abstract class AbstractNumberRangeSet<O, T, R, C>(
ranges: Iterable<R>,
Expand All @@ -25,7 +25,6 @@ abstract class AbstractNumberRangeSet<O, T, R, C>(
cache.add(range)
}
}
println("$ranges -> $cache")
segments = cache.toList()
first = segments.firstOrNull()?.start ?: factory.maxValue
last = segments.lastOrNull()?.endInclusive ?: factory.minValue
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package de.linkel.aoc.utils.rangeset

import de.linkel.aoc.utils.mixins.intersect
import de.linkel.aoc.utils.mixins.intersects
import de.linkel.aoc.utils.iterables.intersect
import de.linkel.aoc.utils.iterables.intersects

class IntRangeSetFactory: RangeFactory<Int, IntRange, Int> {
companion object {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package de.linkel.aoc.utils.rangeset

import de.linkel.aoc.utils.mixins.intersect
import de.linkel.aoc.utils.mixins.intersects
import de.linkel.aoc.utils.iterables.intersect
import de.linkel.aoc.utils.iterables.intersects

class LongRangeSetFactory: RangeFactory<Long, LongRange, Long> {
companion object {
Expand Down
11 changes: 11 additions & 0 deletions lib/src/test/kotlin/de/linkel/aoc/utils/StringTest.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package de.linkel.aoc.utils

import org.assertj.core.api.Assertions
import org.junit.jupiter.api.Test

class StringTest {
@Test
fun `can replace s single character at a given position`() {
Assertions.assertThat("test".replaceIndex(1, '3')).isEqualTo("t3st")
}
}
Loading

0 comments on commit e9a8447

Please sign in to comment.