static <A, B, C, X> Fst<C> compose(Fst<A> left, Fst<B> right,
			F1<A, X> leftSel, F1<B, X> rightSel, F2<A, B, C> compo) {

		Fst<C> intersection = new Fst<>();

		LinkedList<StatePair> stack = new LinkedList<>();
		Map<StatePair, State> chart = new HashMap<>();

		for (State l : left.initialStates) {
			for (State r : right.initialStates) {
				StatePair lr = new StatePair(l, r);
				resolvePair2(lr, left, right, chart, intersection);
				stack.push(lr);
			}
		}

		while (!stack.isEmpty()) {
			State l = stack.peek().fst;
			State r = stack.peek().snd;
			StatePair lr = stack.pop();
			State source = chart.get(lr);

			for (Transition<A> fromL : l.outgoing) {
				for (Transition<B> fromR : r.outgoing) {

					C both = composable(fromL.label, fromR.label, leftSel,
							rightSel, compo);
					if (both != null) {

						StatePair targetPair = new StatePair(fromL.target,
								fromR.target);

						State target = resolvePair2(targetPair, left, right,
								chart, intersection);

						intersection.addTransition(both, source, target);

						stack.push(targetPair);

					} else {
						// optimization : remove dead ends
						if (!intersection.isAccept(source)) {
							intersection.remove(source);
						}
					}
				}
			}
		}

		return intersection;
	}