package aoc2024

import println
import readInput
import splitLongs

/*
--- Day 7: Bridge Repair ---
https://adventofcode.com/2024/day/7
*/
fun main() {

    val inlineTestInput = """
190: 10 19
3267: 81 40 27
83: 17 5
156: 15 6
7290: 6 8 6 15
161011: 16 10 13
192: 17 8 14
21037: 9 7 18 13
292: 11 6 16 20
"""

    fun part1(input: List<String>): Long {
        var sum = 0L
        for (i in input) {
            val numbers = i.splitLongs(" ", ":")
            val eqres = numbers[0]
            val ops = numbers.drop(1)
            val combs = (1 shl (ops.size - 1))
            for (t in 0 until combs) {
                val res = ops.reduceIndexed { index, acc, l -> if (((1 shl (index - 1)) and t) != 0) acc * l else acc + l }
                if (res == eqres) {
                    sum += eqres
                    break
                }
            }
        }
        return sum
    }

    val powTenTable = generateSequence(1L) { it * 10L }.take(10).toList().toLongArray()
    val powThreeTable = generateSequence(1) { it * 3 }.take(12).toList().toIntArray()

    fun fastCeilLog10(n: Long): Int {
        for (i in 1 until 9) {
            if (powTenTable[i] > n) return i
        }
        return -1
    }

    fun part2(input: List<String>): Long {
        var sum = 0L
        for (i in input) {
            val numbers = i.splitLongs(" ", ":")
            val eqres = numbers[0]
            val ops = numbers.drop(1)
            val combs = powThreeTable[ops.size - 1]
            for (t in 0 until combs) {
                var res = ops[0]
                for (index in 1 until ops.size) {
                    val l = ops[index]
                    res = when ((t / powThreeTable[index - 1]) % 3) {
                        0 -> res + l
                        1 -> res * l
                        2 -> res * powTenTable[fastCeilLog10(l)] + l
                        else -> 0
                    }
                    if (res > eqres) break
                }
                if (res == eqres) {
                    sum += eqres
                    break
                }
            }
        }
        return sum
    }

    // test if implementation meets criteria from the description, like:
    val testInput = inlineTestInput.trim().reader().readLines()
    //val testInput = readInput("aoc2024/Day07_test")
    val testInputPart1Result = part1(testInput)
    println("Part 1 Test: $testInputPart1Result")
    val testInputPart2Result = part2(testInput)
    println("Part 2 Test: $testInputPart2Result")
    check(testInputPart1Result == 3749L)
    check(testInputPart2Result == 11387L)

    val input = readInput("aoc2024/Day07")
    part1(input).println()
    part2(input).println()
}
