0ebe6569603d9f2853f62ca9133645a75b78b984 — Michael Captain 1 year, 7 months ago
```Complete 2019 Day 16 Part B
```
```2 files changed, 37 insertions(+), 16 deletions(-)

```
`M src/main/advent2019/Day16.kt => src/main/advent2019/Day16.kt +35 -14`
```@@ 1,26 1,47 @@

-import common.countFrom
+import common.repeat
import kotlin.math.absoluteValue

-fun pattern(n: Int): Sequence<Int> {
-    val basePattern = listOf(0, 1, 0, -1)
-    return countFrom(0).map { basePattern[(it / n) % 4] }.drop(1)
-}
-
fun solve16a(s: String, phases: Int = 100): String {
-    val data = s.map { it.digitToInt() }
-    val seq = generateSequence(data) { phase ->
-        (1..data.count()).map {
-            phase.asSequence().zip(pattern(it)).sumOf { (a, b) -> a * b }.let { n ->
-                (n % 10).absoluteValue
-            }
+    val signal = s.map { it.digitToInt() }
+    val seq = generateSequence(signal) { phase ->
+        (1..signal.count()).map { n ->
+            (phase.foldIndexed(0) { i, sum, d ->
+                sum + when (((i + 1) / n) % 4) {
+                    1 -> d
+                    3 -> -d
+                    else -> 0
+                }
+            } % 10).absoluteValue
}
-    }.elementAt(phases)
-    return seq.take(8).joinToString("")
+    }
+    return seq.elementAt(phases).take(8).joinToString("")
+}
+
+fun solve16b(s: String, phases: Int = 100): String {
+    val signal = s.map { it.digitToInt() }.asSequence().repeat().take(s.count() * 10_000).toList()
+    val messageOffset = s.take(7).toInt()
+    // digits earlier in the signal don't affect digits later in the signal, so we can drop to the point we're interested in
+    val offsetSignal = signal.drop(messageOffset)
+    assert(offsetSignal.count() <= signal.count() / 2) {
+        "if the offset puts us in the first half of the signal, " +
+                "we can't treat the pattern matrix as upper triangular with alls 1s, " +
+                "so the optimisation used to solve this problem won't work"
+    }
+    val seq = generateSequence(offsetSignal) { phase ->
+        // past a certain point, n becomes sufficiently large that all that matters is whether when the index is divided
+        // by n whether you get 0 or 1, i.e. whether you fall into the 0s or the 1s part of the pattern.
+        // in fact, the 1s start coming in when the index is exactly n, the applied matrix is upper triangular with
+        // all 1s. So applying the dot product from the bottom lines upwards, each digit of the next phase is the
+        // units digit of the running sum backwards.
+        phase.reversed().runningReduce { a, b -> a + b }.reversed().map { (it % 10).absoluteValue }
+    }
+    return seq.elementAt(phases).take(8).joinToString("")
}

fun main() {
val input = input("day16").first()
println(solve16a(input)) // 27229269
+    println(solve16b(input)) // 26857164
}=
\ No newline at end of file

```
`M src/test/advent2019/Day16KtTest.kt => src/test/advent2019/Day16KtTest.kt +2 -2`
```@@ 16,10 16,10 @@ internal class Day16KtTest {
assertEquals("52432133", solve16a("69317163492948606335995924319873"))
}

-    /*@Test
+    @Test
fun solve16b() {
assertEquals("84462026", solve16b("03036732577212944063491565474664"))
assertEquals("78725270", solve16b("02935109699940807407585447034323"))
assertEquals("53553731", solve16b("03081770884921959731165446850517"))
-    }*/
+    }
}=
\ No newline at end of file

```