package main

import (
	"bufio"
	"errors"
	"fmt"
	"log"
	"os"
	"strconv"
	"strings"
)

func main() {
	input := stdinToStringSlice()
	//part1(input)
	part2(input)
}

func part1(input []string) {
	vpc := NewState()
	if strings.HasPrefix(input[0], "#ip") {
		// Binding the ip to a register...
		pts := strings.Split(input[0], " ")
		vpc.ipReg = Atoi(pts[1])
		input = input[1:]
	}
	for i := 0; i < 200; i++ {
		if vpc.ip >= len(input) {
			break
		}
		inp := input[vpc.ip]
		if op, cmd, err := vpc.parseCommand(inp); err == nil {
			if vpc.ipReg > -1 {
				vpc.reg[vpc.ipReg] = vpc.ip
			}
			fmt.Print(vpc.string(), " -> ", op, " ", cmd, " -> ")
			vpc.run(op, cmd)
			fmt.Println(vpc.string())
			vpc.ip = vpc.reg[vpc.ipReg]
		}
		vpc.ip++
	}
	fmt.Println(vpc.string())
}

func part2(input []string) {
	vpc := NewState()
	vpc.reg[0] = 1
	if strings.HasPrefix(input[0], "#ip") {
		// Binding the ip to a register...
		pts := strings.Split(input[0], " ")
		vpc.ipReg = Atoi(pts[1])
		input = input[1:]
	}

	for {
		if vpc.ip >= len(input) {
			break
		}
		if vpc.ip == 3 {
			// When ip == 3, we kick off the loop.
			// Looks like it's just summing the divisors of the value in register 2
			divisors := getDivisors(vpc.reg[2])
			fmt.Println(divisors)
			fmt.Println(sum(divisors))
			break
		} else {
			// Normal Execution
			inp := input[vpc.ip]
			if op, cmd, err := vpc.parseCommand(inp); err == nil {
				if vpc.ipReg > -1 {
					vpc.reg[vpc.ipReg] = vpc.ip
				}
				fmt.Print(vpc.string(), " -> ", op, " ", cmd, " -> ")
				vpc.run(op, cmd)
				fmt.Println(vpc.string())
				vpc.ip = vpc.reg[vpc.ipReg]
			}
			vpc.ip++
		}
	}
}

type State struct {
	ip, ipReg int
	reg       [6]int
	OpCodes   map[string]func(a, b, c int)
}

func NewState() *State {
	s := &State{
		ip:    0,
		ipReg: -1,
	}
	s.OpCodes = make(map[string]func(a, b, c int))
	s.OpCodes["addr"] = s.addr
	s.OpCodes["addi"] = s.addi
	s.OpCodes["mulr"] = s.mulr
	s.OpCodes["muli"] = s.muli
	s.OpCodes["banr"] = s.banr
	s.OpCodes["bani"] = s.bani
	s.OpCodes["borr"] = s.borr
	s.OpCodes["bori"] = s.bori
	s.OpCodes["setr"] = s.setr
	s.OpCodes["seti"] = s.seti
	s.OpCodes["gtir"] = s.gtir
	s.OpCodes["gtri"] = s.gtri
	s.OpCodes["gtrr"] = s.gtrr
	s.OpCodes["eqir"] = s.eqir
	s.OpCodes["eqri"] = s.eqri
	s.OpCodes["eqrr"] = s.eqrr
	return s
}

func (s *State) parseCommand(inp string) (string, []int, error) {
	var retOp string
	var retCmd []int
	pts := strings.Split(inp, " ")
	if len(pts) != 4 {
		return retOp, retCmd, errors.New("Invalid Command")
	}
	retOp = pts[0]
	for _, v := range pts[1:] {
		retCmd = append(retCmd, Atoi(v))
	}
	return retOp, retCmd, nil
}

func (s *State) string() string {
	ret := fmt.Sprintf("ip=%d ", s.ip)
	for i := 0; i < len(s.reg); i++ {
		if i == s.ipReg {
			ret = fmt.Sprintf("%s <%d> ", ret, s.reg[i])
		} else {
			ret = fmt.Sprintf("%s %d ", ret, s.reg[i])
		}
	}
	return ret
}

func (s *State) run(op string, cmd []int) error {
	if fn, ok := s.OpCodes[op]; !ok {
		return errors.New(fmt.Sprintf("Invalid OpCode: %d", cmd[0]))
	} else {
		fn(cmd[0], cmd[1], cmd[2])
	}
	return nil
}

func (s *State) addr(r1, r2, r3 int) {
	s.reg[r3] = s.reg[r1] + s.reg[r2]
}

func (s *State) addi(r1, v2, r3 int) {
	s.reg[r3] = s.reg[r1] + v2
}

func (s *State) mulr(r1, r2, r3 int) {
	s.reg[r3] = s.reg[r1] * s.reg[r2]
}

func (s *State) muli(r1, v2, r3 int) {
	s.reg[r3] = s.reg[r1] * v2
}

func (s *State) banr(r1, r2, r3 int) {
	s.reg[r3] = s.reg[r1] & s.reg[r2]
}

func (s *State) bani(r1, v2, r3 int) {
	s.reg[r3] = s.reg[r1] & v2
}

func (s *State) borr(r1, r2, r3 int) {
	s.reg[r3] = s.reg[r1] | s.reg[r2]
}

func (s *State) bori(r1, v2, r3 int) {
	s.reg[r3] = s.reg[r1] | v2
}

func (s *State) setr(r1, r2, r3 int) {
	s.reg[r3] = s.reg[r1]
}

func (s *State) seti(v1, v2, r3 int) {
	s.reg[r3] = v1
}

func (s *State) gtir(v1, r2, r3 int) {
	s.reg[r3] = boolToInt(v1 > s.reg[r2])
}

func (s *State) gtri(r1, v2, r3 int) {
	s.reg[r3] = boolToInt(s.reg[r1] > v2)
}

func (s *State) gtrr(r1, r2, r3 int) {
	s.reg[r3] = boolToInt(s.reg[r1] > s.reg[r2])
}

func (s *State) eqir(v1, r2, r3 int) {
	s.reg[r3] = boolToInt(v1 == s.reg[r2])
}

func (s *State) eqri(r1, v2, r3 int) {
	s.reg[r3] = boolToInt(s.reg[r1] == v2)
}

func (s *State) eqrr(r1, r2, r3 int) {
	s.reg[r3] = boolToInt(s.reg[r1] == s.reg[r2])
}

func boolToInt(v bool) int {
	if v {
		return 1
	}
	return 0
}

func getDivisors(n int) []int {
	ret := []int{n}
	for i := 1; i < (n / 2); i++ {
		if n%i == 0 {
			ret = append(ret, i)

		}
	}
	return ret
}

func sum(vals []int) int {
	var ret int
	for _, v := range vals {
		ret += v
	}
	return ret
}

func stdinToStringSlice() []string {
	var input []string
	scanner := bufio.NewScanner(os.Stdin)
	for scanner.Scan() {
		input = append(input, scanner.Text())
	}
	return input
}

func Atoi(i string) int {
	var ret int
	var err error
	if ret, err = strconv.Atoi(i); err != nil {
		log.Fatal("Invalid Atoi: " + i)
	}
	return ret
}
