static <L, X> Fst<L> intersect(Fst<L> left, Fst<L> right,
			F1<L, X> intersectOn) {

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

		BooleanRing br = new BooleanRing();

		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);
				resolvePair(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);

			// TODO efficient intersection
			for (Transition<L> fromL : l.outgoing) {
				for (Transition<L> fromR : r.outgoing) {

					L both = intersectable(br, fromL.label, fromR.label,
							intersectOn);
					if (both != null) {

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

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

						intersection.addTransition(fromL.label, source, target);

						stack.push(targetPair);

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

		return intersection;
	}