Skip to content

Commit 14f2c94

Browse files
committed
Extracted some generic functions as utils.
1 parent dc4ae85 commit 14f2c94

File tree

5 files changed

+96
-59
lines changed

5 files changed

+96
-59
lines changed

solutions/aockt/util/Collections.kt

+30
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
package aockt.util
2+
3+
/**
4+
* Generate a [Sequence] of permutations of this collection.
5+
* For obvious reasons, will throw if the collection is too large.
6+
*/
7+
fun <T> Collection<T>.generatePermutations(): Sequence<List<T>> = when (size) {
8+
!in 0 .. 9 -> throw Exception("Too many permutations. This is probably not what you want to use.")
9+
0 -> emptySequence()
10+
1 -> sequenceOf(this.toList())
11+
else -> {
12+
val first = first()
13+
drop(1)
14+
.generatePermutations()
15+
.flatMap { perm -> (0..perm.size).map { perm.toMutableList().apply { add(it, first) } } }
16+
}
17+
}
18+
19+
/**
20+
* Returns the power set of the collection (all possible unordered combinations) except it's a [List] and not a [Set]
21+
* because repeated values are allowed.
22+
*/
23+
fun <T> Collection<T>.powerSet(): List<List<T>> = when (size) {
24+
0 -> listOf(emptyList())
25+
else -> {
26+
val first = listOf(first())
27+
val next = drop(1).powerSet()
28+
next.map { first + it } + next
29+
}
30+
}

solutions/aockt/y2015/Y2015D09.kt

+3-22
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,10 @@
11
package aockt.y2015
22

3+
import aockt.util.generatePermutations
34
import io.github.jadarma.aockt.core.Solution
45

56
object Y2015D09 : Solution {
67

7-
// The code for generating permutations is a slightly refactored version of Marcin Moskala's implementation,
8-
// because it's short and I want to keep dependencies to a minimum. Find the original at:
9-
// https://github.com/MarcinMoskala/KotlinDiscreteMathToolkit/blob/17a7329af042c5de232051027ec1155011d57da8/src/main/java/com/marcinmoskala/math/PermutationsExt.kt#L20
10-
// TODO: Make Util.
11-
private fun <T> List<T>.permutations(): Set<List<T>> = when (size) {
12-
0 -> emptySet()
13-
1 -> setOf(take(1))
14-
else -> drop(1)
15-
.permutations()
16-
.flatMap { sublist -> (0..sublist.size).map { sublist.plusAt(it, first()) } }
17-
.toSet()
18-
}
19-
20-
private fun <T> List<T>.plusAt(index: Int, element: T): List<T> = when (index) {
21-
!in 0..size -> throw Error("Cannot put at index $index because size is $size")
22-
0 -> listOf(element) + this
23-
size -> this + element
24-
else -> dropLast(size - index) + element + drop(index)
25-
}
26-
278
private val inputRegex = Regex("""^(\w+) to (\w+) = (\d+)$""")
289

2910
/** Parses a single line of input and returns a triple containing two locations and the distance between them. */
@@ -36,7 +17,7 @@ object Y2015D09 : Solution {
3617
* Given a list of distances between all pairs of locations on the map, returns all possible paths that visit all
3718
* of them and their total distance.
3819
*/
39-
private fun bruteForceRoutes(locationData: List<Triple<String, String, Int>>): List<Pair<List<String>, Int>> {
20+
private fun bruteForceRoutes(locationData: List<Triple<String, String, Int>>): Sequence<Pair<List<String>, Int>> {
4021
val locations = mutableSetOf<String>()
4122
val distances = mutableMapOf<Pair<String, String>, Int>()
4223

@@ -49,7 +30,7 @@ object Y2015D09 : Solution {
4930

5031
return locations
5132
.toList()
52-
.permutations()
33+
.generatePermutations()
5334
.map { route -> route to route.windowed(2).sumOf { distances.getValue(it[0] to it[1]) } }
5435
}
5536

solutions/aockt/y2015/Y2015D13.kt

+3-22
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,10 @@
11
package aockt.y2015
22

3+
import aockt.util.generatePermutations
34
import io.github.jadarma.aockt.core.Solution
45

56
object Y2015D13 : Solution {
67

7-
// The code for generating permutations is a slightly refactored version of Marcin Moskala's implementation,
8-
// because it's short and I want to keep dependencies to a minimum. Find the original at:
9-
// https://github.com/MarcinMoskala/KotlinDiscreteMathToolkit/blob/17a7329af042c5de232051027ec1155011d57da8/src/main/java/com/marcinmoskala/math/PermutationsExt.kt#L20
10-
// TODO: Make Util.
11-
private fun <T> List<T>.permutations(): Set<List<T>> = when (size) {
12-
0 -> emptySet()
13-
1 -> setOf(take(1))
14-
else -> drop(1)
15-
.permutations()
16-
.flatMap { sublist -> (0..sublist.size).map { sublist.plusAt(it, first()) } }
17-
.toSet()
18-
}
19-
20-
private fun <T> List<T>.plusAt(index: Int, element: T): List<T> = when (index) {
21-
!in 0..size -> throw Error("Cannot put at index $index because size is $size")
22-
0 -> listOf(element) + this
23-
size -> this + element
24-
else -> dropLast(size - index) + element + drop(index)
25-
}
26-
278
private val inputRegex = Regex("""^(\w+) would (lose|gain) (\d+) happiness units by sitting next to (\w+).$""")
289

2910
/**
@@ -44,7 +25,7 @@ object Y2015D13 : Solution {
4425
private fun bruteForceArrangement(
4526
guestData: List<Triple<String, String, Int>>,
4627
includeApatheticSelf: Boolean = false,
47-
): List<Pair<List<String>, Int>> {
28+
): Sequence<Pair<List<String>, Int>> {
4829
val guests = mutableSetOf<String>()
4930
val happinessScores = mutableMapOf<String, MutableMap<String, Int>>()
5031

@@ -68,7 +49,7 @@ object Y2015D13 : Solution {
6849

6950
return guests
7051
.toList()
71-
.permutations()
52+
.generatePermutations()
7253
.map { arrangement ->
7354
arrangement to arrangement
7455
.plusElement(arrangement.first())

solutions/aockt/y2015/Y2015D17.kt

+2-15
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,14 @@
11
package aockt.y2015
22

3+
import aockt.util.powerSet
34
import io.github.jadarma.aockt.core.Solution
45

56
object Y2015D17 : Solution {
67

7-
/**
8-
* Returns the power set of the [items] (all possible unordered combinations) except it's a [List] and not a [Set]
9-
* because repeated values are allowed.
10-
* TODO: Make Util.
11-
*/
12-
private fun powerSet(items: List<Int>): List<List<Int>> = when (items.size) {
13-
0 -> listOf(emptyList())
14-
else -> {
15-
val next = powerSet(items.drop(1))
16-
val nextWithThis = next.map { listOf(items.first()) + it }
17-
nextWithThis + next
18-
}
19-
}
20-
218
/** Parses the input and returns all valid ways of filling the containers. */
229
private fun commonPart(input: String): List<List<Int>> = input
2310
.lines().map(String::toInt)
24-
.let(this::powerSet)
11+
.powerSet()
2512
.filter { it.sum() == 150 }
2613

2714
override fun partOne(input: String) = commonPart(input).count()
+58
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
package aockt.util
2+
3+
import io.kotest.assertions.throwables.shouldThrow
4+
import io.kotest.core.spec.DisplayName
5+
import io.kotest.core.spec.style.FunSpec
6+
import io.kotest.matchers.collections.shouldContainExactlyInAnyOrder
7+
8+
@DisplayName("Utils: Collections")
9+
class CollectionsUtilTest : FunSpec({
10+
11+
context("Permutations") {
12+
test("generate simple permutations correctly") {
13+
val list = listOf(1, 2, 3)
14+
val permutations = listOf(
15+
listOf(1, 2, 3),
16+
listOf(1, 3, 2),
17+
listOf(2, 1, 3),
18+
listOf(2, 3, 1),
19+
listOf(3, 1, 2),
20+
listOf(3, 2, 1),
21+
)
22+
23+
list.generatePermutations().toList() shouldContainExactlyInAnyOrder permutations
24+
}
25+
26+
test("only work for small collections") {
27+
val list = listOf(1, 2, 3, 4, 5, 6, 7, 8, 9, 0)
28+
shouldThrow<Exception> { list.generatePermutations() }
29+
}
30+
}
31+
32+
context("Power Sets") {
33+
test("generate simple power sets") {
34+
val items = listOf(1, 2, 3)
35+
val powerSet = listOf(
36+
emptyList(),
37+
listOf(1),
38+
listOf(2),
39+
listOf(3),
40+
listOf(1, 2),
41+
listOf(1, 3),
42+
listOf(2, 3),
43+
listOf(1, 2, 3),
44+
)
45+
items.powerSet().also(::println) shouldContainExactlyInAnyOrder powerSet
46+
}
47+
test("work with duplicate items") {
48+
val items = listOf(1, 1)
49+
val powerSet = listOf(
50+
emptyList(),
51+
listOf(1),
52+
listOf(1),
53+
listOf(1, 1),
54+
)
55+
items.powerSet().also(::println) shouldContainExactlyInAnyOrder powerSet
56+
}
57+
}
58+
})

0 commit comments

Comments
 (0)