// compose.h

// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// Copyright 2005-2010 Google, Inc.
// Author: riley@google.com (Michael Riley)
//
// \file
// Compose a PDT and an FST.

#ifndef FST_EXTENSIONS_PDT_COMPOSE_H__
#define FST_EXTENSIONS_PDT_COMPOSE_H__

#include <fst/compose.h>

namespace fst {

// Class to setup composition options for PDT composition.
// Default is for the PDT as the first composition argument.
template <class Arc, bool left_pdt = true>
class PdtComposeOptions : public
ComposeFstOptions<Arc,
                  MultiEpsMatcher< Matcher<Fst<Arc> > >,
                  MultiEpsFilter<AltSequenceComposeFilter<
                                   MultiEpsMatcher<
                                     Matcher<Fst<Arc> > > > > > {
 public:
  typedef typename Arc::Label Label;
  typedef MultiEpsMatcher< Matcher<Fst<Arc> > > PdtMatcher;
  typedef MultiEpsFilter<AltSequenceComposeFilter<PdtMatcher> > PdtFilter;
  typedef ComposeFstOptions<Arc, PdtMatcher, PdtFilter> COptions;
  using COptions::matcher1;
  using COptions::matcher2;
  using COptions::filter;

  PdtComposeOptions(const Fst<Arc> &ifst1,
                    const vector<pair<Label, Label> > &parens,
                    const Fst<Arc> &ifst2) {
    matcher1 = new PdtMatcher(ifst1, MATCH_OUTPUT, kMultiEpsList);
    matcher2 = new PdtMatcher(ifst2, MATCH_INPUT, kMultiEpsLoop);

    // Treat parens as multi-epsilons when composing.
    for (size_t i = 0; i < parens.size(); ++i) {
      matcher1->AddMultiEpsLabel(parens[i].first);
      matcher1->AddMultiEpsLabel(parens[i].second);
      matcher2->AddMultiEpsLabel(parens[i].first);
      matcher2->AddMultiEpsLabel(parens[i].second);
    }

    filter = new PdtFilter(ifst1, ifst2, matcher1, matcher2, true);
  }
};

// Class to setup composition options for PDT with FST composition.
// Specialization is for the FST as the first composition argument.
template <class Arc>
class PdtComposeOptions<Arc, false> : public
ComposeFstOptions<Arc,
                  MultiEpsMatcher< Matcher<Fst<Arc> > >,
                  MultiEpsFilter<SequenceComposeFilter<
                                   MultiEpsMatcher<
                                     Matcher<Fst<Arc> > > > > > {
 public:
  typedef typename Arc::Label Label;
  typedef MultiEpsMatcher< Matcher<Fst<Arc> > > PdtMatcher;
  typedef MultiEpsFilter<SequenceComposeFilter<PdtMatcher> > PdtFilter;
  typedef ComposeFstOptions<Arc, PdtMatcher, PdtFilter> COptions;
  using COptions::matcher1;
  using COptions::matcher2;
  using COptions::filter;

  PdtComposeOptions(const Fst<Arc> &ifst1,
                    const Fst<Arc> &ifst2,
                    const vector<pair<Label, Label> > &parens) {
    matcher1 = new PdtMatcher(ifst1, MATCH_OUTPUT, kMultiEpsLoop);
    matcher2 = new PdtMatcher(ifst2, MATCH_INPUT, kMultiEpsList);

    // Treat parens as multi-epsilons when composing.
    for (size_t i = 0; i < parens.size(); ++i) {
      matcher1->AddMultiEpsLabel(parens[i].first);
      matcher1->AddMultiEpsLabel(parens[i].second);
      matcher2->AddMultiEpsLabel(parens[i].first);
      matcher2->AddMultiEpsLabel(parens[i].second);
    }

    filter = new PdtFilter(ifst1, ifst2, matcher1, matcher2, true);
  }
};


// Composes pushdown transducer (PDT) encoded as an FST (1st arg) and
// an FST (2nd arg) with the result also a PDT encoded as an Fst. (3rd arg).
// In the PDTs, some transitions are labeled with open or close
// parentheses. To be interpreted as a PDT, the parens must balance on
// a path (see PdtExpand()). The open-close parenthesis label pairs
// are passed in 'parens'.
template <class Arc>
void Compose(const Fst<Arc> &ifst1,
             const vector<pair<typename Arc::Label,
                               typename Arc::Label> > &parens,
             const Fst<Arc> &ifst2,
             MutableFst<Arc> *ofst,
             const ComposeOptions &opts = ComposeOptions()) {

  PdtComposeOptions<Arc, true> copts(ifst1, parens, ifst2);
  copts.gc_limit = 0;
  *ofst = ComposeFst<Arc>(ifst1, ifst2, copts);
  if (opts.connect)
    Connect(ofst);
}


// Composes an FST (1st arg) and pushdown transducer (PDT) encoded as
// an FST (2nd arg) with the result also a PDT encoded as an Fst (3rd arg).
// In the PDTs, some transitions are labeled with open or close
// parentheses. To be interpreted as a PDT, the parens must balance on
// a path (see ExpandFst()). The open-close parenthesis label pairs
// are passed in 'parens'.
template <class Arc>
void Compose(const Fst<Arc> &ifst1,
             const Fst<Arc> &ifst2,
             const vector<pair<typename Arc::Label,
                               typename Arc::Label> > &parens,
             MutableFst<Arc> *ofst,
             const ComposeOptions &opts = ComposeOptions()) {

  PdtComposeOptions<Arc, false> copts(ifst1, ifst2, parens);
  copts.gc_limit = 0;
  *ofst = ComposeFst<Arc>(ifst1, ifst2, copts);
  if (opts.connect)
    Connect(ofst);
}

}  // namespace fst

#endif  // FST_EXTENSIONS_PDT_COMPOSE_H__