def read_file():
	with open("input.txt","r") as f:
		groups = f.read().split('\n\n')
		rules = groups[0].split('\n')
		your_ticket = groups[1].split('\n')[1:]
		tickets = groups[2].split('\n')[1:]
		return rules, your_ticket, tickets

def find_valid_set(rule):
	_, ranges = rule.split(': ')
	range1, range2 = ranges.split(' or ')
	a, b = map(int,range1.split('-'))
	c, d = map(int,range2.split('-'))
	set1 = set(range(a,b+1))
	set2 = set(range(c,d+1))
	return set1 | set2
	
def find_valid_superset(rules):
	valid_superset = set()
	for rule in rules:
		valid_set = find_valid_set(rule)
		valid_superset = valid_superset | valid_set
	return valid_superset

def part1(rules, tickets):
	"""Add up all values not valid for any range."""
	valid_set = find_valid_superset(rules)
	error_rate = 0
	for ticket in tickets:
		numbers = map(int,ticket.split(','))
		for number in numbers:
			if number not in valid_set:
				error_rate += number
	return error_rate

def find_valid_tickets(tickets, rules):
	valid_tickets = []
	valid_set = find_valid_superset(rules)
	for ticket in tickets:
		numbers = map(int,ticket.split(','))
		if all([number in valid_set for number in numbers]):
			valid_tickets.append(ticket)
	return valid_tickets

def parse_tickets(tickets):
	parsed_tickets = []
	for ticket in tickets:
		numbers = map(int,ticket.split(','))
		parsed_ticket = [int(number) for number in numbers]
		parsed_tickets.append(parsed_ticket)
	return parsed_tickets 

def part2(rules, your_ticket, tickets):
	"""Find product of 6 "departure" fields."""
	valid_tickets = find_valid_tickets(tickets, rules)
	valid_tickets += your_ticket
	parsed_tickets = parse_tickets(valid_tickets)
	your_ticket = parse_tickets(your_ticket)[0]
	ticket_length = len(parsed_tickets[0])
	ticket_count = len(parsed_tickets)
	# convert rules into valid sets
	valid_sets = []
	for i in range(ticket_length):
		valid_set = find_valid_set(rules[i])
		valid_sets.append(valid_set)
	# correlate rulesets with values
	possibilities = []
	for i in range(ticket_length):
		## get all values at position
		values = set()
		for ticket in parsed_tickets:
			values.add(ticket[i])
		## find which rulesets match
		possibility = []
		for j, valid_set in enumerate(valid_sets):
			if all([value in valid_set for value in values]):
				possibility.append(1)
			else:
				possibility.append(0)
		possibilities.append(possibility)
	# process of elimination to correlate ruleset to index
	mapping = {}
	for i in range(ticket_length):
		sums = list(map(sum, possibilities))
		i = sums.index(1) ## find row with only one 1
		row = possibilities[i]
		j = row.index(1) ## find 1 in that row
		mapping[j] = i
		for i in range(ticket_length):
			possibilities[i][j] = 0
	# this bit only works on actual input bc there's no reason to make it robust
	prod = 1
	for j in range(6):
		prod *= your_ticket[mapping[j]]
	return prod

def main():
	rules, your_ticket, tickets = read_file()
	print(part1(rules, tickets), part2(rules, your_ticket, tickets))

if __name__ == "__main__":
	main()