<SYSTEM_TASK:>
Solve the following problem using Python, implementing the functions described below, one line at a time
<END_TASK>
<USER_TASK:>
Description:

def concatenate(*fsms):
		'''
			Concatenate arbitrarily many finite state machines together.
		'''
		alphabet = set().union(*[fsm.alphabet for fsm in fsms])

		def connect_all(i, substate):
			'''
				Take a state in the numbered FSM and return a set containing it, plus
				(if it's final) the first state from the next FSM, plus (if that's
				final) the first state from the next but one FSM, plus...
			'''
			result = {(i, substate)}
			while i < len(fsms) - 1 and substate in fsms[i].finals:
				i += 1
				substate = fsms[i].initial
				result.add((i, substate))
			return result

		# Use a superset containing states from all FSMs at once.
		# We start at the start of the first FSM. If this state is final in the
		# first FSM, then we are also at the start of the second FSM. And so on.
		initial = set()
		if len(fsms) > 0:
			initial.update(connect_all(0, fsms[0].initial))
		initial = frozenset(initial)

		def final(state):
			'''If you're in a final state of the final FSM, it's final'''
			for (i, substate) in state:
				if i == len(fsms) - 1 and substate in fsms[i].finals:
					return True
			return False

		def follow(current, symbol):
			'''
				Follow the collection of states through all FSMs at once, jumping to the
				next FSM if we reach the end of the current one
				TODO: improve all follow() implementations to allow for dead metastates?
			'''
			next = set()
			for (i, substate) in current:
				fsm = fsms[i]
				if substate in fsm.map and symbol in fsm.map[substate]:
					next.update(connect_all(i, fsm.map[substate][symbol]))
			if len(next) == 0:
				raise OblivionError
			return frozenset(next)

		return crawl(alphabet, initial, final, follow).reduce()