1// pair-weight.h
2
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14//
15// Copyright 2005-2010 Google, Inc.
16// Author: shumash@google.com (Masha Maria Shugrina)
17//
18// \file
19// Pair weight templated base class for weight classes that
20// contain two weights (e.g. Product, Lexicographic)
21
22#ifndef FST_LIB_PAIR_WEIGHT_H_
23#define FST_LIB_PAIR_WEIGHT_H_
24
25#include <climits>
26#include <stack>
27#include <string>
28
29#include <fst/weight.h>
30
31
32DECLARE_string(fst_weight_parentheses);
33DECLARE_string(fst_weight_separator);
34
35namespace fst {
36
37template<class W1, class W2> class PairWeight;
38template <class W1, class W2>
39istream &operator>>(istream &strm, PairWeight<W1, W2> &w);
40
41template<class W1, class W2>
42class PairWeight {
43 public:
44  friend istream &operator>><W1, W2>(istream&, PairWeight<W1, W2>&);
45
46  typedef PairWeight<typename W1::ReverseWeight,
47                     typename W2::ReverseWeight>
48  ReverseWeight;
49
50  PairWeight() {}
51
52  PairWeight(const PairWeight& w) : value1_(w.value1_), value2_(w.value2_) {}
53
54  PairWeight(W1 w1, W2 w2) : value1_(w1), value2_(w2) {}
55
56  static const PairWeight<W1, W2> &Zero() {
57    static const PairWeight<W1, W2> zero(W1::Zero(), W2::Zero());
58    return zero;
59  }
60
61  static const PairWeight<W1, W2> &One() {
62    static const PairWeight<W1, W2> one(W1::One(), W2::One());
63    return one;
64  }
65
66  static const PairWeight<W1, W2> &NoWeight() {
67    static const PairWeight<W1, W2> no_weight(W1::NoWeight(), W2::NoWeight());
68    return no_weight;
69  }
70
71  istream &Read(istream &strm) {
72    value1_.Read(strm);
73    return value2_.Read(strm);
74  }
75
76  ostream &Write(ostream &strm) const {
77    value1_.Write(strm);
78    return value2_.Write(strm);
79  }
80
81  PairWeight<W1, W2> &operator=(const PairWeight<W1, W2> &w) {
82    value1_ = w.Value1();
83    value2_ = w.Value2();
84    return *this;
85  }
86
87  bool Member() const { return value1_.Member() && value2_.Member(); }
88
89  size_t Hash() const {
90    size_t h1 = value1_.Hash();
91    size_t h2 = value2_.Hash();
92    const int lshift = 5;
93    const int rshift = CHAR_BIT * sizeof(size_t) - 5;
94    return h1 << lshift ^ h1 >> rshift ^ h2;
95  }
96
97  PairWeight<W1, W2> Quantize(float delta = kDelta) const {
98    return PairWeight<W1, W2>(value1_.Quantize(delta),
99                                 value2_.Quantize(delta));
100  }
101
102  ReverseWeight Reverse() const {
103    return ReverseWeight(value1_.Reverse(), value2_.Reverse());
104  }
105
106  const W1& Value1() const { return value1_; }
107
108  const W2& Value2() const { return value2_; }
109
110 protected:
111  void SetValue1(const W1 &w) { value1_ = w; }
112  void SetValue2(const W2 &w) { value2_ = w; }
113
114  // Reads PairWeight when there are not parentheses around pair terms
115  inline static istream &ReadNoParen(
116      istream &strm, PairWeight<W1, W2>& w, char separator) {
117    int c;
118    do {
119      c = strm.get();
120    } while (isspace(c));
121
122    string s1;
123    while (c != separator) {
124      if (c == EOF) {
125        strm.clear(std::ios::badbit);
126        return strm;
127      }
128      s1 += c;
129      c = strm.get();
130    }
131    istringstream strm1(s1);
132    W1 w1 = W1::Zero();
133    strm1 >> w1;
134
135    // read second element
136    W2 w2 = W2::Zero();
137    strm >> w2;
138
139    w = PairWeight<W1, W2>(w1, w2);
140    return strm;
141  }
142
143  // Reads PairWeight when there are parentheses around pair terms
144  inline static istream &ReadWithParen(
145      istream &strm, PairWeight<W1, W2>& w,
146      char separator, char open_paren, char close_paren) {
147    int c;
148    do {
149      c = strm.get();
150    } while (isspace(c));
151    if (c != open_paren) {
152      FSTERROR() << " is fst_weight_parentheses flag set correcty? ";
153      strm.clear(std::ios::failbit);
154      return strm;
155    }
156    c = strm.get();
157
158    // read first element
159    stack<int> parens;
160    string s1;
161    while (c != separator || !parens.empty()) {
162      if (c == EOF) {
163        strm.clear(std::ios::badbit);
164        return strm;
165      }
166      s1 += c;
167      // if parens encountered before separator, they must be matched
168      if (c == open_paren) {
169        parens.push(1);
170      } else if (c == close_paren) {
171        // Fail for mismatched parens
172        if (parens.empty()) {
173          strm.clear(std::ios::failbit);
174          return strm;
175        }
176        parens.pop();
177      }
178      c = strm.get();
179    }
180    istringstream strm1(s1);
181    W1 w1 = W1::Zero();
182    strm1 >> w1;
183
184    // read second element
185    string s2;
186    c = strm.get();
187    while (c != EOF) {
188      s2 += c;
189      c = strm.get();
190    }
191    if (s2.empty() || (s2[s2.size() - 1] != close_paren)) {
192      FSTERROR() << " is fst_weight_parentheses flag set correcty? ";
193      strm.clear(std::ios::failbit);
194      return strm;
195    }
196
197    s2.erase(s2.size() - 1, 1);
198    istringstream strm2(s2);
199    W2 w2 = W2::Zero();
200    strm2 >> w2;
201
202    w = PairWeight<W1, W2>(w1, w2);
203    return strm;
204  }
205
206 private:
207  W1 value1_;
208  W2 value2_;
209
210};
211
212template <class W1, class W2>
213inline bool operator==(const PairWeight<W1, W2> &w,
214                       const PairWeight<W1, W2> &v) {
215  return w.Value1() == v.Value1() && w.Value2() == v.Value2();
216}
217
218template <class W1, class W2>
219inline bool operator!=(const PairWeight<W1, W2> &w1,
220                       const PairWeight<W1, W2> &w2) {
221  return w1.Value1() != w2.Value1() || w1.Value2() != w2.Value2();
222}
223
224
225template <class W1, class W2>
226inline bool ApproxEqual(const PairWeight<W1, W2> &w1,
227                        const PairWeight<W1, W2> &w2,
228                        float delta = kDelta) {
229  return ApproxEqual(w1.Value1(), w2.Value1(), delta) &&
230      ApproxEqual(w1.Value2(), w2.Value2(), delta);
231}
232
233template <class W1, class W2>
234inline ostream &operator<<(ostream &strm, const PairWeight<W1, W2> &w) {
235  if(FLAGS_fst_weight_separator.size() != 1) {
236    FSTERROR() << "FLAGS_fst_weight_separator.size() is not equal to 1";
237    strm.clear(std::ios::badbit);
238    return strm;
239  }
240  char separator = FLAGS_fst_weight_separator[0];
241  if (FLAGS_fst_weight_parentheses.empty())
242    return strm << w.Value1() << separator << w.Value2();
243
244  if (FLAGS_fst_weight_parentheses.size() != 2) {
245    FSTERROR() << "FLAGS_fst_weight_parentheses.size() is not equal to 2";
246    strm.clear(std::ios::badbit);
247    return strm;
248  }
249  char open_paren = FLAGS_fst_weight_parentheses[0];
250  char close_paren = FLAGS_fst_weight_parentheses[1];
251  return strm << open_paren << w.Value1() << separator
252              << w.Value2() << close_paren ;
253}
254
255template <class W1, class W2>
256inline istream &operator>>(istream &strm, PairWeight<W1, W2> &w) {
257  if(FLAGS_fst_weight_separator.size() != 1) {
258    FSTERROR() << "FLAGS_fst_weight_separator.size() is not equal to 1";
259    strm.clear(std::ios::badbit);
260    return strm;
261  }
262  char separator = FLAGS_fst_weight_separator[0];
263  bool read_parens = !FLAGS_fst_weight_parentheses.empty();
264  if (read_parens) {
265    if (FLAGS_fst_weight_parentheses.size() != 2) {
266      FSTERROR() << "FLAGS_fst_weight_parentheses.size() is not equal to 2";
267      strm.clear(std::ios::badbit);
268      return strm;
269    }
270    return PairWeight<W1, W2>::ReadWithParen(
271        strm, w, separator, FLAGS_fst_weight_parentheses[0],
272        FLAGS_fst_weight_parentheses[1]);
273  } else {
274    return PairWeight<W1, W2>::ReadNoParen(strm, w, separator);
275  }
276}
277
278}  // namespace fst
279
280#endif  // FST_LIB_PAIR_WEIGHT_H_
281