#!/usr/bin/env python

import enum
import itertools
import sys
from collections.abc import Iterator
from typing import NamedTuple


class Point(NamedTuple):
    x: int
    y: int

    def __add__(self, other):
        if not isinstance(other, Point):
            return NotImplemented
        return Point(self.x + other.x, self.y + other.y)

    def __sub__(self, other):
        if not isinstance(other, Point):
            return NotImplemented
        return Point(self.x - other.x, self.y - other.y)


def translate(points: set[Point], delta: Point) -> set[Point]:
    return {p + delta for p in points}


class Rock(str, enum.Enum):
    LINE = "####"
    PLUS = ".#.\n###\n.#."
    CORNER = "..#\n..#\n###"
    VERTICAL_LINE = "#\n#\n#\n#"
    SQUARE = "##\n##"

    @classmethod
    def stream(cls) -> Iterator["Rock"]:
        yield from itertools.cycle(iter(cls))

    def to_points(self) -> set[Point]:
        res: set[Point] = set()

        for y, line in enumerate(reversed(self.splitlines())):
            for x, c in enumerate(line):
                if c == ".":
                    continue
                res.add(Point(x, y))

        return res


class JetStream(str, enum.Enum):
    LEFT = "<"
    RIGHT = ">"

    @classmethod
    def stream(cls, jet_pattern: str) -> Iterator["JetStream"]:
        yield from itertools.cycle(map(cls, jet_pattern))

    def as_delta(self) -> Point:
        if self == self.LEFT:
            return Point(-1, 0)
        if self == self.RIGHT:
            return Point(1, 0)
        assert False  # Sanity check


def solve(input: list[str]) -> int:
    fallen_stack: set[Point] = set()
    max_height = 0

    LEFT_WALL = -1
    RIGHT_WALL = 7
    FLOOR = 0

    rocks = list(iter(Rock))
    jet_stream = [JetStream(c) for c in input[0]]

    t = 0
    jet_index = 0
    rock_index = 0

    def step(rock: set[Point], jet: JetStream) -> tuple[set[Point], bool]:
        # Check if it can be pushed by the jet, or if it hits an obstacle
        pushed_rock = translate(rock, jet.as_delta())
        if not (fallen_stack & pushed_rock) and all(
            LEFT_WALL < p.x < RIGHT_WALL for p in pushed_rock
        ):
            rock = pushed_rock

        # Check if it can go down
        fallen_rock = translate(rock, Point(0, -1))

        if not (fallen_stack & fallen_rock) and all(p.y > FLOOR for p in fallen_rock):
            return fallen_rock, True
        return rock, False

    def simulate_rock_fall() -> None:
        nonlocal max_height
        nonlocal jet_index
        nonlocal rock_index

        rock = rocks[rock_index].to_points()

        # Align 2 units away from LEFT_WALL and 3 higher than
        # current stack
        rock = translate(rock, Point(2, max_height + 3 + 1))

        while True:
            rock, keep_going = step(rock, jet_stream[jet_index])
            jet_index = (jet_index + 1) % len(jet_stream)
            if not keep_going:
                break

        fallen_stack.update(rock)
        max_height = max(max_height, max(p.y for p in rock))
        rock_index = (rock_index + 1) % len(rocks)

    StackStateHash = tuple[int, int, frozenset[Point]]

    def stack_state_hash() -> StackStateHash:
        top = frozenset(
            Point(p.x, p.y - max_height)
            for p in fallen_stack
            if p.y >= (max_height - 50)  # Cut-off point chosen arbitrarily...
        )
        return rock_index, jet_index, top

    assert len(input) == 1  # Sanity check

    cache: dict[StackStateHash, tuple[int, int]] = {}
    added_height = 0

    END_OF_SIMULATION = 1_000_000_000_000
    while t < END_OF_SIMULATION:
        simulate_rock_fall()
        t += 1
        stack_hash = stack_state_hash()
        if stack_hash in cache:
            previous_t, previous_height = cache[stack_hash]
            cycle_length = t - previous_t
            num_cycles = (END_OF_SIMULATION - t) // cycle_length
            added_height += num_cycles * (max_height - previous_height)
            t += num_cycles * cycle_length
        else:
            cache[stack_hash] = t, max_height

    return max_height + added_height


def main() -> None:
    input = sys.stdin.read().splitlines()
    print(solve(input))


if __name__ == "__main__":
    main()