import std.conv;
import std.typecons;

import dayutil;

immutable size_t TIMES = 2020;
immutable size_t TIMES2 = 30000000;

Variant run(int part, File input, bool bigboy, string[] args) {
	int[] numbers = input.byLineCopy.joiner(",").array.splitter(",").map!(to!int).array;
	return Variant(parts!int(part,
			() => part1(numbers),
			() => part2(numbers)));
}

int part1(int[] startingNumbers) {
	int[] memory;
	memory.reserve(2020);

	memory ~= startingNumbers;

	int previous = startingNumbers[$ - 1];
	foreach(index; memory.length..TIMES) {
		ptrdiff_t last = memory[0..$ - 1].retro.countUntil(previous) + 1;

		previous = cast(int) last;

		memory ~= [previous];
	}

	return previous;
}

int part2(int[] startingNumbers) {
	int[int] memory;

	foreach(idx, num; startingNumbers[0..$ - 1]) {
		memory[num] = cast(int) idx + 1;
	}

	int previousNumber = startingNumbers[$ - 1];

	foreach(index; memory.length..TIMES2 - 1) {
		int lastSpoken = memory.get(previousNumber, 0);
		int newNumber = lastSpoken;

		if (lastSpoken > 0) {
			newNumber = cast(int) index + 1 - lastSpoken;
		}

		memory[previousNumber] = cast(int) index + 1;

		/+if (index < 20 || index == TIMES2 - 2) {
			writeln("i:", index + 1, "; newNumber: ", newNumber, ", lastSpoken: ", lastSpoken, ", prevous: ", previousNumber);
			if (index != TIMES2 - 2) writeln(memory);
		}+/
		previousNumber = newNumber;
	}

	return previousNumber;
}

unittest {
	//assert(part1([0,3,6]) == 12);
	assert(part1([1,3,2]) == 1);
	assert(part1([2,1,3]) == 10);
	assert(part1([1,2,3]) == 27);

	// Stop slowing down my unit tests
	//assert(part2([0,3,6]) == 175594);
	//assert(part2([1,3,2]) == 2578);
}
