#!/bin/python3
import sys,time,re
from pprint import pprint
from collections import deque
sys.path.insert(0, '../../')
from fred import list2int,get_re,nprint,lprint,loadFile
start_time = time.time()

input_f = 'input'

part = 1
#########################################
#                                       #
#              Part 1                   #
#                                       #
#########################################

# Part 1 was first done using a list and each 
# number in the input list was converted to ints
# then each new list was created and passed through 
# again. 
# I also did the Part 1 using deque, but this wasn't
# any faster at all. 

def rule2(number):
    num_str = str(number)          
    middle = len(num_str) // 2
    return [int(num_str[:middle]), int(num_str[middle:])]

def part1(input_f):
    instructions = []
    with open(input_f) as file:
        instructions = list2int(file.readline().strip().split(' '))

    new_inst = []
    for x in range(25):
        for idx,inst in enumerate(instructions):

            if inst == 0:
                new_inst.append(1)
            else:
                if len(list(str(inst))) % 2 == 0:
                    new_inst += rule2(inst)
                else:
                    new_inst.append(instructions[idx] * 2024)
        instructions = new_inst
        new_inst = []
    
    return len(instructions) 


def part1_dq(input_f):
    dq = []
    with open(input_f) as file:
        dq = deque(file.readline().strip().split(' '))

    new_inst = deque([])
    for r in range(25):
        i = 0
        while_time = time.time()

        while i < len(dq):
            if dq[i] == 0:
                new_inst.append(1)
            else:
                if len(str(dq[i])) % 2 == 0:
                    x = rule2(dq[i])
                    new_inst.append(x[0])
                    new_inst.append(x[1])
                else:
                    new_inst.append(int(dq[i]) * 2024)
            i += 1
        dq = new_inst
        new_inst = deque([])
    return len(dq) 

start_time = time.time()
print('Part 1:',part1(input_f), '\t\t\t(list)\t', round((time.time() - start_time)*1000), 'ms')
start_time = time.time()
print('Part 1:',part1(input_f), '\t\t\t(deque)\t', round((time.time() - start_time)*1000), 'ms')

#########################################
#                                       #
#              Part 2                   #
#                                       #
#########################################

# Part 2 was not possible using either list or deque.
# I tested various calculations to see if there was a 
# pattern but i couldn't find one. 
# I started using simple recursion in hopes that 
# would speed it up. That was not the case.
# I ended up splitting the function in two and the part
# that returns a value from the rules, would be stored
# in a dict where the number and the current blink count 
# would be the key.
# If a number+blink combo was already in the dict, it 
# would use the already calculated result, instead
# of calculating it again. 
# I tested using functools.cache on the rules() function
# but that gave the same compute time.

def rule2(number):
    num_str = str(number)          
    middle = len(num_str) // 2
    return [str(int(num_str[:middle])), str(int(num_str[middle:]))]

def genNewNumbers(start,end):
    if end == 0:
        return 1
    end -= 1
    if (start,end) not in values:
        values[(start,end)] = rules(start,end)
    result = values[(start,end)]
    return result

def rules(start,end):
    if start == '0':
        result = genNewNumbers('1',end) 
    else:
        if len(start) % 2 == 0:
            x = rule2(start)
            result = 0
            result += genNewNumbers(x[0],end) 
            result += genNewNumbers(x[1],end)

        else:
            result = genNewNumbers(str(int(start)*2024),end)
    return result

values = {}

def part2(input_f):
    numbers = []
    with open(input_f) as file:
        numbers = file.readline().strip().split(' ')

    result = 0
    for i in numbers:
        result += genNewNumbers(i,75)
    return result

start_time = time.time()
print('Part 2:',part2(input_f), '\t(cache)\t', round((time.time() - start_time)*1000), 'ms')