#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
#include <limits.h>

#include "../../lib/aoc.h"

typedef long long stone;

typedef struct Stones {
  stone first;
  stone second;
} Stones;

typedef struct StonePile {
  stone value;
  long long count;
} StonePile;

int sort_stone_piles(const void *a, const void *b) {
  return ((*(StonePile*) a).value - (*(StonePile*) b).value) % INT_MAX;
}

int num_digits(stone s) {
  stone comp = 10;
  int digits = 1;
  while (s >= comp) {
    digits++;
    comp *= 10;
  }
  return digits;
}

int num_children(stone s, int generations_remaining) {
  if (generations_remaining == 0) return 1;

  int next_gen = generations_remaining - 1;
  int stone_num_digits = num_digits(s);
  int nc;

  if (s == 0ll) {
    nc = num_children(1, next_gen);
  } else if (stone_num_digits % 2 == 0) {
    stone comp = 10;
    for (int i = 1; i < stone_num_digits / 2; i++) comp *= 10;
    nc = num_children(s / comp, next_gen) + num_children(s % comp, next_gen);
  } else {
    nc = num_children(s * 2024, next_gen);
  }
  return nc;
}

Stones change(stone s) {
  Stones stones;
  int stone_num_digits = num_digits(s);

  if (s == 0ll) {
    stones.first = 1;
    stones.second = -1;
  } else if (stone_num_digits % 2 == 0) {
    stone comp = 10;
    for (int i = 1; i < stone_num_digits / 2; i++) comp *= 10;
    stones.first = s / comp;
    stones.second = s % comp;
  } else {
    stones.first = s * 2024;
    stones.second = -1;
  }

  return stones;
}

int main() {
  char *input = aoc_read_input();

  StonePile stone_array[50000], next_stone_array[50000];
  int stone_count = 0, next_stone_count = 0;
  char *current_token = strtok(input, " ");

  long long stone_count_sum = 0ll;
  long long big_stone_count_sum = 0ll;

  while (current_token != NULL) {
    stone_array[stone_count].value = atoll(current_token);
    stone_array[stone_count].count = 1ll;
    stone_count++;
    current_token = strtok(NULL, " ");
  }

  for (int i = 0; i < 75; i++) {
    next_stone_count = 0;
    for (int j = 0; j < stone_count; j++) {
      Stones s = change(stone_array[j].value);
      next_stone_array[next_stone_count].value = s.first;
      next_stone_array[next_stone_count].count = stone_array[j].count;
      next_stone_count++;
      if (s.second != -1) {
	next_stone_array[next_stone_count].value = s.second;
	next_stone_array[next_stone_count].count = stone_array[j].count;
	next_stone_count++;
      }
    }
    qsort(next_stone_array, next_stone_count, sizeof(StonePile), sort_stone_piles);

    stone_array[0].count = next_stone_array[0].count;
    stone_array[0].value = next_stone_array[0].value;
    stone_count = 1;

    for (int j = 1; j < next_stone_count; j++) {
      if (next_stone_array[j].value == stone_array[stone_count - 1].value) {
	stone_array[stone_count - 1].count += next_stone_array[j].count;
      } else {
	stone_count++;
	stone_array[stone_count - 1].value = next_stone_array[j].value;
	stone_array[stone_count - 1].count = next_stone_array[j].count;
      }
    }

    if (i == 24) {
      stone_count_sum = 0;
      for (int j = 0; j < stone_count; j++) {
	stone_count_sum += stone_array[j].count;
      }
    }

    if (i == 74) {
      big_stone_count_sum = 0;
      for (int j = 0; j < stone_count; j++) {
	big_stone_count_sum += stone_array[j].count;
      }
    }
  }


  printf("Part 1: %lld\n", stone_count_sum);
  printf("Part 2: %lld\n", big_stone_count_sum);
  
  aoc_free();
  return 0;
}