from functools import reduce
from itertools import islice


def imperative(chart):
    part1, part2 = 0, 1
    for run, rise in [(1, 1), (3, 1), (5, 1), (7, 1), (1, 2)]:
        trees, col = 0, 0
        # itertools.islice is a bit like `list[i:j:step]` except it doesn't
        # make a copy of the underlying list in memory. This both saves you the
        # time required to make such a copy, and it save you some RAM.
        for row in islice(chart, 0, len(chart), rise):
            istree = (row[col % len(row)] == '#')
            part1 += (istree and run == 3 and rise == 1)
            trees += istree
            col += run
        part2 *= trees

    print("Part I:  ", part1)
    print("Part II: ", part2)


def functional(chart):
    def tree_at(run):
        def is_tree_at(elem):
            i, row = elem
            return row[run * i % len(row)] == "#"

        return is_tree_at

    def adder(x, y):
        return x + y

    def multiplier(x, y):
        return x * y

    def solve(chart, slope):
        run, rise = slope
        rows = islice(chart, 0, len(chart), rise)
        return reduce(adder, map(tree_at(run), enumerate(rows)))

    slopes = [(1, 1), (3, 1), (5, 1), (7, 1), (1, 2)]

    part1 = solve(chart, (3, 1))
    part2 = reduce(multiplier, map(lambda s: solve(chart, s), slopes))

    print("Part I:  ", part1)
    print("Part II: ", part2)


with open('input.txt', 'r') as handle:
    chart = list(map(lambda s: s.strip(), handle.readlines()))


print("Imperative:")
imperative(chart)

print()

print("Functional:")
functional(chart)
