package aoc2025

import println
import readInput
import kotlin.math.log10

/*
--- Day 2: Gift Shop ---
https://adventofcode.com/2025/day/2
*/
fun main() {

    val inlineTestInput = """
11-22,95-115,998-1012,1188511880-1188511890,222220-222224,1698522-1698528,446443-446449,38593856-38593862,565653-565659,824824821-824824827,2121212118-2121212124
"""

    fun part1(input: List<String>): Long {
        val ranges = input[0].split(",").map { it.split("-").map { it.toLong() } }
        val powTenTable = generateSequence(1L) { it * 10L }.take(20).toList().toLongArray()
        var sum = 0L
        for ((low, high) in ranges) {
            var i = low
            while (i <= high) {
                val numDigits = log10(i.toDouble()).toInt() + 1
                if (numDigits % 2 == 1) {
                    i = powTenTable[numDigits]
                } else {
                    val firstHalf = i / powTenTable[numDigits / 2]
                    val matchNum = firstHalf * powTenTable[numDigits / 2] + firstHalf
                    if (matchNum in low..high) {
                        sum += matchNum
                    }
                    i = (firstHalf + 1) * powTenTable[numDigits / 2]
                }
            }
        }
        return sum
    }

    fun part2(input: List<String>): Long {
        val ranges = input[0].split(",").map { it.split("-").map { it.toLong() } }
        val powTenTable = generateSequence(1L) { it * 10L }.take(20).toList().toLongArray()
        val mulElevenArray = arrayOf(
            generateSequence(1L) { it * 10L + 1L }.take(20).toList().toLongArray(),
            generateSequence(1L) { it * 100L + 1L }.take(10).toList().toLongArray(),
            generateSequence(1L) { it * 1000L + 1L }.take(7).toList().toLongArray(),
            generateSequence(1L) { it * 10000L + 1L }.take(5).toList().toLongArray(),
            generateSequence(1L) { it * 100000L + 1L }.take(4).toList().toLongArray(),
            generateSequence(1L) { it * 1000000L + 1L }.take(4).toList().toLongArray(),
            generateSequence(1L) { it * 10000000L + 1L }.take(3).toList().toLongArray(),
            generateSequence(1L) { it * 100000000L + 1L }.take(3).toList().toLongArray(),
        )

        var sum = 0L
        for ((low, high) in ranges) {
            var i = low
            val illegalSet = mutableSetOf<Long>()
            while (i <= high) {
                val numDigits = log10(i.toDouble()).toInt() + 1
                for (n in 1..numDigits / 2) {
                    val firstBit = i / powTenTable[numDigits - n]
                    val matchNum = firstBit * mulElevenArray[n - 1][(numDigits - 1) / n]
                    illegalSet.add(matchNum)
                }
                val firstHalf = i / powTenTable[numDigits / 2]
                i = (firstHalf + 1) * powTenTable[numDigits / 2]
            }
            sum += illegalSet.filter { it in low..high }.sum()
        }
        return sum
    }

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

    val input = readInput("aoc2025/Day02")
    part1(input).println()
    part2(input).println()
}
