diff --git a/src/PrimesAndFactors.kt b/src/PrimesAndFactors.kt index 1035b97..350904a 100644 --- a/src/PrimesAndFactors.kt +++ b/src/PrimesAndFactors.kt @@ -1,4 +1,5 @@ import java.lang.Long.numberOfTrailingZeros +import java.math.BigInteger import java.util.* import kotlin.math.abs import kotlin.math.min @@ -211,6 +212,29 @@ fun extendedGcd(a: Long, b: Long): Pair> { return old_r to (old_s to old_t) } +fun extendedGcd(a: BigInteger, b: BigInteger): Pair> { + var old_r = a + var r = b + var old_s = BigInteger.ONE + var s = BigInteger.ZERO + var old_t = BigInteger.ZERO + var t = BigInteger.ONE + while (r != BigInteger.ZERO) { + val q = old_r / r + val rtmp = old_r + old_r = r + r = rtmp - q * r + val stmp = old_s + old_s = s + s = stmp - q * s + val ttmp = old_t + old_t = t + t = ttmp - q * t + } + + return old_r to (old_s to old_t) +} + fun extendedGcd(v: List): Pair> { if (v.size < 2) throw IllegalArgumentException("Expected at least 2 elements") @@ -235,6 +259,30 @@ fun extendedGcd(v: List): Pair> { return gcd to coeffs } +fun extendedGcdBigInteger(v: List): Pair> { + if (v.size < 2) throw IllegalArgumentException("Expected at least 2 elements") + + val gcds = ArrayList(v.size) + val coeffs = ArrayList(v.size) + var (gcd, p1) = extendedGcd(v[0], v[1]) + coeffs.add(p1.first) + coeffs.add(p1.second) + gcds.add(gcd) + gcds.add(gcd) + for (i in 2 until v.size) { + val (gcdnew, pi) = extendedGcd(gcd, v[i]) + gcd = gcdnew + coeffs.add(pi.second) + gcds.add(gcd) + } + for (i in gcds.indices) { + if (gcds[i] != gcd) { + coeffs[i] *= gcds[i] / gcd + } + } + return gcd to coeffs +} + fun calcPrimeFactorsAndPhi( n: Long, primes: MutableList, diff --git a/src/aoc2025/Day10.kt b/src/aoc2025/Day10.kt index d6e25c7..1f14ab9 100644 --- a/src/aoc2025/Day10.kt +++ b/src/aoc2025/Day10.kt @@ -1,13 +1,14 @@ package aoc2025 import chineseRemainder -import extendedGcd +import extendedGcdBigInteger import primeFactors import primeSequence import println import readInput import sieveOfErastosthenes import splitInts +import java.math.BigInteger /* --- Day 10: Factory --- @@ -68,24 +69,22 @@ fun main() { for (m in machines) { println() val maxJoltage = m.joltage.max() - val primefactors = primes.dropWhile { it <= maxJoltage }.take(m.size).map { it.toLong() } - if (primefactors.size != m.size) throw IllegalStateException() - val primePow = LongArray(m.size) - primePow[0] = primefactors[0] - for (i in 1 until m.size) { - primePow[i] = primePow[i - 1] * primefactors[i] + val numBits = 32 - maxJoltage.countLeadingZeroBits() + val bigTarget = m.joltage.foldIndexed(BigInteger.ZERO) { index, acc, i -> + acc.plus( + BigInteger.valueOf(i.toLong()).shiftLeft(numBits * index) + ) } - val bigTarget = m.joltage.foldIndexed(0L) { index, acc, i -> acc.plus(i.toLong() * primePow[index]) } - val bigToggles = ArrayList() + val bigToggles = ArrayList() for (t in m.toggles) { var tt = t - var bigToggle = 0L - var idx = 0 + var bigToggle = BigInteger.ZERO + var shift = 0 while (tt > 0) { if (tt and 1 != 0) { - bigToggle += primePow[idx] + bigToggle += BigInteger.ONE.shiftLeft(shift) } - idx++ + shift += numBits tt = tt shr 1 } bigToggles.add(bigToggle) @@ -94,12 +93,12 @@ fun main() { // j0 * bigtoggle[0] + j1 * bigtoggle[1] + ... = bigTarget // is a Linear Diophantine equation that can be solved with the extended Euclidean algorithm - val (gcd, coeffients) = extendedGcd(bigToggles) - if (bigTarget % gcd != 0L) throw IllegalStateException() + val (gcd, coeffients) = extendedGcdBigInteger(bigToggles) + if (bigTarget % gcd != BigInteger.ZERO) throw IllegalStateException() val factorMap = HashMap() var good = true - val primeFactors = primeFactors(bigTarget, sieve) + val primeFactors = primeFactors(bigTarget.toLong(), sieve) for (pf in primeFactors) { val sf = factorMap[pf] val pr = 1000 % pf