code: public static MutableFst compose(MutableFst fst1, MutableFst fst2, Semiring semiring, boolean useSorted) {
      fst1.throwIfInvalid();
      fst2.throwIfInvalid();
      if (!FstUtils.symbolTableEquals(fst1.getOutputSymbols(), fst2.getInputSymbols())) {
        throw new IllegalArgumentException("Symbol tables don't match, cant compose " + fst1 + " to " + fst2);
      }
  
      WriteableSymbolTable table = symbolTableEffectiveCopy(fst1.getOutputSymbols());
  
      int e1index = getOrAddEps(table, true);
      int e2index = getOrAddEps(table, false);
      String eps1 = table.invert().keyForId(e1index);
      String eps2 = table.invert().keyForId(e2index);
      MutableFst filter = makeFilter(table, semiring, eps1, eps2);
      augment(AugmentLabels.OUTPUT, fst1, semiring, eps1, eps2);
      augment(AugmentLabels.INPUT, fst2, semiring, eps1, eps2);
      if (useSorted) {
        ArcSort.sortByOutput(fst1);
        ArcSort.sortByInput(filter);
      }
      assert(FstUtils.symbolTableEquals(fst1.getOutputSymbols(), filter.getInputSymbols(), LOG_REPORTER));
      MutableFst tmp = Compose.doCompose(fst1, filter, semiring, useSorted);
      if (useSorted) {
        ArcSort.sortByOutput(tmp);
      }
      assert(FstUtils.symbolTableEquals(tmp.getOutputSymbols(), fst2.getInputSymbols(), LOG_REPORTER));
      MutableFst res = Compose.doCompose(tmp, fst2, semiring, useSorted);
      Connect.apply(res);
  
      return res;
    }
docs: Computes the composition of two Fsts. The two Fsts are augmented in order to avoid multiple epsilon paths in the
  resulting Fst
  
  @param fst1     the first Fst
  @param fst2     the second Fst
  @param semiring the semiring to use in the operation
  @param useSorted if true then the input fsts should be sorted
  @return the composed Fst