package main

import (
	"fmt"
	"math/big"
	"strconv"

	helpers "git.bullercodeworks.com/brian/adventofcode/helpers"
)

func main() {
	pt := helpers.GetArgNumber(1)
	inp := helpers.StdinToStringSlice()
	if pt == "1" {
		part1(inp)
	} else {
		part2(inp)
	}
}

func NewDeck(n int) []int {
	s := make([]int, n)
	for i := 0; i < n; i++ {
		s[i] = i
	}
	return s
}

func deal(s []int) []int {
	s2 := make([]int, len(s))
	for i := range s {
		s2[len(s)-1-i] = s[i]
	}
	return s2
}

func cut(s []int, n int) []int {
	if n >= 0 {
		return append(s[n:], s[:n]...)
	}
	n = -n
	return append(s[len(s)-n:], s[:len(s)-n]...)
}

func dealn(s []int, n int) []int {
	s2 := make([]int, len(s))
	for i := range s {
		s2[(i*n)%len(s)] = s[i]
	}
	return s2
}

func part1(inp []string) {
	s := NewDeck(10007)
	for _, line := range inp {
		if line == "deal into new stack" {
			s = deal(s)
		} else if line[:3] == "cut" {
			i, _ := strconv.Atoi(line[4:])
			s = cut(s, i)
		} else if line[:19] == "deal with increment" {
			i, _ := strconv.Atoi(line[20:])
			s = dealn(s, i)
		}
	}
	for i, v := range s {
		if v == 2019 {
			fmt.Println(i)
			return
		}
	}
}

func part2(inp []string) {
	deckSize := big.NewInt(119315717514047)
	shuffles := big.NewInt(101741582076661)
	off, inc := big.NewInt(0), big.NewInt(1)
	for _, line := range inp {
		if line == "deal into new stack" {
			inc.Mul(inc, big.NewInt(-1))
			off.Add(off, inc)
		} else if line[:3] == "cut" {
			i, _ := strconv.Atoi(line[4:])
			off.Add(off, big.NewInt(0).Mul(big.NewInt(int64(i)), inc))
		} else if line[:19] == "deal with increment" {
			i, _ := strconv.Atoi(line[20:])
			inc.Mul(inc, big.NewInt(0).Exp(big.NewInt(int64(i)), big.NewInt(0).Sub(deckSize, big.NewInt(2)), deckSize))
		}
	}
	lastInc := big.NewInt(0).Exp(inc, shuffles, deckSize)
	lastOff := big.NewInt(0).Exp(inc, shuffles, deckSize)
	lastOff.Sub(big.NewInt(1), lastOff)
	mod := big.NewInt(0).Exp(big.NewInt(0).Sub(big.NewInt(1), inc), big.NewInt(0).Sub(deckSize, big.NewInt(2)), deckSize)
	lastOff.Mul(lastOff, mod)
	lastOff.Mul(lastOff, off)

	res := big.NewInt(0).Mul(big.NewInt(2020), lastInc)
	res.Add(res, lastOff)
	res.Mod(res, deckSize)
	fmt.Println("Result:", res)
}
