048124e0432ce11ddb1ef63ce77c905a015feab9
[platform/upstream/openfst.git] / src / extensions / python / pywrapfst.pyx
1 #cython: nonecheck=True
2 # See www.openfst.org for extensive documentation on this weighted
3 # finite-state transducer library.
4
5
6 """Python interface to the FST scripting API.
7
8 Operations which construct new FSTs are implemented as traditional functions, as
9 are two-argument boolean functions like `equal` and `equivalent`. Destructive
10 operations---those that mutate an FST, in place---are instance methods, as is
11 `write`. Operator overloading is not used. The following example, based on
12 Mohri et al. 2002, shows the construction of an ASR system given a pronunciation
13 lexicon L, grammar G, a transducer from context-dependent phones to
14 context-independent phones C, and an HMM set H:
15
16   L = fst.Fst.read("L.fst")
17   G = fst.Fst.read("G.fst")
18   C = fst.Fst.read("C.fst")
19   H = fst.Fst.read("H.fst")
20   LG = fst.determinize(fst.compose(L, G))
21   CLG = fst.determinize(fst.compose(C, LG))
22   HCLG = fst.determinize(fst.compose(H, CLG))
23   HCLG.minimize()                                      # NB: works in-place.
24
25 Python variables here use snake_case and constants are in all caps, minus the
26 normal `k` prefix.
27 """
28
29 # Overview of the file:
30 #
31 # * Imports
32 # * Custom exceptions
33 # * General helpers
34 # * Weight and helpers
35 # * _SymbolTable, _EncodeMapperSymbolTable, _FstSymbolTable,
36 #   _MutableFstSymbolTable, SymbolTable, and helpers
37 # * SymbolTableIterator
38 # * EncodeMapper
39 # * _Fst, _MutableFst, Fst, and helpers
40 # * FST properties
41 # * Arc, ArcIterator, and MutableArcIterator
42 # * StateIterator
43 # * FST operations
44 # * Compiler
45 # * FarReader and FarWriter
46 # * Cleanup operations for module entrance and exit.
47 #
48 # TODO(kbg): Try breaking this apart into smaller pieces.
49 #
50 # A few of the more idiosyncratic choices made here are due to "impedance
51 # mismatches" between C++ and Python, as follows.
52 #
53 # Another issue is that due to differences in C++ and Python scope rules, most
54 # C++ class instances have to be heap-allocated. Since all are packed into
55 # Python class instances, Python destructors are used to semi-automatically
56 # free C++ instances. The one exception are the various `...Options` structs.
57 # All that is included here are the constructors; there is no need to include
58 # the names of the struct members. Cython does not draw any meaningful
59 # distinction between structs and C++ classes, so these look just like class
60 # definitions.
61 #
62 # Cython's type annotations (e.g., `string`) are used when the variables will
63 # be sent as arguments to C++ functions, but are not used for variables used
64 # within the module.
65 #
66 # Internal functions which may raise a Python error do not have a C++ return
67 # type simply because this leads the C++ compiler to think that the resulting
68 # value could be used before it is populated.
69
70
71 ## Imports.
72
73 # C imports.
74 from libc.stdint cimport INT32_MAX
75 from libc.stdint cimport SIZE_MAX
76 from posix.unistd cimport getpid
77
78 # C++ imports.
79 from libcpp cimport bool
80 from libcpp.cast cimport const_cast
81 from libcpp.cast cimport static_cast
82
83 # Our C++ imports.
84 from ios cimport ofstream
85 from memory cimport static_pointer_cast
86
87 # Cython operator workarounds.
88 from cython.operator cimport address as addr       # &foo
89 from cython.operator cimport dereference as deref  # *foo
90 from cython.operator cimport preincrement as inc   # ++foo
91
92 # Python imports.
93 import atexit
94 import numbers
95 import subprocess
96 import logging
97
98
99 # TODO(kbg): Figure out how to access static class variables so I don't have
100 # to do it this way.
101 kNoSymbol = -1
102
103
104 ## Custom exceptions.
105
106
107 class FstError(Exception):
108
109   pass
110
111
112 class FstArgError(FstError, ValueError):
113
114   pass
115
116
117 class FstBadWeightError(FstError, ValueError):
118
119   pass
120
121
122 class FstDeletedConstructorError(FstError, RuntimeError):
123
124   pass
125
126
127 class FstIndexError(FstError, IndexError):
128
129   pass
130
131
132 class FstIOError(FstError, IOError):
133
134   pass
135
136
137 class FstOpError(FstError, RuntimeError):
138
139   pass
140
141
142 ## General helpers.
143
144
145 cdef string tostring(data, encoding="utf8") except *:
146   """Converts strings to bytestrings.
147
148   This function converts Python bytestrings and Unicode strings to bytestrings
149   encoded in UTF-8. It is used to process most Python string arguments before
150   passing them to the lower-level library.
151
152   Args:
153     data: A Unicode string or bytestring.
154     encoding: The desired encoding, defaulting to UTF-8.
155
156   Returns:
157     A bytestring.
158
159   Raises:
160     FstArgError: Cannot encode string.
161     UnicodeEncodeError.
162
163   This function is not visible to Python users.
164   """
165   # A Python bytestring can be implicitly cast to a C++ string.
166   if isinstance(data, bytes):
167     return data
168   elif isinstance(data, unicode):
169     return data.encode(encoding)
170   raise FstArgError("Cannot encode as string: {!r}".format(data))
171
172
173 cdef string weighttostring(data, encoding="utf8") except *:
174   """Converts strings or numerics to bytestrings.
175
176   This function converts Python bytestrings, Unicode strings, and numerics
177   which can be cast to floats to bytestrings encoded in UTF-8. It is used to
178   process Python string arguments so they can be used to construct Weight
179   objects. In most cases, weights are underlyingly floating-point, but since
180   not all weights are, they can only be constructed using a string.
181
182   Args:
183     data: A Unicode string, bytestring, or type which can be converted to a
184       Python float.
185
186   Returns:
187     A bytestring.
188
189   Raise:
190     FstArgError: Cannot encode string.
191     ValueError: Invalid literal for float.
192     UnicodeEncodeError.
193
194   This function is not visible to Python users.
195   """
196   # A Python bytestring can be implicitly cast to a C++ string.
197   if isinstance(data, bytes):
198     return data
199   elif isinstance(data, unicode):
200     return data.encode(encoding)
201   elif isinstance(data, numbers.Number):
202     return str(data).encode(encoding)
203   raise FstArgError("Cannot encode as string: {!r}".format(data))
204
205
206 cdef fst.ComposeFilter _get_compose_filter(
207     const string &compose_filter) except *:
208   """Matches string with the appropriate ComposeFilter enum value.
209
210   This function takes a string argument and returns the matching ComposeFilter
211   enum value used to initialize ComposeOptions instances. ComposeOptions is used
212   by difference and intersection in addition to composition.
213
214   Args:
215     compose_filter: A string matching a known composition filter; one of:
216         "alt_sequence", "auto", "match", "null", "sequence", "trivial".
217
218   Returns:
219     A ComposeFilter enum value.
220
221   Raises:
222     FstArgError: Unknown compose filter type.
223
224   This function is not visible to Python users.
225   """
226   cdef fst.ComposeFilter compose_filter_enum
227   if not fst.GetComposeFilter(compose_filter, addr(compose_filter_enum)):
228     raise FstArgError("Unknown compose filter type: {!r}".format(
229         compose_filter))
230   return compose_filter_enum
231
232
233 cdef fst.DeterminizeType _get_determinize_type(const string &det_type) except *:
234   """Matches string with the appropriate DeterminizeType enum value.
235
236   Args:
237     det_type: A string matching a known determinization type; one of:
238         "functional", "nonfunctional", "disambiguate".
239
240   Returns:
241     A DeterminizeType enum value.
242
243   Raises:
244     FstArgError: Unknown determinization type.
245
246   This function is not visible to Python users.
247   """
248   cdef fst.DeterminizeType det_type_enum
249   if not fst.GetDeterminizeType(det_type, addr(det_type_enum)):
250     raise FstArgError("Unknown determinization type: {!r}".format(det_type))
251   return det_type_enum
252
253
254 cdef fst.QueueType _get_queue_type(const string &queue_type) except *:
255   """Matches string with the appropriate QueueType enum value.
256
257   This function takes a string argument and returns the matching QueueType enum
258   value passed to the RmEpsilonOptions constructor.
259
260   Args:
261     queue_type: A string matching a known queue type; one of: "auto", "fifo",
262         "lifo", "shortest", "state", "top".
263
264   Returns:
265     A QueueType enum value.
266
267   Raises:
268     FstArgError: Unknown queue type.
269
270   This function is not visible to Python users.
271   """
272   cdef fst.QueueType queue_type_enum
273   if not fst.GetQueueType(queue_type, addr(queue_type_enum)):
274     raise FstArgError("Unknown queue type: {!r}".format(queue_type))
275   return queue_type_enum
276
277
278 cdef fst.RandArcSelection _get_rand_arc_selection(
279     const string &select) except *:
280   """Matches string with the appropriate RandArcSelection enum value.
281
282   This function takes a string argument and returns the matching
283   RandArcSelection enum value passed to the RandGenOptions constructor.
284
285   Args:
286     select: A string matching a known random arc selection type; one of:
287         "uniform", "log_prob", "fast_log_prob".
288
289   Returns:
290     A RandArcSelection enum value.
291
292   Raises:
293     FstArgError: Unknown random arc selection type.
294
295   This function is not visible to Python users.
296   """
297   cdef fst.RandArcSelection select_enum
298   if not fst.GetRandArcSelection(select, addr(select_enum)):
299     raise FstArgError("Unknown random arc selection type: {!r}".format(select))
300   return select_enum
301
302
303 cdef fst.ReplaceLabelType _get_replace_label_type(
304     const string &replace_label_type, bool epsilon_on_replace) except *:
305   """Matches string with the appropriate ReplaceLabelType enum value.
306
307   This function takes a string argument and returns the matching
308   ReplaceLabelType enum value passed to the ReplaceOptions constructor.
309
310   Args:
311     replace_label_type: A string matching a known replace label type; one of:
312         "neither", "input", "output", "both".
313     epsilon_on_replace: Should call/return arcs be epsilon arcs?
314
315   Returns:
316     A ReplaceLabelType enum value.
317
318   Raises:
319     FstArgError: Unknown replace label type.
320
321   This function is not visible to Python users.
322   """
323   cdef fst.ReplaceLabelType replace_label_type_enum
324   if not fst.GetReplaceLabelType(replace_label_type, epsilon_on_replace,
325                                  addr(replace_label_type_enum)):
326     raise FstArgError("Unknown replace label type: {!r}".format(
327                       replace_label_type))
328   return replace_label_type_enum
329
330
331 ## Weight and helpers.
332
333
334 cdef class Weight(object):
335
336   """
337   Weight(weight_type, weight_string)
338
339   FST weight class.
340
341   This class represents an FST weight. When passed as an argument to an FST
342   operation, it should have the weight type of the input FST(s) to said
343   operation.
344
345   Args:
346     weight_type: A string indicating the weight type.
347     weight_string: A string indicating the underlying weight.
348
349   Raises:
350     FstArgError: Weight type not found.
351     FstBadWeightError: Invalid weight.
352   """
353
354   def __repr__(self):
355     return "<{} Weight {} at 0x{:x}>".format(self.type(), self.to_string(),
356                                              id(self))
357
358   def __str__(self):
359     return self.to_string()
360
361   # This attempts to convert the string form into a float, raising
362   # ValueError when that is not appropriate.
363
364   def __float__(self):
365     return float(self.to_string())
366
367   def __init__(self, weight_type, weight):
368     self._weight.reset(new fst.WeightClass(tostring(weight_type),
369                                            weighttostring(weight)))
370     self._check_weight()
371
372   cdef void _check_weight(self) except *:
373     if self.type() == b"none":
374       raise FstArgError("Weight type not found")
375     if self.to_string() == b"BadNumber":
376       raise FstBadWeightError("Invalid weight")
377
378   cpdef Weight copy(self):
379     """
380     copy(self)
381
382     Returns a copy of the Weight.
383     """
384     cdef Weight result = Weight.__new__(Weight)
385     result._weight.reset(new
386         fst.WeightClass(<fst.WeightClass> deref(self._weight)))
387     return result
388
389   # To get around the inability to declare cdef class methods, we define the
390   # C++ part out-of-class and then call it from within.
391
392   @classmethod
393   def Zero(cls, weight_type):
394     """
395     Weight.Zero(weight_type)
396
397     Constructs semiring zero.
398     """
399     return _Weight_Zero(weight_type)
400
401   @classmethod
402   def One(cls, weight_type):
403     """
404     Weight.One(weight_type)
405
406     Constructs semiring One.
407     """
408     return _Weight_One(weight_type)
409
410   @classmethod
411   def NoWeight(cls, weight_type):
412     """
413     Weight.NoWeight(weight_type)
414
415     Constructs a non-member weight in the semiring.
416     """
417     return _Weight_NoWeight(weight_type)
418
419   def __richcmp__(Weight w1, Weight w2, int op):
420     # TODO(kbg): Replace this with __eq__ once Cython 0.27 is widely available.
421     if op == 2:  # `==`
422       return fst.Eq(deref(w1._weight), deref(w2._weight))
423     elif op == 3:  # `!=`
424       return fst.Ne(deref(w1._weight), deref(w2._weight))
425     else:
426       raise NotImplementedError("Invalid operator {!r}".format(op))
427
428   cpdef string to_string(self):
429     return self._weight.get().ToString()
430
431   cpdef string type(self):
432     """type(self)
433
434     Returns a string indicating the weight type.
435     """
436     return self._weight.get().Type()
437
438
439 cdef Weight _plus(Weight lhs, Weight rhs):
440   cdef Weight result = Weight.__new__(Weight)
441   result._weight.reset(new fst.WeightClass(fst.Plus(deref(lhs._weight),
442                                                     deref(rhs._weight))))
443   return result
444
445
446 def plus(Weight lhs, Weight rhs):
447   """
448   plus(lhs, rhs)
449
450   Computes the sum of two Weights in the same semiring.
451
452   This function computes lhs \oplus rhs, raising an exception if lhs and rhs
453   are not in the same semiring.
454
455   Args:
456      lhs: Left-hand side Weight.
457      rhs: Right-hand side Weight.
458
459   Returns:
460     A Weight object.
461
462   Raises:
463     FstArgError: Weight type not found (or not in same semiring).
464     FstBadWeightError: invalid weight.
465   """
466   cdef Weight result = _plus(lhs, rhs)
467   result._check_weight()
468   return result
469
470
471 cdef Weight _times(Weight lhs, Weight rhs):
472   cdef Weight result = Weight.__new__(Weight)
473   result._weight.reset(new fst.WeightClass(fst.Times(deref(lhs._weight),
474                                                      deref(rhs._weight))))
475   return result
476
477
478 def times(Weight lhs, Weight rhs):
479   """
480   times(lhs, rhs)
481
482   Computes the product of two Weights in the same semiring.
483
484   This function computes lhs \otimes rhs, raising an exception if lhs and rhs
485   are not in the same semiring.
486
487   Args:
488      lhs: Left-hand side Weight.
489      rhs: Right-hand side Weight.
490
491   Returns:
492     A Weight object.
493
494   Raises:
495     FstArgError: Weight type not found (or not in same semiring).
496     FstBadWeightError: Invalid weight.
497   """
498   cdef Weight result = _times(lhs, rhs)
499   result._check_weight()
500   return result
501
502
503 cdef Weight _divide(Weight lhs, Weight rhs):
504   cdef Weight result = Weight.__new__(Weight)
505   result._weight.reset(new fst.WeightClass(fst.Divide(deref(lhs._weight),
506                                                       deref(rhs._weight))))
507   return result
508
509
510 def divide(Weight lhs, Weight rhs):
511   """
512   divide(lhs, rhs)
513
514   Computes the quotient of two Weights in the same semiring.
515
516   This function computes lhs \oslash rhs, raising an exception if lhs and rhs
517   are not in the same semiring. As there is no way to specify whether to use
518   left vs. right division, this assumes a commutative semiring in which these
519   are equivalent operations.
520
521   Args:
522      lhs: Left-hand side Weight.
523      rhs: Right-hand side Weight.
524
525   Returns:
526     A Weight object.
527
528   Raises:
529     FstArgError: Weight type not found (or not in same semiring).
530     FstBadWeightError: Invalid weight.
531   """
532   cdef Weight result = _divide(lhs, rhs)
533   result._check_weight()
534   return result
535
536
537 cdef Weight _power(Weight w, size_t n):
538   cdef Weight result = Weight.__new__(Weight)
539   result._weight.reset(new fst.WeightClass(fst.Power(deref(w._weight), n)))
540   return result
541
542
543 def power(Weight w, size_t n):
544   """
545   power(lhs, rhs)
546
547   Computes the iterated product of a weight.
548
549   Args:
550      w: The weight.
551      n: The power.
552
553   Returns:
554     A Weight object.
555
556   Raises:
557     FstArgError: Weight type not found (or not in same semiring).
558     FstBadWeightError: Invalid weight.
559   """
560   cdef Weight result = _power(w, n)
561   result._check_weight()
562   return result
563
564
565 cdef fst.WeightClass _get_WeightClass_or_Zero(const string &weight_type,
566                                               weight) except *:
567   """Converts weight string to a WeightClass.
568
569   This function constructs a WeightClass instance of the desired weight type.
570   If the first argument is null, the weight is set to semiring Zero.
571
572   Args:
573     weight_type: A string denoting the desired weight type.
574     weight: A object indicating the desired weight; if omitted, the weight is
575         set to semiring Zero.
576
577   Returns:
578     A WeightClass object.
579
580   This function is not visible to Python users.
581   """
582   cdef fst.WeightClass result
583   if weight is None:
584     result = fst.WeightClass.Zero(weight_type)
585   elif isinstance(weight, Weight):
586     result = deref(<fst.WeightClass *> (<Weight> weight)._weight.get())
587   else:
588     result = fst.WeightClass(weight_type, weighttostring(weight))
589     if result.ToString() == b"BadNumber":
590       raise FstBadWeightError(weighttostring(weight))
591   return result
592
593
594 cdef fst.WeightClass _get_WeightClass_or_One(const string &weight_type,
595                                              weight) except *:
596   """Converts weight string to a WeightClass.
597
598   This function constructs a WeightClass instance of the desired weight type.
599   If the first argument is null, the weight is set to semiring One.
600
601   Args:
602     weight_type: A string denoting the desired weight type.
603     weight: A object indicating the desired weight; if omitted, the weight is
604         set to semiring One.
605
606   Returns:
607     A WeightClass object.
608
609   This function is not visible to Python users.
610   """
611   cdef fst.WeightClass result
612   if weight is None:
613     result = fst.WeightClass.One(weight_type)
614   elif isinstance(weight, Weight):
615     result = deref(<fst.WeightClass *> (<Weight> weight)._weight.get())
616   else:
617     result = fst.WeightClass(weight_type, weighttostring(weight))
618     if result.ToString() == b"BadNumber":
619       raise FstBadWeightError(weighttostring(weight))
620   return result
621
622
623 cdef Weight _Weight_Zero(weight_type):
624   cdef Weight result = Weight.__new__(Weight)
625   result._weight.reset(new fst.WeightClass(fst.WeightClass.Zero(
626       tostring(weight_type))))
627   if result._weight.get().Type() == b"none":
628     raise FstArgError("Weight type not found")
629   return result
630
631
632 cdef Weight _Weight_One(weight_type):
633   cdef Weight result = Weight.__new__(Weight)
634   result._weight.reset(new fst.WeightClass(
635         fst.WeightClass.One(tostring(weight_type))))
636   if result._weight.get().Type() == b"none":
637     raise FstArgError("Weight type not found")
638   return result
639
640
641 cdef Weight _Weight_NoWeight(weight_type):
642   cdef Weight result = Weight.__new__(Weight)
643   result._weight.reset(new fst.WeightClass(
644         fst.WeightClass.NoWeight(tostring(weight_type))))
645   return result
646
647
648 ## _SymbolTable, _MutableSymbolTable, _EncodeMapperSymbolTable, _FstSymbolTable,
649 ##  _MutableFstSymbolTable, SymbolTable, and helpers.
650 #
651 # SymbolTable hierarchy:
652 #
653 # _SymbolTable: abstract base class; has-a SymbolTable*
654 # _EncodeMapperSymbolTable(_SymbolTable): constant symbol table returned by
655 #     EncodeMapper.input_symbols/output_symbols
656 # _FstSymbolTable(_SymbolTable): constant symbol table returned by
657 #     _Fst.input_symbols/output_symbols
658 #
659 # _MutableSymbolTable(_SymbolTable): abstract base class adding mutation methods
660 # _MutableFstSymbolTable(_MutableSymbolTable): mutable symbol table returned by
661 #     _MutableFst.mutable_input_symbols/mutable_output_symbols
662 # SymbolTable(_MutableSymbolTable): adds constructor
663
664
665 cdef class _SymbolTable(object):
666
667   """
668   (No constructor.)
669
670   Base class for the symbol table hierarchy.
671
672   This class is the base class for SymbolTable. It has a "deleted" constructor
673   and implementations for the const methods of the wrapped SymbolTable.
674   """
675
676   # NB: Do not expose any non-const methods of the wrapped SymbolTable here.
677   # Doing so will allow undefined behavior.
678
679   def __init__(self):
680     raise FstDeletedConstructorError(
681         "Cannot construct {}".format(self.__class__.__name__))
682
683   def __iter__(self):
684     return SymbolTableIterator(self)
685
686   cpdef int64 available_key(self):
687     """
688     available_key(self)
689
690     Returns an integer indicating the next available key index in the table.
691     """
692     return self._table.AvailableKey()
693
694   cpdef string checksum(self):
695     """
696     checksum(self)
697
698     Returns a string indicating the label-agnostic MD5 checksum for the table.
699     """
700     return self._table.CheckSum()
701
702   cpdef SymbolTable copy(self):
703     """
704     copy(self)
705
706     Returns a mutable copy of the SymbolTable.
707     """
708     return _init_SymbolTable(self._table.Copy())
709
710   def find(self, key):
711     """
712     find(self, key)
713
714     Given a symbol or index, finds the other one.
715
716     This method returns the index associated with a symbol key, or the symbol
717     associated with a index key.
718
719     Args:
720       key: Either a string or an index.
721
722     Returns:
723       If the key is a string, the associated index or NO_LABEL if not found; if
724           the key is an integer, the associated symbol or an empty string if
725           not found.
726     """
727     try:
728       return self._table.FindIndex(tostring(key))
729     except FstArgError:
730       return self._table.FindSymbol(key)
731
732   cpdef int64 get_nth_key(self, ssize_t pos) except *:
733     """
734     get_nth_key(self, pos)
735
736     Retrieves the integer index of the n-th key in the table.
737
738     Args:
739       pos: The n-th key to retrieve.
740
741     Returns:
742       The integer index of the n-th key, or NO_LABEL if not found.
743     """
744     return self._table.GetNthKey(pos)
745
746   cpdef string labeled_checksum(self):
747     """
748     labeled_checksum(self)
749
750     Returns a string indicating the label-dependent MD5 checksum for the table.
751     """
752     return self._table.LabeledCheckSum()
753
754   cpdef bool member(self, key):
755     """
756     member(self, key)
757
758     Given a symbol or index, returns whether it is found in the table.
759
760     This method returns a boolean indicating whether the given symbol or index
761     is present in the table. If one intends to perform subsequent lookup, it is
762     better to simply call the find method, catching the KeyError.
763
764     Args:
765       key: Either a string or an index.
766
767     Returns:
768       Whether or not the key is present (as a string or a index) in the table.
769     """
770     try:
771       return self._table.MemberSymbol(tostring(key))
772     except FstArgError:
773       return self._table.MemberIndex(key)
774
775   def __contains__(self, key):
776     return self.member(key)
777
778   cpdef string name(self):
779     """
780     name(self)
781
782     Returns the symbol table's name.
783     """
784     return self._table.Name()
785
786   cpdef size_t num_symbols(self):
787     """
788     num_symbols(self)
789
790     Returns the number of symbols in the symbol table.
791     """
792     return self._table.NumSymbols()
793
794   cpdef void write(self, filename) except *:
795     """
796     write(self, filename)
797
798     Serializes symbol table to a file.
799
800     This methods writes the SymbolTable to a file in binary format.
801
802     Args:
803       filename: The string location of the output file.
804
805     Raises:
806       FstIOError: Write failed.
807     """
808     if not self._table.Write(tostring(filename)):
809       raise FstIOError("Write failed: {!r}".format(filename))
810
811   cpdef void write_text(self, filename) except *:
812     """
813     write_text(self, filename)
814
815     Writes symbol table to text file.
816
817     This method writes the SymbolTable to a file in human-readable format.
818
819     Args:
820       filename: The string location of the output file.
821
822     Raises:
823       FstIOError: Write failed.
824     """
825     if not self._table.WriteText(tostring(filename)):
826       raise FstIOError("Write failed: {!r}".format(filename))
827
828
829 cdef class _EncodeMapperSymbolTable(_SymbolTable):
830
831   """
832   (No constructor.)
833
834   Immutable SymbolTable class for tables stored in an EncodeMapper.
835
836   This class wraps a library const SymbolTable and exposes const methods of the
837   wrapped object. It is only to be returned by method, never constructed
838   directly.
839   """
840
841   # NB: Do not expose any non-const methods of the wrapped SymbolTable here.
842   # Doing so will allow undefined behavior.
843
844   def __repr__(self):
845     return "<const EncodeMapper SymbolTable {!r} at 0x{:x}>".format(self.name(),
846                                                                     id(self))
847
848
849 cdef class _FstSymbolTable(_SymbolTable):
850
851   """
852   (No constructor.)
853
854   Mutable SymbolTable class for tables stored in a mutable FST.
855
856   This class wraps a library SymbolTable and exposes methods of the wrapped
857   object. It is only to be returned by method, never constructed directly.
858   """
859
860   # NB: Do not expose any non-const methods of the wrapped SymbolTable here.
861   # Doing so will allow undefined behavior.
862
863   def __repr__(self):
864     return "<const Fst SymbolTable {!r} at 0x{:x}>".format(self.name(),
865                                                            id(self))
866
867
868 cdef class _MutableSymbolTable(_SymbolTable):
869
870   """
871   (No constructor.)
872
873   Base class for mutable symbol tables.
874
875   This class is the base class for a mutable SymbolTable. It has a "deleted"
876   constructor and implementations of all methods of the wrapped SymbolTable.
877   """
878
879   cpdef int64 add_symbol(self, symbol, int64 key=kNoSymbol):
880     """
881     add_symbol(self, symbol, key=NO_SYMBOL)
882
883     Adds a symbol to the table and returns the index.
884
885     This method adds a symbol to the table. The caller can optionally
886     specify a non-negative integer index for the key.
887
888     Args:
889       symbol: A symbol string.
890       key: An index for the symbol; if not specified, the next index will be
891           used.
892
893     Returns:
894       The integer key of the new symbol.
895     """
896     cdef string symbol_string = tostring(symbol)
897     if key != kNoSymbol:
898       return self._table.AddSymbol(symbol_string, key)
899     else:
900       return self._table.AddSymbol(symbol_string)
901
902   cpdef void add_table(self, _SymbolTable syms):
903     """
904     add_table(self, syms)
905
906     Adds another SymbolTable to this table.
907
908     This method merges another symbol table into the current table. All key
909     values will be offset by the current available key.
910
911     Args:
912       syms: A SymbolTable to be merged with the current table.
913     """
914     self._table.AddTable(deref(syms._table))
915
916   cpdef void set_name(self, new_name) except *:
917     self._table.SetName(tostring(new_name))
918
919
920 cdef class _MutableFstSymbolTable(_MutableSymbolTable):
921   """
922   (No constructor.)
923
924   Mutable SymbolTable assigned to an FST.
925   """
926
927   def __repr__(self):
928     return "<Fst SymbolTable {!r} at 0x{:x}>".format(self.name(), id(self))
929
930
931 cdef class SymbolTable(_MutableSymbolTable):
932
933   """
934   SymbolTable(name="<unspecified>")
935
936   Mutable SymbolTable class.
937
938   This class wraps the library SymbolTable and exposes both const (i.e.,
939   access) and non-const (i.e., mutation) methods of wrapped object.
940
941   Unlike other classes in the hierarchy, it has a working constructor and can be
942   used to programmatically construct a SymbolTable in memory.
943
944   Args:
945     name: An optional string indicating the table's name.
946   """
947
948   def __repr__(self):
949     return "<SymbolTable {!r} at 0x{:x}>".format(self.name(), id(self))
950
951   def __init__(self, name=b"<unspecified>"):
952     self._table = new fst.SymbolTable(tostring(name))
953     self._smart_table.reset(self._table)
954
955   @classmethod
956   def read(cls, filename):
957     """
958     SymbolTable.read(filename)
959
960     Reads symbol table from binary file.
961
962     This class method creates a new SymbolTable from a symbol table binary file.
963
964     Args:
965       filename: The string location of the input binary file.
966
967     Returns:
968       A new SymbolTable instance.
969
970     See also: `SymbolTable.read_fst`, `SymbolTable.read_text`.
971     """
972     cdef fst.SymbolTable *tsyms = fst.SymbolTable.Read(tostring(filename))
973     if tsyms == NULL:
974       raise FstIOError("Read failed: {!r}".format(filename))
975     return _init_SymbolTable(tsyms)
976
977   @classmethod
978   def read_text(cls, filename, bool allow_negative_labels=False):
979     """
980     SymbolTable.read_text(filename)
981
982     Reads symbol table from text file.
983
984     This class method creates a new SymbolTable from a symbol table text file.
985
986     Args:
987       filename: The string location of the input text file.
988       allow_negative_labels: Should negative labels be allowed? (Not
989           recommended; may cause conflicts).
990
991     Returns:
992       A new SymbolTable instance.
993
994     See also: `SymbolTable.read`, `SymbolTable.read_fst`.
995     """
996     cdef unique_ptr[fst.SymbolTableTextOptions] opts
997     opts.reset(new fst.SymbolTableTextOptions(allow_negative_labels))
998     cdef fst.SymbolTable *tsyms = fst.SymbolTable.ReadText(tostring(filename),
999                                                            deref(opts))
1000     if tsyms == NULL:
1001       raise FstIOError("Read failed: {!r}".format(filename))
1002     return _init_SymbolTable(tsyms)
1003
1004   @classmethod
1005   def read_fst(cls, filename, bool input_table):
1006     """
1007     SymbolTable.read_fst(filename, input_table)
1008
1009     Reads symbol table from an FST file without loading the corresponding FST.
1010
1011     This class method creates a new SymbolTable by reading either the input or
1012     output symbol table from an FST file, without loading the corresponding FST.
1013
1014     Args:
1015       filename: The string location of the input FST file.
1016       input_table: Should the input table be read (True) or the output table
1017           (False)?
1018
1019     Returns:
1020       A new SymbolTable instance, or None if none can be read.
1021
1022     Raises:
1023       FstIOError: Read failed.
1024
1025     See also: `SymbolTable.read`, `SymbolTable.read_text`.
1026     """
1027     cdef fst.SymbolTable *tsyms = fst.FstReadSymbols(filename, input_table)
1028     if tsyms == NULL:
1029       raise FstIOError("Read failed: {!r}".format(filename))
1030     return _init_SymbolTable(tsyms)
1031
1032
1033 cdef _EncodeMapperSymbolTable _init_EncodeMapperSymbolTable(
1034     fst.SymbolTable *table, shared_ptr[fst.EncodeMapperClass] encoder):
1035   cdef _EncodeMapperSymbolTable result = (
1036       _EncodeMapperSymbolTable.__new__(_EncodeMapperSymbolTable))
1037   result._table = table
1038   result._encoder = encoder
1039   return result
1040
1041
1042 cdef _FstSymbolTable _init_FstSymbolTable(fst.SymbolTable *table,
1043                                           shared_ptr[fst.FstClass] ifst):
1044   cdef _FstSymbolTable result = _FstSymbolTable.__new__(_FstSymbolTable)
1045   result._table = table
1046   result._fst = ifst
1047   return result
1048
1049
1050 cdef _MutableFstSymbolTable _init_MutableFstSymbolTable(fst.SymbolTable *table,
1051     shared_ptr[fst.MutableFstClass] ifst):
1052   cdef _MutableFstSymbolTable result = (
1053       _MutableFstSymbolTable.__new__(_MutableFstSymbolTable))
1054   result._table = table
1055   result._mfst = ifst
1056   return result
1057
1058
1059 cdef SymbolTable _init_SymbolTable(fst.SymbolTable *table):
1060   cdef SymbolTable result = SymbolTable.__new__(SymbolTable)
1061   result._table = table
1062   return result
1063
1064
1065 # Constructive SymbolTable operations.
1066
1067
1068 cpdef SymbolTable compact_symbol_table(_SymbolTable syms):
1069   """
1070   compact_symbol_table(syms)
1071
1072   Constructively relabels a SymbolTable to make it a contiguous mapping.
1073
1074   Args:
1075     syms: Input SymbolTable.
1076
1077   Returns:
1078     A new compacted SymbolTable.
1079   """
1080   return _init_SymbolTable(fst.CompactSymbolTable(deref(syms._table)))
1081
1082
1083 cpdef SymbolTable merge_symbol_table(_SymbolTable lhs, _SymbolTable rhs):
1084   """
1085   merge_symbol_table(lhs, rhs)
1086
1087   Merges all symbols from the left table into the right.
1088
1089   This function creates a new SymbolTable which is the merger of the two input
1090   symbol Tables. Symbols in the right-hand table that conflict with those in the
1091   left-hand table will be assigned values from the left-hand table. Thus the
1092   returned table will never modify symbol assignments from the left-hand side,
1093   but may do so on the right.
1094
1095   If the left-hand table is associated with an FST, it may be necessary to
1096   relabel it using the output table.
1097
1098   Args:
1099     lhs: Left-hand side SymbolTable.
1100     rhs: Left-hand side SymbolTable.
1101
1102   Returns:
1103     A new merged SymbolTable.
1104
1105   See also: `relabel_symbols`.
1106   """
1107   return _init_SymbolTable(fst.MergeSymbolTable(deref(lhs._table),
1108                                                 deref(rhs._table), NULL))
1109
1110
1111 ## SymbolTableIterator.
1112
1113
1114 cdef class SymbolTableIterator(object):
1115
1116   """
1117   SymbolTableIterator(syms)
1118
1119   This class is used for iterating over a symbol table.
1120   """
1121
1122   def __repr__(self):
1123     return "<SymbolTableIterator at 0x{:x}>".format(id(self))
1124
1125   def __init__(self, _SymbolTable syms):
1126     self._siter.reset(new fst.SymbolTableIterator(deref(syms._table)))
1127
1128   # This just registers this class as a possible iterator.
1129   def __iter__(self):
1130     return self
1131
1132   # Magic method used to get a Pythonic API out of the C++ API.
1133   def __next__(self):
1134     if self.done():
1135       raise StopIteration
1136     cdef int64 value = self.value()
1137     cdef string symbol = self.symbol()
1138     self.next()
1139     return (value, symbol)
1140
1141   cpdef bool done(self):
1142     """
1143     done(self)
1144
1145     Indicates whether the iterator is exhausted or not.
1146
1147     Returns:
1148       True if the iterator is exhausted, False otherwise.
1149     """
1150     return self._siter.get().Done()
1151
1152   cpdef void next(self):
1153     """
1154     next(self)
1155
1156     Advances the iterator.
1157     """
1158     self._siter.get().Next()
1159
1160   cpdef void reset(self):
1161     """
1162     reset(self)
1163
1164     Resets the iterator to the initial position.
1165     """
1166     self._siter.get().Reset()
1167
1168   cpdef string symbol(self):
1169     """
1170     symbol(self)
1171
1172     Returns the current symbol string.
1173
1174     This method returns the current symbol string at this point in the table.
1175
1176     Returns:
1177       A symbol string.
1178     """
1179     return self._siter.get().Symbol()
1180
1181   cpdef int64 value(self):
1182     """
1183     value(self)
1184
1185     Returns the current integer index of the symbol.
1186
1187     Returns:
1188       An integer index.
1189     """
1190     return self._siter.get().Value()
1191
1192
1193 ## EncodeMapper.
1194
1195
1196 cdef class EncodeMapper(object):
1197
1198   """
1199   EncodeMapper(arc_type="standard", encode_labels=False, encode_weights=False)
1200
1201   Arc encoder class, wrapping EncodeMapperClass.
1202
1203   This class provides an object which can be used to encode or decode FST arcs.
1204   This is most useful to convert an FST to an unweighted acceptor, on which
1205   some FST operations are more efficient, and then decoding the FST afterwards.
1206
1207   To use an instance of this class to encode or decode a mutable FST, pass it
1208   as the first argument to the FST instance methods `encode` and `decode`.
1209
1210   For implementational reasons, it is not currently possible to use an encoder
1211   on disk to construct this class.
1212
1213   Args:
1214     arc_type: A string indicating the arc type.
1215     encode_labels: Should labels be encoded?
1216     encode_weights: Should weights be encoded?
1217   """
1218
1219   def __repr__(self):
1220     return "<EncodeMapper at 0x{:x}>".format(id(self))
1221
1222   def __init__(self,
1223                arc_type=b"standard",
1224                bool encode_labels=False,
1225                bool encode_weights=False):
1226     cdef uint32 flags = fst.GetEncodeFlags(encode_labels, encode_weights)
1227     self._encoder.reset(new fst.EncodeMapperClass(tostring(arc_type), flags,
1228                                                   fst.ENCODE))
1229     if not self._encoder:
1230       raise FstOpError("Unknown arc type: {!r}".format(arc_type))
1231
1232   cpdef string arc_type(self):
1233     """
1234     arc_type(self)
1235
1236     Returns a string indicating the arc type.
1237     """
1238     return self._encoder.get().ArcType()
1239
1240   # Python's equivalent to operator().
1241
1242   def __call__(self, Arc arc):
1243     """
1244     self(state, ilabel, olabel, weight, nextstate)
1245
1246     Uses the encoder to encode an arc.
1247
1248     Args:
1249       ilabel: The integer index of the input label.
1250       olabel: The integer index of the output label.
1251       weight: A Weight or weight string indicating the desired final weight; if
1252         null, it is set to semiring One.
1253       nextstate: The integer index of the destination state.
1254
1255     Raises:
1256       FstOpError: Incompatible or invalid weight.
1257     """
1258     return _init_Arc(self._encoder.get().__call__(deref(arc._arc)))
1259
1260   cpdef uint32 flags(self):
1261     """
1262     flags(self)
1263
1264     Returns the encoder's flags.
1265     """
1266     return self._encoder.get().Flags()
1267
1268   cpdef _EncodeMapperSymbolTable input_symbols(self):
1269     """
1270     input_symbols(self)
1271
1272     Returns the encoder's input symbol table, or None if none is present.
1273     """
1274     cdef fst.SymbolTable *syms = const_cast[SymbolTable_ptr](
1275         self._encoder.get().InputSymbols())
1276     if syms == NULL:
1277       return
1278     return _init_EncodeMapperSymbolTable(syms, self._encoder)
1279
1280   cpdef _EncodeMapperSymbolTable output_symbols(self):
1281     """
1282     output_symbols(self)
1283
1284     Returns the encoder's output symbol table, or None if none is present.
1285     """
1286     cdef fst.SymbolTable *syms = const_cast[SymbolTable_ptr](
1287         self._encoder.get().OutputSymbols())
1288     if syms == NULL:
1289       return
1290     return _init_EncodeMapperSymbolTable(syms, self._encoder)
1291
1292   cpdef uint64 properties(self, uint64 mask):
1293     """
1294     properties(self, mask)
1295
1296     Provides property bits.
1297
1298     This method provides user access to the properties of the encoder.
1299
1300     Args:
1301       mask: The property mask to be compared to the encoder's properties.
1302
1303     Returns:
1304       A 64-bit bitmask representing the requested properties.
1305     """
1306     return self._encoder.get().Properties(mask)
1307
1308   cpdef void set_input_symbols(self, _SymbolTable syms) except *:
1309     """
1310     set_input_symbols(self, syms)
1311
1312     Sets the encoder's input symbol table.
1313
1314     Args:
1315       syms: A SymbolTable.
1316
1317     See also: `set_output_symbols`.
1318     """
1319     self._encoder.get().SetInputSymbols(syms._table)
1320
1321   cpdef void set_output_symbols(self, _SymbolTable syms) except *:
1322     """
1323     set_output_symbols(self, syms)
1324
1325     Sets the encoder's output symbol table.
1326
1327     Args:
1328       syms: A SymbolTable.
1329
1330     See also: `set_input_symbols`.
1331     """
1332     self._encoder.get().SetOutputSymbols(syms._table)
1333
1334   cpdef string weight_type(self):
1335     """
1336     weight_type(self)
1337
1338     Returns a string indicating the weight type.
1339     """
1340     return self._encoder.get().WeightType()
1341
1342
1343 ## _Fst, _MutableFst, Fst, and helpers.
1344 #
1345 # Fst hierarchy:
1346 #
1347 # _Fst: base class; has-a FstClass*.
1348 # _MutableFst(_Fst): adds mutable methods.
1349 # Fst(filename): pseudo-constructor.
1350
1351
1352 cdef class _Fst(object):
1353
1354   """
1355   (No constructor.)
1356
1357   Immutable FST class, wrapping FstClass.
1358
1359   This class is the basic user-facing FST object. It does not itself support any
1360   mutation operations.
1361   """
1362
1363   # IPython notebook magic to produce an SVG of the FST.
1364   def _repr_svg_(self):
1365     """IPython notebook magic to produce an SVG of the FST using GraphViz.
1366
1367     This method produces an SVG of the internal graph. Users wishing to create
1368     publication-quality graphs should instead use the method `draw`, which
1369     exposes additional parameters.
1370
1371     Raises:
1372       OSError: Cannot locate the `dot` executable.
1373       subprocess.CalledProcessError: `dot` returned non-zero exit code.
1374
1375     See also: `draw`, `text`.
1376     """
1377     # Throws OSError if the dot executable is not found.
1378     proc = subprocess.Popen(["dot", "-Tsvg"], stdin=subprocess.PIPE,
1379                             stdout=subprocess.PIPE, stderr=subprocess.PIPE)
1380     cdef stringstream sstrm
1381     fst.DrawFst(deref(self._fst), self._fst.get().InputSymbols(),
1382                 self._fst.get().OutputSymbols(), NULL,
1383                 self._fst.get().Properties(fst.kAcceptor, True) ==
1384                 fst.kAcceptor,
1385                 b"", 8.5, 11, True, False, 0.4, 0.25, 14, 5, b"g", False,
1386                 addr(sstrm), b"_repr_svg")
1387     (sout, serr) = proc.communicate(sstrm.str())
1388     if proc.returncode != 0:  # Just to be explicit.
1389       raise subprocess.CalledProcessError(proc.returncode, self._DOT_TSVG)
1390     return sout.decode("utf8")
1391
1392   def __repr__(self):
1393     return "<{} Fst at 0x{:x}>".format(self.fst_type(), id(self))
1394
1395   def __init__(self):
1396     raise FstDeletedConstructorError(
1397         "Cannot construct {}".format(self.__class__.__name__))
1398
1399    # Other magic methods.
1400
1401   def __str__(self):
1402     return self.text(acceptor=self._fst.get().Properties(fst.kAcceptor, True) ==
1403         fst.kAcceptor,
1404         show_weight_one=self._fst.get().Properties(fst.kWeighted, True) ==
1405         fst.kWeighted)
1406
1407   cpdef string arc_type(self):
1408     """
1409     arc_type(self)
1410
1411     Returns a string indicating the arc type.
1412     """
1413     return self._fst.get().ArcType()
1414
1415   cpdef ArcIterator arcs(self, int64 state):
1416     """
1417     arcs(self, state)
1418
1419     Returns an iterator over arcs leaving the specified state.
1420
1421     Args:
1422       state: The source state ID.
1423
1424     Returns:
1425       An ArcIterator.
1426
1427     See also: `mutable_arcs`, `states`.
1428     """
1429     return ArcIterator(self, state)
1430
1431   cpdef _Fst copy(self):
1432     """
1433     copy(self)
1434
1435     Makes a copy of the FST.
1436     """
1437     return _init_XFst(new fst.FstClass(deref(self._fst)))
1438
1439   cpdef void draw(self,
1440                   filename,
1441                   _SymbolTable isymbols=None,
1442                   _SymbolTable osymbols=None,
1443                   SymbolTable ssymbols=None,
1444                   bool acceptor=False,
1445                   title=b"",
1446                   double width=8.5,
1447                   double height=11,
1448                   bool portrait=False,
1449                   bool vertical=False,
1450                   double ranksep=0.4,
1451                   double nodesep=0.25,
1452                   int32 fontsize=14,
1453                   int32 precision=5,
1454                   float_format=b"g",
1455                   bool show_weight_one=False):
1456     """
1457     draw(self, filename, isymbols=None, osymbols=None, ssymbols=None,
1458          acceptor=False, title="", width=8.5, height=11, portrait=False,
1459          vertical=False, ranksep=0.4, nodesep=0.25, fontsize=14,
1460          precision=5, float_format="g", show_weight_one=False):
1461
1462     Writes out the FST in Graphviz text format.
1463
1464     This method writes out the FST in the dot graph description language. The
1465     graph can be rendered using the `dot` executable provided by Graphviz.
1466
1467     Args:
1468       filename: The string location of the output dot/Graphviz file.
1469       isymbols: An optional symbol table used to label input symbols.
1470       osymbols: An optional symbol table used to label output symbols.
1471       ssymbols: An optional symbol table used to label states.
1472       acceptor: Should the figure be rendered in acceptor format if possible?
1473       title: An optional string indicating the figure title.
1474       width: The figure width, in inches.
1475       height: The figure height, in inches.
1476       portrait: Should the figure be rendered in portrait rather than
1477           landscape?
1478       vertical: Should the figure be rendered bottom-to-top rather than
1479           left-to-right?
1480       ranksep: The minimum separation separation between ranks, in inches.
1481       nodesep: The minimum separation between nodes, in inches.
1482       fontsize: Font size, in points.
1483       precision: Numeric precision for floats, in number of chars.
1484       float_format: One of: 'e', 'f' or 'g'.
1485       show_weight_one: Should weights equivalent to semiring One be printed?
1486
1487     See also: `text`.
1488     """
1489     cdef string filename_string = tostring(filename)
1490     cdef unique_ptr[ofstream] ostrm
1491     ostrm.reset(new ofstream(filename_string))
1492     cdef fst.SymbolTable *ssymbols_ptr = NULL
1493     if ssymbols is not None:
1494       ssymbols_ptr = ssymbols._table
1495     fst.DrawFst(deref(self._fst),
1496         self._fst.get().InputSymbols() if isymbols is None
1497         else isymbols._table,
1498         self._fst.get().OutputSymbols() if osymbols is None
1499         else osymbols._table,
1500         ssymbols_ptr, acceptor, tostring(title), width, height, portrait,
1501         vertical, ranksep, nodesep, fontsize, precision,
1502         tostring(float_format), show_weight_one, ostrm.get(),
1503         filename_string)
1504
1505   cpdef Weight final(self, int64 state):
1506     """
1507     final(self, state)
1508
1509     Returns the final weight of a state.
1510
1511     Args:
1512       state: The integer index of a state.
1513
1514     Returns:
1515       The final Weight of that state.
1516
1517     Raises:
1518       FstIndexError: State index out of range.
1519     """
1520     cdef Weight weight = Weight.__new__(Weight)
1521     weight._weight.reset(new fst.WeightClass(self._fst.get().Final(state)))
1522     return weight
1523
1524   cpdef string fst_type(self):
1525     """
1526     fst_type(self)
1527
1528     Returns a string indicating the FST type.
1529     """
1530     return self._fst.get().FstType()
1531
1532   cpdef _FstSymbolTable input_symbols(self):
1533     """
1534     input_symbols(self)
1535
1536     Returns the FST's input symbol table, or None if none is present.
1537
1538     See also: `input_symbols`.
1539     """
1540     cdef fst.SymbolTable *syms = const_cast[SymbolTable_ptr](
1541       self._fst.get().InputSymbols())
1542     if syms == NULL:
1543       return
1544     return _init_FstSymbolTable(syms, self._fst)
1545
1546   cpdef size_t num_arcs(self, int64 state) except *:
1547     """
1548     num_arcs(self, state)
1549
1550     Returns the number of arcs leaving a state.
1551
1552     Args:
1553       state: The integer index of a state.
1554
1555     Returns:
1556       The number of arcs leaving that state.
1557
1558     Raises:
1559       FstIndexError: State index out of range.
1560
1561     See also: `num_states`.
1562     """
1563     cdef size_t result = self._fst.get().NumArcs(state)
1564     if result == SIZE_MAX:
1565       raise FstIndexError("State index out of range")
1566     return result
1567
1568   cpdef size_t num_input_epsilons(self, int64 state) except *:
1569     """
1570     num_input_epsilons(self, state)
1571
1572     Returns the number of arcs with epsilon input labels leaving a state.
1573
1574     Args:
1575       state: The integer index of a state.
1576
1577     Returns:
1578       The number of epsilon-input-labeled arcs leaving that state.
1579
1580     Raises:
1581       FstIndexError: State index out of range.
1582
1583     See also: `num_output_epsilons`.
1584     """
1585     cdef size_t result = self._fst.get().NumInputEpsilons(state)
1586     if result == SIZE_MAX:
1587       raise FstIndexError("State index out of range")
1588     return result
1589
1590   cpdef size_t num_output_epsilons(self, int64 state) except *:
1591     """
1592     num_output_epsilons(self, state)
1593
1594     Returns the number of arcs with epsilon output labels leaving a state.
1595
1596     Args:
1597       state: The integer index of a state.
1598
1599     Returns:
1600       The number of epsilon-output-labeled arcs leaving that state.
1601
1602     Raises:
1603       FstIndexError: State index out of range.
1604
1605     See also: `num_input_epsilons`.
1606     """
1607     cdef size_t result = self._fst.get().NumOutputEpsilons(state)
1608     if result == SIZE_MAX:
1609       raise FstIndexError("State index out of range")
1610     return result
1611
1612   cpdef _FstSymbolTable output_symbols(self):
1613     """
1614     output_symbols(self)
1615
1616     Returns the FST's output symbol table, or None if none is present.
1617
1618     See also: `input_symbols`.
1619     """
1620     cdef fst.SymbolTable *syms = const_cast[SymbolTable_ptr](
1621       self._fst.get().OutputSymbols())
1622     if syms == NULL:
1623       return
1624     return _init_FstSymbolTable(syms, self._fst)
1625
1626   cpdef uint64 properties(self, uint64 mask, bool test):
1627     """
1628     properties(self, mask, test)
1629
1630     Provides property bits.
1631
1632     This method provides user access to the properties attributes for the FST.
1633     The resulting value is a long integer, but when it is cast to a boolean,
1634     it represents whether or not the FST has the `mask` property.
1635
1636     Args:
1637       mask: The property mask to be compared to the FST's properties.
1638       test: Should any unknown values be computed before comparing against
1639           the mask?
1640
1641     Returns:
1642       A 64-bit bitmask representing the requested properties.
1643     """
1644     return self._fst.get().Properties(mask, test)
1645
1646   cpdef int64 start(self):
1647     """
1648     start(self)
1649
1650     Returns the start state.
1651     """
1652     return self._fst.get().Start()
1653
1654   cpdef StateIterator states(self):
1655     """
1656     states(self)
1657
1658     Returns an iterator over all states in the FST.
1659
1660     Returns:
1661       A StateIterator object for the FST.
1662
1663     See also: `arcs`, `mutable_arcs`.
1664     """
1665     return StateIterator(self)
1666
1667   cpdef string text(self, _SymbolTable isymbols=None,
1668       _SymbolTable osymbols=None, _SymbolTable ssymbols=None,
1669       bool acceptor=False, bool show_weight_one=False, missing_sym=b""):
1670     """
1671     text(self, isymbols=None, osymbols=None, ssymbols=None, acceptor=False,
1672          show_weight_one=False, missing_sym="")
1673
1674     Produces a human-readable string representation of the FST.
1675
1676     This method generates a human-readable string representation of the FST.
1677     The caller may optionally specify SymbolTables used to label input labels,
1678     output labels, or state labels, respectively.
1679
1680     Args:
1681       isymbols: An optional symbol table used to label input symbols.
1682       osymbols: An optional symbol table used to label output symbols.
1683       ssymbols: An optional symbol table used to label states.
1684       acceptor: Should the FST be rendered in acceptor format if possible?
1685       show_weight_one: Should weights equivalent to semiring One be printed?
1686       missing_symbol: The string to be printed when symbol table lookup fails.
1687
1688     Returns:
1689       A formatted string representing the machine.
1690     """
1691     # Prints FST to stringstream, then returns resulting string.
1692     cdef fst.SymbolTable *ssymbols_ptr = NULL
1693     if ssymbols is not None:
1694       ssymbols_ptr = ssymbols._table
1695     cdef stringstream sstrm
1696     fst.PrintFst(deref(self._fst), sstrm, b"<pywrapfst>",
1697         self._fst.get().InputSymbols() if isymbols is None
1698         else isymbols._table,
1699         self._fst.get().OutputSymbols() if osymbols is None
1700         else osymbols._table,
1701         ssymbols_ptr, acceptor, show_weight_one, tostring(missing_sym))
1702     return sstrm.str()
1703
1704   cpdef bool verify(self):
1705     """
1706     verify(self)
1707
1708     Verifies that an FST's contents are sane.
1709
1710     Returns:
1711       True if the contents are sane, False otherwise.
1712     """
1713     return fst.Verify(deref(self._fst))
1714
1715   cpdef string weight_type(self):
1716     """
1717     weight_type(self)
1718
1719     Provides the FST's weight type.
1720
1721     Returns:
1722       A string representing the weight type.
1723     """
1724     return self._fst.get().WeightType()
1725
1726   cpdef void write(self, filename) except *:
1727     """
1728     write(self, filename)
1729
1730     Serializes FST to a file.
1731
1732     This method writes the FST to a file in a binary format.
1733
1734     Args:
1735       filename: The string location of the output file.
1736
1737     Raises:
1738       FstIOError: Write failed.
1739     """
1740     if not self._fst.get().Write(tostring(filename)):
1741       raise FstIOError("Write failed: {!r}".format(filename))
1742
1743   cpdef string write_to_string(self):
1744     """
1745     write_to_string(self)
1746
1747     Serializes FST to a string.
1748
1749     Returns:
1750       A string.
1751
1752     See also: `read_from_string`.
1753     """
1754     return self._fst.get().WriteToString()
1755
1756
1757 cdef class _MutableFst(_Fst):
1758
1759   """
1760   (No constructor.)
1761
1762   Mutable FST class, wrapping MutableFstClass.
1763
1764   This class extends _Fst by adding mutation operations.
1765   """
1766
1767   cdef void _check_mutating_imethod(self) except *:
1768     """Checks whether an operation mutating the FST has produced an error.
1769
1770     This function is not visible to Python users.
1771     """
1772     if self._fst.get().Properties(fst.kError, True) == fst.kError:
1773       raise FstOpError("Operation failed")
1774
1775   cdef void _add_arc(self, int64 state, Arc arc) except *:
1776     if not self._fst.get().ValidStateId(state):
1777       raise FstIndexError("State index out of range")
1778     if not self._mfst.get().AddArc(state, deref(arc._arc)):
1779       raise FstOpError("Incompatible or invalid weight type")
1780     self._check_mutating_imethod()
1781
1782   def add_arc(self, int64 state, Arc arc):
1783     """
1784     add_arc(self, state, arc)
1785
1786     Adds a new arc to the FST and return self.
1787
1788     Args:
1789       state: The integer index of the source state.
1790       arc: The arc to add.
1791
1792     Returns:
1793       self.
1794
1795     Raises:
1796       FstIndexError: State index out of range.
1797       FstOpdexError: Incompatible or invalid weight type.
1798
1799     See also: `add_state`.
1800     """
1801     self._add_arc(state, arc)
1802     return self
1803
1804   cpdef int64 add_state(self) except *:
1805     """
1806     add_state(self)
1807
1808     Adds a new state to the FST and returns the state ID.
1809
1810     Returns:
1811       The integer index of the new state.
1812
1813     See also: `add_arc`, `set_start`, `set_final`.
1814     """
1815     cdef int64 result = self._mfst.get().AddState()
1816     self._check_mutating_imethod()
1817     return result
1818
1819   cdef void _arcsort(self, sort_type=b"ilabel") except *:
1820     cdef fst.ArcSortType sort_type_enum
1821     if not fst.GetArcSortType(tostring(sort_type), addr(sort_type_enum)):
1822       raise FstArgError("Unknown sort type {!r}".format(sort_type))
1823     fst.ArcSort(self._mfst.get(), sort_type_enum)
1824     self._check_mutating_imethod()
1825
1826   def arcsort(self, sort_type=b"ilabel"):
1827     """
1828     arcsort(self, sort_type="ilabel")
1829
1830     Sorts arcs leaving each state of the FST.
1831
1832     This operation destructively sorts arcs leaving each state using either
1833     input or output labels.
1834
1835     Args:
1836       sort_type: Either "ilabel" (sort arcs according to input labels) or
1837           "olabel" (sort arcs according to output labels).
1838
1839     Returns:
1840       self.
1841
1842     Raises:
1843       FstArgError: Unknown sort type.
1844
1845     See also: `topsort`.
1846     """
1847     self._arcsort(sort_type)
1848     return self
1849
1850   cdef void _closure(self, bool closure_plus=False) except *:
1851     fst.Closure(self._mfst.get(), fst.GetClosureType(closure_plus))
1852     self._check_mutating_imethod()
1853
1854   def closure(self, bool closure_plus=False):
1855     """
1856     closure(self, closure_plus=False)
1857
1858     Computes concatenative closure.
1859
1860     This operation destructively converts the FST to its concatenative closure.
1861     If A transduces string x to y with weight a, then the closure transduces x
1862     to y with weight a, xx to yy with weight a \otimes a, xxx to yyy with weight
1863     a \otimes a \otimes a, and so on. The empty string is also transduced to
1864     itself with semiring One if `closure_plus` is False.
1865
1866     Args:
1867       closure_plus: If False, do not accept the empty string.
1868
1869     Returns:
1870       self.
1871     """
1872     self._closure(closure_plus)
1873     return self
1874
1875   cdef void _concat(self, _Fst ifst) except *:
1876     fst.Concat(self._mfst.get(), deref(ifst._fst))
1877     self._check_mutating_imethod()
1878
1879   def concat(self, _Fst ifst):
1880     """
1881     concat(self, ifst)
1882
1883     Computes the concatenation (product) of two FSTs.
1884
1885     This operation destructively concatenates the FST with a second FST. If A
1886     transduces string x to y with weight a and B transduces string w to v with
1887     weight b, then their concatenation transduces string xw to yv with weight a
1888     \otimes b.
1889
1890     Args:
1891       ifst: The second input FST.
1892
1893     Returns:
1894       self.
1895     """
1896     self._concat(ifst)
1897     return self
1898
1899   cdef void _connect(self) except *:
1900     fst.Connect(self._mfst.get())
1901     self._check_mutating_imethod()
1902
1903   def connect(self):
1904     """
1905     connect(self)
1906
1907     Removes unsuccessful paths.
1908
1909     This operation destructively trims the FST, removing states and arcs that
1910     are not part of any successful path.
1911
1912     Returns:
1913       self.
1914     """
1915     self._connect()
1916     return self
1917
1918   cdef void _decode(self, EncodeMapper encoder) except *:
1919     fst.Decode(self._mfst.get(), deref(encoder._encoder))
1920     self._check_mutating_imethod()
1921
1922   def decode(self, EncodeMapper encoder):
1923     """
1924     decode(self, encoder)
1925
1926     Decodes encoded labels and/or weights.
1927
1928     This operation reverses the encoding performed by `encode`.
1929
1930     Args:
1931       encoder: An EncodeMapper object used to encode the FST.
1932
1933     Returns:
1934       self.
1935
1936     See also: `encode`.
1937     """
1938     self._decode(encoder)
1939     return self
1940
1941   cdef void _delete_arcs(self, int64 state, size_t n=0) except *:
1942     if not (self._mfst.get().DeleteArcs(state, n) if n else
1943             self._mfst.get().DeleteArcs(state)):
1944       raise FstIndexError("State index out of range")
1945     self._check_mutating_imethod()
1946
1947   def delete_arcs(self, int64 state, size_t n=0):
1948     """
1949     delete_arcs(self, state, n=0)
1950
1951     Deletes arcs leaving a particular state.
1952
1953     Args:
1954       state: The integer index of a state.
1955       n: An optional argument indicating how many arcs to be deleted. If this
1956           argument is omitted or passed as zero, all arcs from this state are
1957           deleted.
1958
1959     Returns:
1960       self.
1961
1962     Raises:
1963       FstIndexError: State index out of range.
1964
1965     See also: `delete_states`.
1966     """
1967     self._delete_arcs(state, n)
1968     return self
1969
1970   cdef void _delete_states(self, states=None) except *:
1971     # Only the former signature has a possible indexing failure.
1972     if states:
1973       if not self._mfst.get().DeleteStates(<const vector[int64]> states):
1974         raise FstIndexError("State index out of range")
1975     else:
1976       self._mfst.get().DeleteStates()
1977     self._check_mutating_imethod()
1978
1979   def delete_states(self, states=None):
1980     """
1981     delete_states(self, states=None)
1982
1983     Deletes states.
1984
1985     Args:
1986       states: An optional iterable of integer indices of the states to be
1987           deleted. If this argument is omitted, all states are deleted.
1988
1989     Returns:
1990       self.
1991
1992     Raises:
1993       FstIndexError: State index out of range.
1994
1995     See also: `delete_arcs`.
1996     """
1997     self._delete_states(states)
1998     return self
1999
2000   cdef void _encode(self, EncodeMapper encoder) except *:
2001     fst.Encode(self._mfst.get(), encoder._encoder.get())
2002     self._check_mutating_imethod()
2003
2004   def encode(self, EncodeMapper encoder):
2005     """
2006     encode(self, encoder)
2007
2008     Encodes labels and/or weights.
2009
2010     This operation allows for the representation of a weighted transducer as a
2011     weighted acceptor, an unweighted transducer, or an unweighted acceptor by
2012     considering the pair (input label, output label), the pair (input label,
2013     weight), or the triple (input label, output label, weight) as a single
2014     label. Applying this operation mutates the EncodeMapper argument, which
2015     can then be used to decode.
2016
2017     Args:
2018       encoder: An EncodeMapper object to be used as the encoder.
2019
2020     Returns:
2021       self.
2022
2023     See also: `decode`.
2024     """
2025     self._encode(encoder)
2026     return self
2027
2028   cdef void _invert(self) except *:
2029     fst.Invert(self._mfst.get())
2030     self._check_mutating_imethod()
2031
2032   def invert(self):
2033     """
2034     invert(self)
2035
2036     Inverts the FST's transduction.
2037
2038     This operation destructively inverts the FST's transduction by exchanging
2039     input and output labels.
2040
2041     Returns:
2042       self.
2043     """
2044     self._invert()
2045     return self
2046
2047   cdef void _minimize(self, float delta=fst.kDelta,
2048                       bool allow_nondet=False) except *:
2049     # This runs in-place when the second argument is null.
2050     fst.Minimize(self._mfst.get(), NULL, delta, allow_nondet)
2051     self._check_mutating_imethod()
2052
2053   def minimize(self, float delta=fst.kDelta, bool allow_nondet=False):
2054     """
2055     minimize(self, delta=0.0009765625, allow_nondet=False)
2056
2057     Minimizes the FST.
2058
2059     This operation destructively performs the minimization of deterministic
2060     weighted automata and transducers. If the input FST A is an acceptor, this
2061     operation produces the minimal acceptor B equivalent to A, i.e. the
2062     acceptor with a minimal number of states that is equivalent to A. If the
2063     input FST A is a transducer, this operation internally builds an equivalent
2064     transducer with a minimal number of states. However, this minimality is
2065     obtained by allowing transition having strings of symbols as output labels,
2066     this known in the litterature as a real-time transducer. Such transducers
2067     are not directly supported by the library. This function will convert such
2068     transducer by expanding each string-labeled transition into a sequence of
2069     transitions. This will results in the creation of new states, hence losing
2070     the minimality property.
2071
2072     Args:
2073       delta: Comparison/quantization delta.
2074       allow_nondet: Attempt minimization of non-deterministic FST?
2075
2076     Returns:
2077       self.
2078     """
2079     self._minimize(delta)
2080     return self
2081
2082   cpdef MutableArcIterator mutable_arcs(self, int64 state):
2083     """
2084     mutable_arcs(self, state)
2085
2086     Returns a mutable iterator over arcs leaving the specified state.
2087
2088     Args:
2089       state: The source state ID.
2090
2091     Returns:
2092       A MutableArcIterator.
2093
2094     See also: `arcs`, `states`.
2095     """
2096     return MutableArcIterator(self, state)
2097
2098   def mutable_input_symbols(self):
2099     """
2100     mutable_input_symbols(self)
2101
2102     Returns the FST's (mutable) input symbol table, or None if none is present.
2103     """
2104     cdef fst.SymbolTable *tst = self._mfst.get().MutableInputSymbols()
2105     if tst == NULL:
2106       return
2107     return _init_MutableFstSymbolTable(tst, self._mfst)
2108
2109   def mutable_output_symbols(self):
2110     """
2111     mutable_output_symbols(self)
2112
2113     Returns the FST's (mutable) output symbol table, or None if none is present.
2114     """
2115     cdef fst.SymbolTable *tst = self._mfst.get().MutableOutputSymbols()
2116     if tst == NULL:
2117       return
2118     return _init_MutableFstSymbolTable(tst, self._mfst)
2119
2120   cpdef int64 num_states(self):
2121     """
2122     num_states(self)
2123
2124     Returns the number of states.
2125     """
2126     return self._mfst.get().NumStates()
2127
2128   cdef void _project(self, bool project_output=False) except *:
2129     fst.Project(self._mfst.get(), fst.GetProjectType(project_output))
2130     self._check_mutating_imethod()
2131
2132   def project(self, bool project_output=False):
2133     """
2134     project(self, project_output=False)
2135
2136     Converts the FST to an acceptor using input or output labels.
2137
2138     This operation destructively projects an FST onto its domain or range by
2139     either copying each arc's input label to its output label (the default) or
2140     vice versa.
2141
2142     Args:
2143       project_output: Should the output labels be projected?
2144
2145     Returns:
2146       self.
2147
2148     See also: `decode`, `encode`, `relabel_pairs`, `relabel_symbols`.
2149     """
2150     self._project(project_output)
2151     return self
2152
2153   cdef void _prune(self, float delta=fst.kDelta, int64 nstate=fst.kNoStateId,
2154                    weight=None) except *:
2155     # Threshold is set to semiring Zero (no pruning) if no weight is specified.
2156     cdef fst.WeightClass wc = _get_WeightClass_or_Zero(self.weight_type(),
2157                                                        weight)
2158     fst.Prune(self._mfst.get(), wc, nstate, delta)
2159     self._check_mutating_imethod()
2160
2161   def prune(self,
2162             float delta=fst.kDelta,
2163             int64 nstate=fst.kNoStateId,
2164             weight=None):
2165     """
2166     prune(self, delta=0.0009765625, nstate=NO_STATE_ID, weight=None)
2167
2168     Removes paths with weights below a certain threshold.
2169
2170     This operation deletes states and arcs in the input FST that do not belong
2171     to a successful path whose weight is no more (w.r.t the natural semiring
2172     order) than the threshold t \otimes-times the weight of the shortest path in
2173     the input FST. Weights must be commutative and have the path property.
2174
2175     Args:
2176       delta: Comparison/quantization delta.
2177       nstate: State number threshold.
2178       weight: A Weight or weight string indicating the desired weight threshold
2179           below which paths are pruned; if omitted, no paths are pruned.
2180
2181     Returns:
2182       self.
2183
2184     See also: The constructive variant.
2185     """
2186     self._prune(delta, nstate, weight)
2187     return self
2188
2189   cdef void _push(self,
2190                   float delta=fst.kDelta,
2191                   bool remove_total_weight=False,
2192                   bool to_final=False) except *:
2193     fst.Push(self._mfst.get(), fst.GetReweightType(to_final), delta,
2194              remove_total_weight)
2195     self._check_mutating_imethod()
2196
2197   def push(self,
2198            float delta=fst.kDelta,
2199            bool remove_total_weight=False,
2200            bool to_final=False):
2201     """
2202     push(self, delta=0.0009765625, remove_total_weight=False, to_final=False)
2203
2204     Pushes weights towards the initial or final states.
2205
2206     This operation destructively produces an equivalent transducer by pushing
2207     the weights towards the initial state or toward the final states. When
2208     pushing weights towards the initial state, the sum of the weight of the
2209     outgoing transitions and final weight at any non-initial state is equal to
2210     one in the resulting machine. When pushing weights towards the final states,
2211     the sum of the weight of the incoming transitions at any state is equal to
2212     one. Weights need to be left distributive when pushing towards the initial
2213     state and right distributive when pushing towards the final states.
2214
2215     Args:
2216       delta: Comparison/quantization delta.
2217       remove_total_weight: If pushing weights, should the total weight be
2218           removed?
2219       to_final: Push towards final states?
2220
2221     Returns:
2222       self.
2223
2224     See also: The constructive variant, which also supports label pushing.
2225     """
2226     self._push(delta, remove_total_weight, to_final)
2227     return self
2228
2229   cdef void _relabel_pairs(self, ipairs=None, opairs=None) except *:
2230     cdef unique_ptr[vector[fst.LabelPair]] _ipairs
2231     _ipairs.reset(new vector[fst.LabelPair]())
2232     cdef unique_ptr[vector[fst.LabelPair]] _opairs
2233     _opairs.reset(new vector[fst.LabelPair]())
2234     cdef int64 before
2235     cdef int64 after
2236     if ipairs:
2237       for (before, after) in ipairs:
2238         _ipairs.get().push_back(fst.LabelPair(before, after))
2239     if opairs:
2240       for (before, after) in opairs:
2241         _opairs.get().push_back(fst.LabelPair(before, after))
2242     if _ipairs.get().empty() and _opairs.get().empty():
2243       raise FstArgError("No relabeling pairs specified.")
2244     fst.Relabel(self._mfst.get(), deref(_ipairs), deref(_opairs))
2245     self._check_mutating_imethod()
2246
2247   def relabel_pairs(self, ipairs=None, opairs=None):
2248     """
2249     relabel_pairs(self, ipairs=None, opairs=None)
2250
2251     Replaces input and/or output labels using pairs of labels.
2252
2253     This operation destructively relabels the input and/or output labels of the
2254     FST using pairs of the form (old_ID, new_ID); omitted indices are
2255     identity-mapped.
2256
2257     Args:
2258       ipairs: An iterable containing (older index, newer index) integer pairs.
2259       opairs: An iterable containing (older index, newer index) integer pairs.
2260
2261     Returns:
2262       self.
2263
2264     Raises:
2265       FstArgError: No relabeling pairs specified.
2266
2267     See also: `decode`, `encode`, `project`, `relabel_tables`.
2268     """
2269     self._relabel_pairs(ipairs, opairs)
2270     return self
2271
2272   cdef void _relabel_tables(self,
2273                             _SymbolTable old_isymbols=None,
2274                             _SymbolTable new_isymbols=None,
2275                             unknown_isymbol=b"",
2276                             bool attach_new_isymbols=True,
2277                             _SymbolTable old_osymbols=None,
2278                             _SymbolTable new_osymbols=None,
2279                             unknown_osymbol=b"",
2280                             bool attach_new_osymbols=True) except *:
2281     if new_isymbols is None and new_osymbols is None:
2282       raise FstArgError("No new SymbolTables specified")
2283     cdef fst.SymbolTable *new_isymbols_ptr = NULL
2284     if new_isymbols is not None:
2285       new_isymbols_ptr = new_isymbols._table
2286     cdef fst.SymbolTable *new_osymbols_ptr = NULL
2287     if new_osymbols is not None:
2288       new_osymbols_ptr = new_osymbols._table
2289     fst.Relabel(self._mfst.get(),
2290         self._fst.get().InputSymbols() if old_isymbols is None else
2291         old_isymbols._table, new_isymbols_ptr, tostring(unknown_isymbol),
2292         attach_new_isymbols,
2293         self._fst.get().OutputSymbols() if old_osymbols is None else
2294         old_osymbols._table, new_osymbols_ptr, tostring(unknown_osymbol),
2295         attach_new_osymbols)
2296     self._check_mutating_imethod()
2297
2298   def relabel_tables(self,
2299                      _SymbolTable old_isymbols=None,
2300                      _SymbolTable new_isymbols=None,
2301                      unknown_isymbol=b"",
2302                      bool attach_new_isymbols=True,
2303                      _SymbolTable old_osymbols=None,
2304                      _SymbolTable new_osymbols=None,
2305                      unknown_osymbol=b"",
2306                      bool attach_new_osymbols=True):
2307     """
2308     relabel_tables(self, old_isymbols=None, new_isymbols=None,
2309                    unknown_isymbol="", attach_new_isymbols=True,
2310                    old_osymbols=None, new_osymbols=None,
2311                    unknown_osymbol="", attach_new_osymbols=True)
2312
2313     Replaces input and/or output labels using SymbolTables.
2314
2315     This operation destructively relabels the input and/or output labels of the
2316     FST using user-specified symbol tables; omitted symbols are identity-mapped.
2317
2318     Args:
2319        old_isymbols: The old SymbolTable for input labels, defaulting to the
2320           FST's input symbol table.
2321        new_isymbols: A SymbolTable used to relabel the input labels
2322        unknown_isymbol: Input symbol to use to relabel OOVs (if empty,
2323           OOVs raise an exception)
2324        attach_new_isymbols: Should new_isymbols be made the FST's input symbol
2325           table?
2326        old_osymbols: The old SymbolTable for output labels, defaulting to the
2327           FST's output symbol table.
2328        new_osymbols: A SymbolTable used to relabel the output labels.
2329        unknown_osymbol: Outnput symbol to use to relabel OOVs (if empty,
2330           OOVs raise an exception)
2331        attach_new_isymbols: Should new_osymbols be made the FST's output symbol
2332           table?
2333
2334     Returns:
2335       self.
2336
2337     Raises:
2338       FstArgError: No SymbolTable specified.
2339
2340     See also: `decode`, `encode`, `project`, `relabel_pairs`.
2341     """
2342     self._relabel_tables(old_isymbols, new_isymbols,
2343                          unknown_isymbol, attach_new_isymbols,
2344                          old_osymbols, new_osymbols,
2345                          unknown_osymbol, attach_new_osymbols)
2346     return self
2347
2348   cdef void _reserve_arcs(self, int64 state, size_t n) except *:
2349     if not self._mfst.get().ReserveArcs(state, n):
2350       raise FstIndexError("State index out of range")
2351     self._check_mutating_imethod()
2352
2353   def reserve_arcs(self, int64 state, size_t n):
2354     """
2355     reserve_arcs(self, state, n)
2356
2357     Reserve n arcs at a particular state (best effort).
2358
2359     Args:
2360       state: The integer index of a state.
2361       n: The number of arcs to reserve.
2362
2363     Returns:
2364       self.
2365
2366     Raises:
2367       FstIndexError: State index out of range.
2368
2369     See also: `reserve_states`.
2370     """
2371     self._reserve_arcs(state, n)
2372     return self
2373
2374   cdef void _reserve_states(self, int64 n) except *:
2375     self._mfst.get().ReserveStates(n)
2376     self._check_mutating_imethod()
2377
2378   def reserve_states(self, int64 n):
2379     """
2380     reserve_states(self, n)
2381
2382     Reserve n states (best effort).
2383
2384     Args:
2385       n: The number of states to reserve.
2386
2387     Returns:
2388       self.
2389
2390     See also: `reserve_arcs`.
2391     """
2392     self._reserve_states(n)
2393     return self
2394
2395   cdef void _reweight(self, potentials, bool to_final=False) except *:
2396     cdef unique_ptr[vector[fst.WeightClass]] _potentials
2397     _potentials.reset(new vector[fst.WeightClass]())
2398     cdef string weight_type = self.weight_type()
2399     for weight in potentials:
2400         _potentials.get().push_back(_get_WeightClass_or_One(self.weight_type(),
2401                                                             weight))
2402     fst.Reweight(self._mfst.get(), deref(_potentials),
2403                  fst.GetReweightType(to_final))
2404     self._check_mutating_imethod()
2405
2406   def reweight(self, potentials, bool to_final=False):
2407     """
2408     reweight(self, potentials, to_final=False)
2409
2410     Reweights an FST using an iterable of potentials.
2411
2412     This operation destructively reweights an FST according to the potentials
2413     and in the direction specified by the user. An arc of weight w, with an
2414     origin state of potential p and destination state of potential q, is
2415     reweighted by p^{-1} \otimes (w \otimes q) when reweighting towards the
2416     initial state, and by (p \otimes w) \otimes q^{-1} when reweighting towards
2417     the final states. The weights must be left distributive when reweighting
2418     towards the initial state and right distributive when reweighting towards
2419     the final states (e.g., TropicalWeight and LogWeight).
2420
2421     Args:
2422       potentials: An iterable of Weight or weight strings.
2423       to_final: Push towards final states?
2424
2425     Returns:
2426       self.
2427     """
2428     self._reweight(potentials, to_final)
2429     return self
2430
2431   cdef void _rmepsilon(self,
2432                        queue_type=b"auto",
2433                        bool connect=True,
2434                        weight=None,
2435                        int64 nstate=fst.kNoStateId,
2436                        float delta=fst.kDelta) except *:
2437     cdef fst.WeightClass wc = _get_WeightClass_or_Zero(self.weight_type(),
2438                                                        weight)
2439     cdef unique_ptr[fst.RmEpsilonOptions] opts
2440     opts.reset(new fst.RmEpsilonOptions(_get_queue_type(tostring(queue_type)),
2441                                         connect, wc, nstate, delta))
2442     fst.RmEpsilon(self._mfst.get(), deref(opts))
2443     self._check_mutating_imethod()
2444
2445   def rmepsilon(self,
2446                 queue_type=b"auto",
2447                 bool connect=True,
2448                 weight=None,
2449                 int64 nstate=fst.kNoStateId,
2450                 float delta=fst.kDelta):
2451     """
2452     rmepsilon(self, queue_type="auto", connect=True, weight=None,
2453               nstate=NO_STATE_ID, delta=0.0009765625):
2454
2455     Removes epsilon transitions.
2456
2457     This operation destructively removes epsilon transitions, i.e., those where
2458     both input and output labels are epsilon) from an FST.
2459
2460     Args:
2461       queue_type: A string matching a known queue type; one of: "auto", "fifo",
2462           "lifo", "shortest", "state", "top".
2463       connect: Should output be trimmed?
2464       weight: A Weight or weight string indicating the desired weight threshold
2465           below which paths are pruned; if omitted, no paths are pruned.
2466       nstate: State number threshold.
2467       delta: Comparison/quantization delta.
2468
2469     Returns:
2470       self.
2471     """
2472     self._rmepsilon(queue_type, connect, weight, nstate, delta)
2473     return self
2474
2475   cdef void _set_final(self, int64 state, weight=None) except *:
2476     if not self._mfst.get().ValidStateId(state):
2477       raise FstIndexError("State index out of range")
2478     cdef fst.WeightClass wc = _get_WeightClass_or_One(self.weight_type(),
2479                                                       weight)
2480     if not self._mfst.get().SetFinal(state, wc):
2481       raise FstOpError("Incompatible or invalid weight")
2482     self._check_mutating_imethod()
2483
2484   def set_final(self, int64 state, weight=None):
2485     """
2486     set_final(self, state, weight)
2487
2488     Sets the final weight for a state.
2489
2490     Args:
2491       state: The integer index of a state.
2492       weight: A Weight or weight string indicating the desired final weight; if
2493           omitted, it is set to semiring One.
2494
2495     Returns:
2496       self.
2497
2498     Raises:
2499       FstIndexError: State index out of range.
2500       FstOpError: Incompatible or invalid weight.
2501
2502     See also: `set_start`.
2503     """
2504     self._set_final(state, weight)
2505     return self
2506
2507   cdef void _set_input_symbols(self, _SymbolTable syms) except *:
2508     if syms is None:
2509       self._mfst.get().SetInputSymbols(NULL)
2510       return
2511     self._mfst.get().SetInputSymbols(syms._table)
2512     self._check_mutating_imethod()
2513
2514   def set_input_symbols(self, _SymbolTable syms):
2515     """
2516     set_input_symbols(self, syms)
2517
2518     Sets the input symbol table.
2519
2520     Passing None as a value will delete the input symbol table.
2521
2522     Args:
2523       syms: A SymbolTable.
2524
2525     Returns:
2526       self.
2527
2528     See also: `set_output_symbols`.
2529     """
2530     self._set_input_symbols(syms)
2531     return self
2532
2533   cdef void _set_output_symbols(self, _SymbolTable syms) except *:
2534     if syms is None:
2535       self._mfst.get().SetOutputSymbols(NULL)
2536       return
2537     self._mfst.get().SetOutputSymbols(syms._table)
2538     self._check_mutating_imethod()
2539
2540   def set_output_symbols(self, _SymbolTable syms):
2541     """
2542     set_output_symbols(self, syms)
2543
2544     Sets the output symbol table.
2545
2546     Passing None as a value will delete the output symbol table.
2547
2548     Args:
2549       syms: A SymbolTable.
2550
2551     Returns:
2552       self.
2553
2554     See also: `set_input_symbols`.
2555     """
2556     self._set_output_symbols(syms)
2557     return self
2558
2559   cdef void _set_properties(self, uint64 props, uint64 mask):
2560     self._mfst.get().SetProperties(props, mask)
2561
2562   def set_properties(self, uint64 props, uint64 mask):
2563     """
2564     set_properties(self, props, mask)
2565
2566     Sets the properties bits.
2567
2568     Args:
2569       props: The properties to be set.
2570       mask: A mask to be applied to the `props` argument before setting the
2571           FST's properties.
2572
2573     Returns:
2574       self.
2575     """
2576     self._set_properties(props, mask)
2577     return self
2578
2579   cdef void _set_start(self, int64 state) except *:
2580     if not self._mfst.get().SetStart(state):
2581       raise FstIndexError("State index out of range")
2582     self._check_mutating_imethod()
2583
2584   def set_start(self, int64 state):
2585     """
2586     set_start(self, state)
2587
2588     Sets a state to be the initial state state.
2589
2590     Args:
2591       state: The integer index of a state.
2592
2593     Returns:
2594       self.
2595
2596     Raises:
2597       FstIndexError: State index out of range.
2598
2599     See also: `set_final`.
2600     """
2601     self._set_start(state)
2602     return self
2603
2604   cdef void _topsort(self) except *:
2605     # TopSort returns False if the FST is cyclic, and thus can't be TopSorted.
2606     if not fst.TopSort(self._mfst.get()):
2607       logging.warning("Cannot topsort cyclic FST.")
2608     self._check_mutating_imethod()
2609
2610   def topsort(self):
2611     """
2612     topsort(self)
2613
2614     Sorts transitions by state IDs.
2615
2616     This operation destructively topologically sorts the FST, if it is acyclic;
2617     otherwise it remains unchanged. Once sorted, all transitions are from lower
2618     state IDs to higher state IDs
2619
2620     Returns:
2621        self.
2622
2623     See also: `arcsort`.
2624     """
2625     self._topsort()
2626     return self
2627
2628   cdef void _union(self, _Fst ifst) except *:
2629     fst.Union(self._mfst.get(), deref(ifst._fst))
2630     self._check_mutating_imethod()
2631
2632   def union(self, _Fst ifst):
2633     """
2634     union(self, ifst)
2635
2636     Computes the union (sum) of two FSTs.
2637
2638     This operation computes the union (sum) of two FSTs. If A transduces string
2639     x to y with weight a and B transduces string w to v with weight b, then
2640     their union transduces x to y with weight a and w to v with weight b.
2641
2642     Args:
2643       ifst: The second input FST.
2644
2645     Returns:
2646       self.
2647     """
2648     self._union(ifst)
2649     return self
2650
2651
2652 # Pseudo-constructors for _Fst and _MutableFst.
2653 #
2654 # _init_Fst and _init_MutableFst use an FstClass pointer to instantiate _Fst
2655 # and _MutableFst objects, respectively. The latter function is only safe to
2656 # call when the FST being wrapped is known to be kMutable. The caller can
2657 # safely use it when they have either checked this bit (e.g., by using
2658 # _init_XFst) or have themselves constructed a mutable container for the
2659 # FstClass pointer they're passing (e.g., most of the constructive operations,
2660 # storing their results in a VectorFstClass, a derivative of MutableFstClass).
2661 #
2662 # _create_Fst constructs an empty VectorFstClass of a user-specified arc type,
2663 # and passes this pointer to _init_MutableFst.
2664 #
2665 # _read_Fst reads an FST from disk, performing FST conversion if requested, and
2666 # then passes this pointer to _init_XFst.
2667 #
2668 # The Python class Fst provides a wrapper for these two operations. The former
2669 # can be accessed by calling Fst(...), which acts like a class method, and the
2670 # latter via Fst.read(...), which acts like a static method. This is a bit
2671 # nasty, but totally hidden from the Python user.
2672
2673
2674 cdef _Fst _init_Fst(FstClass_ptr tfst):
2675   if tfst.Properties(fst.kError, True):
2676     raise FstOpError("Operation failed")
2677   cdef _Fst ofst = _Fst.__new__(_Fst)
2678   ofst._fst.reset(<FstClass_ptr> tfst)
2679   return ofst
2680
2681
2682 cdef _MutableFst _init_MutableFst(MutableFstClass_ptr tfst):
2683   if tfst.Properties(fst.kError, True):
2684     raise FstOpError("Operation failed")
2685   cdef _MutableFst ofst = _MutableFst.__new__(_MutableFst)
2686   ofst._fst.reset(<MutableFstClass_ptr> tfst)
2687   # Makes a copy of it as the derived type! Cool.
2688   ofst._mfst = static_pointer_cast[fst.MutableFstClass, fst.FstClass](ofst._fst)
2689   return ofst
2690
2691
2692 cdef _Fst _init_XFst(FstClass_ptr tfst):
2693   if tfst.Properties(fst.kMutable, True):
2694     return _init_MutableFst(static_cast[MutableFstClass_ptr](tfst))
2695   else:
2696     return _init_Fst(tfst)
2697
2698
2699 cdef _MutableFst _create_Fst(arc_type=b"standard"):
2700   cdef unique_ptr[fst.VectorFstClass] tfst
2701   tfst.reset(new fst.VectorFstClass(<string> tostring(arc_type)))
2702   if tfst.get() == NULL:
2703     raise FstOpError("Unknown arc type: {!r}".format(arc_type))
2704   return _init_MutableFst(tfst.release())
2705
2706
2707 cdef _Fst _read_Fst(filename, fst_type=None):
2708   cdef unique_ptr[fst.FstClass] tfst
2709   tfst.reset(fst.FstClass.Read(tostring(filename)))
2710   if tfst.get() == NULL:
2711     raise FstIOError("Read failed: {!r}".format(filename))
2712   # Converts if requested.
2713   cdef string fst_type_string
2714   if fst_type:
2715     fst_type_string = tostring(fst_type)
2716     if fst_type_string != tfst.get().FstType():
2717       tfst.reset(fst.Convert(deref(tfst), fst_type_string))
2718       if tfst.get() == NULL:
2719         raise FstOpError("Conversion to {!r} failed.".format(fst_type))
2720   return _init_XFst(tfst.release())
2721
2722
2723 cdef _Fst _deserialize_Fst(fst_string, fst_type=None):
2724   cdef unique_ptr[fst.FstClass] ofst
2725   ofst.reset(fst.FstClass.ReadFromString(fst_string))
2726   if fst_type is not None:
2727     fst_type_string = tostring(fst_type)
2728     if fst_type_string != ofst.get().FstType():
2729       ofst.reset(fst.Convert(deref(ofst), fst_type_string))
2730       if ofst.get() == NULL:
2731         raise FstOpError("Conversion to {!r} failed.".format(fst_type))
2732   return _init_XFst(ofst.release())
2733
2734
2735 class Fst(object):
2736
2737    """
2738    Fst(arc_type="standard")
2739
2740    Constructs an empty FST.
2741
2742    Args:
2743      arc_type: A string indicating the arc type.
2744
2745    Raises:
2746      FstError: Unknown arc type.
2747
2748    Raises:
2749      FstOpError: operation failed.
2750    """
2751
2752    def __new__(cls, arc_type=b"standard"):
2753     return _create_Fst(arc_type)
2754
2755    @staticmethod
2756    def read(filename, fst_type=None):
2757      """
2758      read(filename, fst_type=None)
2759
2760      Reads an FST from a file.
2761
2762      Args:
2763        filename: The string location of the input file.
2764        fst_type: A string indicating the FST type to convert to; no conversion
2765          is performed if omitted or if the FST is already of the desired type.
2766
2767      Returns:
2768        An FST object.
2769
2770      Raises:
2771        FstIOError: Read failed.
2772        FstOpError: Read-time conversion failed.
2773      """
2774      return _read_Fst(filename, fst_type)
2775
2776    @staticmethod
2777    def read_from_string(fst_string, fst_type=None):
2778      """
2779      read_from_string(fst_string, fst_type=None)
2780
2781      Reads an FST from a serialized string.
2782
2783      Args:
2784        fst_string: The string containing the serialized FST.
2785        fst_type: A string indicating the FST type to convert to; no conversion
2786          is performed if omitted or if the FST is already of the desired type.
2787
2788      Returns:
2789        An FST object.
2790
2791      Raises:
2792        FstIOError: Read failed.
2793        FstOpError: Read-time conversion failed.
2794
2795      See also: `write_to_string`.
2796      """
2797      return _deserialize_Fst(fst_string, fst_type)
2798
2799
2800 ## FST constants.
2801
2802
2803 NO_LABEL = fst.kNoLabel
2804 NO_STATE_ID = fst.kNoStateId
2805 # TODO(kbg): Figure out how to access static class variables so I don't have
2806 # to do it this way.
2807 NO_SYMBOL = kNoSymbol
2808
2809
2810 ## FST properties.
2811
2812
2813 EXPANDED = fst.kExpanded
2814 MUTABLE = fst.kMutable
2815 ERROR = fst.kError
2816 ACCEPTOR = fst.kAcceptor
2817 NOT_ACCEPTOR = fst.kNotAcceptor
2818 I_DETERMINISTIC = fst.kIDeterministic
2819 NON_I_DETERMINISTIC = fst.kNonIDeterministic
2820 O_DETERMINISTIC = fst.kODeterministic
2821 NON_O_DETERMINISTIC = fst.kNonODeterministic
2822 EPSILONS = fst.kEpsilons
2823 NO_EPSILONS = fst.kNoEpsilons
2824 I_EPSILONS = fst.kIEpsilons
2825 NO_I_EPSILONS = fst.kNoIEpsilons
2826 O_EPSILONS = fst.kOEpsilons
2827 NO_O_EPSILONS = fst.kNoOEpsilons
2828 I_LABEL_SORTED = fst.kILabelSorted
2829 NOT_I_LABEL_SORTED = fst.kNotILabelSorted
2830 O_LABEL_SORTED = fst.kOLabelSorted
2831 NOT_O_LABEL_SORTED = fst.kNotOLabelSorted
2832 WEIGHTED = fst.kWeighted
2833 UNWEIGHTED = fst.kUnweighted
2834 CYCLIC = fst.kCyclic
2835 ACYCLIC = fst.kAcyclic
2836 INITIAL_CYCLIC = fst.kInitialCyclic
2837 INITIAL_ACYCLIC = fst.kInitialAcyclic
2838 TOP_SORTED = fst.kTopSorted
2839 NOT_TOP_SORTED = fst.kNotTopSorted
2840 ACCESSIBLE = fst.kAccessible
2841 NOT_ACCESSIBLE = fst.kNotAccessible
2842 COACCESSIBLE = fst.kCoAccessible
2843 NOT_COACCESSIBLE = fst.kNotCoAccessible
2844 STRING = fst.kString
2845 NOT_STRING = fst.kNotString
2846 WEIGHTED_CYCLES = fst.kWeightedCycles
2847 UNWEIGHTED_CYCLES = fst.kUnweightedCycles
2848 NULL_PROPERTIES = fst.kNullProperties
2849 COPY_PROPERTIES = fst.kCopyProperties
2850 INTRINSIC_PROPERTIES = fst.kIntrinsicProperties
2851 EXTRINSIC_PROPERTIES = fst.kExtrinsicProperties
2852 SET_START_PROPERTIES = fst.kSetStartProperties
2853 SET_FINAL_PROPERTIES = fst.kSetFinalProperties
2854 ADD_STATE_PROPERTIES = fst.kAddStateProperties
2855 ADD_ARC_PROPERTIES = fst.kAddArcProperties
2856 SET_ARC_PROPERTIES = fst.kSetArcProperties
2857 DELETE_STATE_PROPERTIES = fst.kDeleteStatesProperties
2858 DELETE_ARC_PROPERTIES = fst.kDeleteArcsProperties
2859 STATE_SORT_PROPERTIES = fst.kStateSortProperties
2860 ARC_SORT_PROPERTIES = fst.kArcSortProperties
2861 I_LABEL_INVARIANT_PROPERTIES = fst.kILabelInvariantProperties
2862 O_LABEL_INVARIANT_PROPERTIES = fst.kOLabelInvariantProperties
2863 WEIGHT_INVARIANT_PROPERTIES = fst.kWeightInvariantProperties
2864 ADD_SUPERFINAL_PROPERTIES = fst.kAddSuperFinalProperties
2865 RM_SUPERFINAL_PROPERTIES = fst.kRmSuperFinalProperties
2866 BINARY_PROPERTIES = fst.kBinaryProperties
2867 TRINARY_PROPERTIES = fst.kTrinaryProperties
2868 POS_TRINARY_PROPERTIES = fst.kPosTrinaryProperties
2869 NEG_TRINARY_PROPERTIES = fst.kNegTrinaryProperties
2870 FST_PROPERTIES = fst.kFstProperties
2871
2872
2873 ## Arc iterator properties.
2874
2875
2876 ARC_I_LABEL_VALUE = fst.kArcILabelValue
2877 ARC_O_LABEL_VALUE = fst.kArcOLabelValue
2878 ARC_WEIGHT_VALUE = fst.kArcWeightValue
2879 ARC_NEXT_STATE_VALUE = fst.kArcNextStateValue
2880 ARC_NO_CACHE = fst.kArcNoCache
2881 ARC_VALUE_FLAGS = fst.kArcValueFlags
2882 ARC_FLAGS = fst.kArcFlags
2883
2884
2885 ## EncodeMapper properties.
2886
2887
2888 ENCODE_LABELS = fst.kEncodeLabels
2889 ENCODE_WEIGHTS = fst.kEncodeWeights
2890 ENCODE_FLAGS = fst.kEncodeFlags
2891
2892
2893 ## Arc, ArcIterator, and MutableArcIterator.
2894
2895
2896 cdef class Arc(object):
2897
2898   """
2899   Arc(ilabel, olabel, weight, nextstate)
2900
2901   This class represents an arc while remaining agnostic about the underlying arc
2902   type.  Attributes of the arc can be accessed or mutated, and the arc can be
2903   copied.
2904
2905   Attributes:
2906     ilabel: The input label.
2907     olabel: The output label.
2908     weight: The arc weight.
2909     nextstate: The destination state for the arc.
2910   """
2911
2912   def __repr__(self):
2913     return "<Arc at 0x{:x}>".format(id(self))
2914
2915   def __init__(self, int64 ilabel, int64 olabel, weight, int64 nextstate):
2916     cdef fst.WeightClass wc = _get_WeightClass_or_One(b"tropical", weight)
2917     self._arc.reset(new fst.ArcClass(ilabel, olabel, wc, nextstate))
2918
2919   cpdef Arc copy(self):
2920     return Arc(self.ilabel, self.olabel, self.weight, self.nextstate)
2921
2922   property ilabel:
2923
2924     def __get__(self):
2925       return deref(self._arc).ilabel
2926
2927     def __set__(self, int64 value):
2928       deref(self._arc).ilabel = value
2929
2930   property olabel:
2931
2932     def __get__(self):
2933       return deref(self._arc).olabel
2934
2935     def __set__(self, int64 value):
2936       deref(self._arc).olabel = value
2937
2938   property weight:
2939
2940     def __get__(self):
2941       cdef Weight weight = Weight.__new__(Weight)
2942       weight._weight.reset(new fst.WeightClass(deref(self._arc).weight))
2943       return weight
2944
2945     def __set__(self, weight):
2946       deref(self._arc).weight = _get_WeightClass_or_One(b"tropical", weight)
2947
2948   property nextstate:
2949
2950     def __get__(self):
2951       return deref(self._arc).nextstate
2952
2953     def __set__(self, int64 value):
2954       deref(self._arc).nextstate = value
2955
2956
2957 cdef Arc _init_Arc(const fst.ArcClass &arc):
2958   cdef Weight weight = Weight.__new__(Weight)
2959   weight._weight.reset(new fst.WeightClass(arc.weight))
2960   return Arc(<int64> arc.ilabel, <int64> arc.olabel, weight,
2961              <int64> arc.nextstate)
2962
2963
2964 cdef class ArcIterator(object):
2965
2966   """
2967   ArcIterator(ifst, state)
2968
2969   This class is used for iterating over the arcs leaving some state of an FST.
2970   """
2971
2972   def __repr__(self):
2973     return "<ArcIterator at 0x{:x}>".format(id(self))
2974
2975   def __init__(self, _Fst ifst, int64 state):
2976     if not ifst._fst.get().ValidStateId(state):
2977       raise FstIndexError("State index out of range")
2978     # Makes copy of the shared_ptr, potentially extending the FST's lifetime.
2979     self._fst = ifst._fst
2980     self._aiter.reset(new fst.ArcIteratorClass(deref(self._fst), state))
2981
2982   # This just registers this class as a possible iterator.
2983   def __iter__(self):
2984     return self
2985
2986   # Magic method used to get a Pythonic API out of the C++ API.
2987   def __next__(self):
2988     if self.done():
2989       raise StopIteration
2990     result = self.value()
2991     self.next()
2992     return result
2993
2994   cpdef bool done(self):
2995     """
2996     done(self)
2997
2998     Indicates whether the iterator is exhausted or not.
2999
3000     Returns:
3001       True if the iterator is exhausted, False otherwise.
3002     """
3003     return self._aiter.get().Done()
3004
3005   cpdef uint32 flags(self):
3006     """
3007     flags(self)
3008
3009     Returns the current iterator behavioral flags.
3010
3011     Returns:
3012       The current iterator behavioral flags as an integer.
3013     """
3014     return self._aiter.get().Flags()
3015
3016   cpdef void next(self):
3017     """
3018     next(self)
3019
3020     Advances the iterator.
3021     """
3022     self._aiter.get().Next()
3023
3024   cpdef size_t position(self):
3025     """
3026     position(self)
3027
3028     Returns the position of the iterator.
3029
3030     Returns:
3031       The iterator's position, expressed as an integer.
3032     """
3033     return self._aiter.get().Position()
3034
3035   cpdef void reset(self):
3036     """
3037     reset(self)
3038
3039     Resets the iterator to the initial position.
3040     """
3041     self._aiter.get().Reset()
3042
3043   cpdef void seek(self, size_t a):
3044     """
3045     seek(self, a)
3046
3047     Advance the iterator to a new position.
3048
3049     Args:
3050       a: The position to seek to.
3051     """
3052     self._aiter.get().Seek(a)
3053
3054   cpdef void set_flags(self, uint32 flags, uint32 mask):
3055     """
3056     set_flags(self, flags, mask)
3057
3058     Sets the current iterator behavioral flags.
3059
3060     Args:
3061       flags: The properties to be set.
3062       mask: A mask to be applied to the `flags` argument before setting them.
3063     """
3064     self._aiter.get().SetFlags(flags, mask)
3065
3066   cpdef object value(self):
3067     """
3068     value(self)
3069
3070     Returns the current arc.
3071     """
3072     return _init_Arc(self._aiter.get().Value())
3073
3074
3075 cdef class MutableArcIterator(object):
3076
3077   """
3078   MutableArcIterator(ifst, state)
3079
3080   This class is used for iterating over the arcs leaving some state of an FST,
3081   also permitting mutation of the current arc.
3082   """
3083
3084   def __repr__(self):
3085     return "<MutableArcIterator at 0x{:x}>".format(id(self))
3086
3087   def __init__(self, _MutableFst ifst, int64 state):
3088     if not ifst._fst.get().ValidStateId(state):
3089       raise FstIndexError("State index out of range")
3090     # Makes copy of the shared_ptr, potentially extending the FST's lifetime.
3091     self._mfst = ifst._mfst
3092     self._aiter.reset(new fst.MutableArcIteratorClass(ifst._mfst.get(), state))
3093
3094   cpdef bool done(self):
3095     """
3096     done(self)
3097
3098     Indicates whether the iterator is exhausted or not.
3099
3100     Returns:
3101       True if the iterator is exhausted, False otherwise.
3102     """
3103     return self._aiter.get().Done()
3104
3105   cpdef uint32 flags(self):
3106     """
3107     flags(self)
3108
3109     Returns the current iterator behavioral flags.
3110
3111     Returns:
3112       The current iterator behavioral flags as an integer.
3113     """
3114     return self._aiter.get().Flags()
3115
3116   cpdef void next(self):
3117     """
3118     next(self)
3119
3120     Advances the iterator.
3121     """
3122     self._aiter.get().Next()
3123
3124   cpdef size_t position(self):
3125     """
3126     position(self)
3127
3128     Returns the position of the iterator.
3129
3130     Returns:
3131       The iterator's position, expressed as an integer.
3132     """
3133     return self._aiter.get().Position()
3134
3135   cpdef void reset(self):
3136     """
3137     reset(self)
3138
3139     Resets the iterator to the initial position.
3140     """
3141     self._aiter.get().Reset()
3142
3143   cpdef void seek(self, size_t a):
3144     """
3145     seek(self, a)
3146
3147     Advance the iterator to a new position.
3148
3149     Args:
3150       a: The position to seek to.
3151     """
3152     self._aiter.get().Seek(a)
3153
3154   cpdef void set_flags(self, uint32 flags, uint32 mask):
3155     """
3156     set_flags(self, flags, mask)
3157
3158     Sets the current iterator behavioral flags.
3159
3160     Args:
3161       flags: The properties to be set.
3162       mask: A mask to be applied to the `flags` argument before setting them.
3163     """
3164     self._aiter.get().SetFlags(flags, mask)
3165
3166   cpdef void set_value(self, Arc arc):
3167     """
3168     set_value(self, arc)
3169
3170     Replace the current arc with a new arc.
3171
3172     Args:
3173       arc: The arc to replace the current arc with.
3174     """
3175     self._aiter.get().SetValue(deref(arc._arc))
3176
3177   cpdef object value(self):
3178     """
3179     value(self)
3180
3181     Returns the current arc.
3182     """
3183     return _init_Arc(self._aiter.get().Value())
3184
3185
3186 ## StateIterator.
3187
3188
3189 cdef class StateIterator(object):
3190
3191   """
3192   StateIterator(ifst)
3193
3194   This class is used for iterating over the states in an FST.
3195   """
3196
3197   def __repr__(self):
3198     return "<StateIterator at 0x{:x}>".format(id(self))
3199
3200   def __init__(self, _Fst ifst):
3201     # Makes copy of the shared_ptr, potentially extending the FST's lifetime.
3202     self._fst = ifst._fst
3203     self._siter.reset(new fst.StateIteratorClass(deref(self._fst)))
3204
3205   # This just registers this class as a possible iterator.
3206   def __iter__(self):
3207     return self
3208
3209   # Magic method used to get a Pythonic API out of the C++ API.
3210   def __next__(self):
3211     if self.done():
3212       raise StopIteration
3213     cdef int64 result = self.value()
3214     self.next()
3215     return result
3216
3217   cpdef bool done(self):
3218     """
3219     done(self)
3220
3221     Indicates whether the iterator is exhausted or not.
3222
3223     Returns:
3224       True if the iterator is exhausted, False otherwise.
3225     """
3226     return self._siter.get().Done()
3227
3228   cpdef void next(self):
3229     """
3230     next(self)
3231
3232     Advances the iterator.
3233     """
3234     self._siter.get().Next()
3235
3236   cpdef void reset(self):
3237     """
3238     reset(self)
3239
3240     Resets the iterator to the initial position.
3241     """
3242     self._siter.get().Reset()
3243
3244   cpdef int64 value(self):
3245     """
3246     value(self)
3247
3248     Returns the current state index.
3249     """
3250     return self._siter.get().Value()
3251
3252
3253 ## FST operations.
3254
3255
3256 cdef _Fst _map(_Fst ifst,
3257                float delta=fst.kDelta,
3258                map_type=b"identity",
3259                weight=None):
3260   cdef fst.MapType map_type_enum
3261   if not fst.GetMapType(tostring(map_type), addr(map_type_enum)):
3262     raise FstArgError("Unknown map type: {!r}".format(map_type))
3263   cdef fst.WeightClass wc = (_get_WeightClass_or_One(ifst.weight_type(),
3264       weight) if map_type_enum == fst.TIMES_MAPPER else
3265       _get_WeightClass_or_Zero(ifst.weight_type(), weight))
3266   return _init_XFst(fst.Map(deref(ifst._fst), map_type_enum, delta, wc))
3267
3268
3269 cpdef _Fst arcmap(_Fst ifst,
3270                   float delta=fst.kDelta,
3271                   map_type=b"identity",
3272                   weight=None):
3273   """
3274   arcmap(ifst, delta=0.0009765625, map_type="identity", weight=None)
3275
3276   Constructively applies a transform to all arcs and final states.
3277
3278   This operation transforms each arc and final state in the input FST using
3279   one of the following:
3280
3281     * identity: maps to self.
3282     * input_epsilon: replaces all input labels with epsilon.
3283     * invert: reciprocates all non-Zero weights.
3284     * output_epsilon: replaces all output labels with epsilon.
3285     * plus: adds a constant to all weights.
3286     * quantize: quantizes weights.
3287     * rmweight: replaces all non-Zero weights with 1.
3288     * superfinal: redirects final states to a new superfinal state.
3289     * times: right-multiplies a constant to all weights.
3290     * to_log: converts weights to the log semiring.
3291     * to_log64: converts weights to the log64 semiring.
3292     * to_standard: converts weights to the tropical ("standard") semiring.
3293
3294   Args:
3295     ifst: The input FST.
3296     delta: Comparison/quantization delta (ignored unless `map_type` is
3297         `quantize`).
3298     map_type: A string matching a known mapping operation (see above).
3299     weight: A Weight or weight string passed to the arc-mapper; ignored unless
3300         `map_type` is `plus` (in which case it defaults to semiring Zero) or
3301         `times` (in which case it defaults to semiring One).
3302
3303   Returns:
3304     An FST with arcs and final states remapped.
3305
3306   Raises:
3307     FstArgError: Unknown map type.
3308
3309   See also: `statemap`.
3310   """
3311   return _map(ifst, delta, map_type, weight)
3312
3313
3314 cpdef _MutableFst compose(_Fst ifst1,
3315                           _Fst ifst2,
3316                           compose_filter=b"auto",
3317                           bool connect=True):
3318   """
3319   compose(ifst1, ifst2, compose_filter="auto", connect=True)
3320
3321   Constructively composes two FSTs.
3322
3323   This operation computes the composition of two FSTs. If A transduces string
3324   x to y with weight a and B transduces y to z with weight b, then their
3325   composition transduces string x to z with weight a \otimes b. The output
3326   labels of the first transducer or the input labels of the second transducer
3327   must be sorted (or otherwise support appropriate matchers).
3328
3329   Args:
3330     ifst1: The first input FST.
3331     ifst2: The second input FST.
3332     compose_filter: A string matching a known composition filter; one of:
3333         "alt_sequence", "auto", "match", "null", "sequence", "trivial".
3334     connect: Should output be trimmed?
3335
3336   Returns:
3337     An FST.
3338
3339   See also: `arcsort`.
3340   """
3341   cdef unique_ptr[fst.VectorFstClass] tfst
3342   tfst.reset(new fst.VectorFstClass(ifst1.arc_type()))
3343   cdef unique_ptr[fst.ComposeOptions] opts
3344   opts.reset(new fst.ComposeOptions(connect,
3345       _get_compose_filter(tostring(compose_filter))))
3346   fst.Compose(deref(ifst1._fst), deref(ifst2._fst), tfst.get(), deref(opts))
3347   return _init_MutableFst(tfst.release())
3348
3349
3350 cpdef _Fst convert(_Fst ifst, fst_type=None):
3351   """
3352   convert(ifst, fst_type=None)
3353
3354   Constructively converts an FST to a new internal representation.
3355
3356   Args:
3357     ifst: The input FST.
3358     fst_type: A string indicating the FST type to convert to, or None if
3359         no conversion is desired.
3360
3361   Returns:
3362     An equivalent Fst converted to the desired FST type.
3363
3364   Raises:
3365     FstOpError: Conversion failed.
3366   """
3367   cdef string fst_type_string = b"" if fst_type is None else tostring(fst_type)
3368   cdef unique_ptr[fst.FstClass] tfst
3369   tfst.reset(fst.Convert(deref(ifst._fst), fst_type_string))
3370   # Script-land Convert returns the null pointer to signal failure.
3371   if tfst.get() == NULL:
3372     raise FstOpError("Conversion to {!r} failed".format(fst_type))
3373   return _init_XFst(tfst.release())
3374
3375
3376 cpdef _MutableFst determinize(_Fst ifst,
3377                               float delta=fst.kDelta,
3378                               det_type=b"functional",
3379                               int64 nstate=fst.kNoStateId,
3380                               int64 subsequential_label=0,
3381                               weight=None,
3382                               bool increment_subsequential_label=False):
3383   """
3384   determinize(ifst, delta=0.0009765625, det_type="functional",
3385               nstate=NO_STATE_ID, subsequential_label=0, weight=None,
3386               incremental_subsequential_label=False)
3387
3388   Constructively determinizes a weighted FST.
3389
3390   This operations creates an equivalent FST that has the property that no
3391   state has two transitions with the same input label. For this algorithm,
3392   epsilon transitions are treated as regular symbols (cf. `rmepsilon`).
3393
3394   Args:
3395     ifst: The input FST.
3396     delta: Comparison/quantization delta.
3397     det_type: Type of determinization; one of: "functional" (input transducer is
3398         functional), "nonfunctional" (input transducer is not functional) and
3399         disambiguate" (input transducer is not functional but only keep the min
3400         of ambiguous outputs).
3401     nstate: State number threshold.
3402     subsequential_label: Input label of arc corresponding to residual final
3403         output when producing a subsequential transducer.
3404     weight: A Weight or weight string indicating the desired weight threshold
3405         below which paths are pruned; if omitted, no paths are pruned.
3406     increment_subsequential_label: Increment subsequential when creating
3407         several arcs for the residual final output at a given state.
3408
3409   Returns:
3410     An equivalent deterministic FST.
3411
3412   Raises:
3413     FstArgError: Unknown determinization type.
3414
3415   See also: `disambiguate`, `rmepsilon`.
3416   """
3417   cdef unique_ptr[fst.VectorFstClass] tfst
3418   tfst.reset(new fst.VectorFstClass(ifst.arc_type()))
3419   # Threshold is set to semiring Zero (no pruning) if weight unspecified.
3420   cdef fst.WeightClass wc = _get_WeightClass_or_Zero(ifst.weight_type(),
3421                                                      weight)
3422   cdef fst.DeterminizeType determinize_type_enum
3423   if not fst.GetDeterminizeType(tostring(det_type),
3424                                 addr(determinize_type_enum)):
3425     raise FstArgError("Unknown determinization type: {!r}".format(det_type))
3426   cdef unique_ptr[fst.DeterminizeOptions] opts
3427   opts.reset(new fst.DeterminizeOptions(delta, wc, nstate, subsequential_label,
3428                                         determinize_type_enum,
3429                                         increment_subsequential_label))
3430   fst.Determinize(deref(ifst._fst), tfst.get(), deref(opts))
3431   return _init_MutableFst(tfst.release())
3432
3433
3434 cpdef _MutableFst difference(_Fst ifst1,
3435                              _Fst ifst2,
3436                              compose_filter=b"auto",
3437                              bool connect=True):
3438   """
3439   difference(ifst1, ifst2, compose_filter="auto", connect=True)
3440
3441   Constructively computes the difference of two FSTs.
3442
3443   This operation computes the difference between two FSAs. Only strings that are
3444   in the first automaton but not in second are retained in the result. The first
3445   argument must be an acceptor; the second argument must be an unweighted,
3446   epsilon-free, deterministic acceptor. The output labels of the first
3447   transducer or the input labels of the second transducer must be sorted (or
3448   otherwise support appropriate matchers).
3449
3450   Args:
3451     ifst1: The first input FST.
3452     ifst2: The second input FST.
3453     compose_filter: A string matching a known composition filter; one of:
3454         "alt_sequence", "auto", "match", "null", "sequence", "trivial".
3455     connect: Should the output FST be trimmed?
3456
3457   Returns:
3458     An FST representing the difference of the FSTs.
3459   """
3460   cdef unique_ptr[fst.VectorFstClass] tfst
3461   tfst.reset(new fst.VectorFstClass(ifst1.arc_type()))
3462   cdef unique_ptr[fst.ComposeOptions] opts
3463   opts.reset(new fst.ComposeOptions(connect, _get_compose_filter(
3464       tostring(compose_filter))))
3465   fst.Difference(deref(ifst1._fst), deref(ifst2._fst), tfst.get(), deref(opts))
3466   return _init_MutableFst(tfst.release())
3467
3468
3469 cpdef _MutableFst disambiguate(_Fst ifst,
3470                                float delta=fst.kDelta,
3471                                int64 nstate=fst.kNoStateId,
3472                                int64 subsequential_label=0,
3473                                weight=None):
3474   """
3475   disambiguate(ifst, delta=0.0009765625, nstate=NO_STATE_ID,
3476                subsequential_label=0, weight=None):
3477
3478   Constructively disambiguates a weighted transducer.
3479
3480   This operation disambiguates a weighted transducer. The result will be an
3481   equivalent FST that has the property that no two successful paths have the
3482   same input labeling. For this algorithm, epsilon transitions are treated as
3483   regular symbols (cf. `rmepsilon`).
3484
3485   Args:
3486     ifst: The input FST.
3487     delta: Comparison/quantization delta.
3488     nstate: State number threshold.
3489     subsequential_label: Input label of arc corresponding to residual final
3490         output when producing a subsequential transducer.
3491     weight: A Weight or weight string indicating the desired weight threshold
3492         below which paths are pruned; if omitted, no paths are pruned.
3493
3494   Returns:
3495     An equivalent disambiguated FST.
3496
3497   See also: `determinize`, `rmepsilon`.
3498   """
3499   cdef unique_ptr[fst.VectorFstClass] tfst
3500   tfst.reset(new fst.VectorFstClass(ifst.arc_type()))
3501   # Threshold is set to semiring Zero (no pruning) if no weight is specified.
3502   cdef fst.WeightClass wc = _get_WeightClass_or_Zero(ifst.weight_type(),
3503                                                      weight)
3504   cdef unique_ptr[fst.DisambiguateOptions] opts
3505   opts.reset(new fst.DisambiguateOptions(delta, wc, nstate,
3506                                          subsequential_label))
3507   fst.Disambiguate(deref(ifst._fst), tfst.get(), deref(opts))
3508   return _init_MutableFst(tfst.release())
3509
3510
3511 cpdef _MutableFst epsnormalize(_Fst ifst, bool eps_norm_output=False):
3512   """
3513   epsnormalize(ifst, eps_norm_output=False)
3514
3515   Constructively epsilon-normalizes an FST.
3516
3517   This operation creates an equivalent FST that is epsilon-normalized. An
3518   acceptor is epsilon-normalized if it it is epsilon-removed (cf. `rmepsilon`).
3519   A transducer is input epsilon-normalized if, in addition, along any path, all
3520   arcs with epsilon input labels follow all arcs with non-epsilon input labels.
3521   Output epsilon-normalized is defined similarly. The input FST must be
3522   functional.
3523
3524   Args:
3525     ifst: The input FST.
3526     eps_norm_output: Should the FST be output epsilon-normalized?
3527
3528   Returns:
3529     An equivalent epsilon-normalized FST.
3530
3531   See also: `rmepsilon`.
3532   """
3533   cdef unique_ptr[fst.VectorFstClass] tfst
3534   tfst.reset(new fst.VectorFstClass(ifst.arc_type()))
3535   fst.EpsNormalize(deref(ifst._fst), tfst.get(), fst.EPS_NORM_OUTPUT if
3536                                                  eps_norm_output else
3537                                                  fst.EPS_NORM_INPUT)
3538   return _init_MutableFst(tfst.release())
3539
3540
3541 cpdef bool equal(_Fst ifst1, _Fst ifst2, float delta=fst.kDelta):
3542   """
3543   equal(ifst1, ifst2, delta=0.0009765625)
3544
3545   Are two FSTs equal?
3546
3547   This function tests whether two FSTs have the same states with the same
3548   numbering and the same transitions with the same labels and weights in the
3549   same order.
3550
3551   Args:
3552     ifst1: The first input FST.
3553     ifst2: The second input FST.
3554     delta: Comparison/quantization delta.
3555
3556   Returns:
3557     True if the FSTs satisfy the above condition, else False.
3558
3559   See also: `equivalent`, `isomorphic`, `randequivalent`.
3560   """
3561   return fst.Equal(deref(ifst1._fst), deref(ifst2._fst), delta)
3562
3563
3564 cpdef bool equivalent(_Fst ifst1, _Fst ifst2, float delta=fst.kDelta) except *:
3565   """
3566   equivalent(ifst1, ifst2, delta=0.0009765625)
3567
3568   Are the two acceptors equivalent?
3569
3570   This operation tests whether two epsilon-free deterministic weighted
3571   acceptors are equivalent, that is if they accept the same strings with the
3572   same weights.
3573
3574   Args:
3575     ifst1: The first input FST.
3576     ifst2: The second input FST.
3577     delta: Comparison/quantization delta.
3578
3579   Returns:
3580     True if the FSTs satisfy the above condition, else False.
3581
3582   See also: `equal`, `isomorphic`, `randequivalent`.
3583   """
3584   return fst.Equivalent(deref(ifst1._fst), deref(ifst2._fst), delta)
3585
3586
3587 cpdef _MutableFst intersect(_Fst ifst1,
3588                             _Fst ifst2,
3589                             compose_filter=b"auto",
3590                             bool connect=True):
3591   """
3592   intersect(ifst1, ifst2, compose_filter="auto", connect=True)
3593
3594   Constructively intersects two FSTs.
3595
3596   This operation computes the intersection (Hadamard product) of two FSTs.
3597   Only strings that are in both automata are retained in the result. The two
3598   arguments must be acceptors. One of the arguments must be label-sorted (or
3599   otherwise support appropriate matchers).
3600
3601   Args:
3602     ifst1: The first input FST.
3603     ifst2: The second input FST.
3604     compose_filter: A string matching a known composition filter; one of:
3605         "alt_sequence", "auto", "match", "null", "sequence", "trivial".
3606     connect: Should output be trimmed?
3607
3608   Returns:
3609     An intersected FST.
3610   """
3611   cdef unique_ptr[fst.VectorFstClass] tfst
3612   tfst.reset(new fst.VectorFstClass(ifst1.arc_type()))
3613   cdef unique_ptr[fst.ComposeOptions] opts
3614   opts.reset(new fst.ComposeOptions(connect,
3615         _get_compose_filter(tostring(compose_filter))))
3616   fst.Intersect(deref(ifst1._fst), deref(ifst2._fst), tfst.get(), deref(opts))
3617   return _init_MutableFst(tfst.release())
3618
3619
3620 cpdef bool isomorphic(_Fst ifst1, _Fst ifst2, float delta=fst.kDelta):
3621   """
3622   isomorphic(ifst1, ifst2, delta=0.0009765625)
3623
3624   Are the two acceptors isomorphic?
3625
3626   This operation determines if two transducers with a certain required
3627   determinism have the same states, irrespective of numbering, and the same
3628   transitions with the same labels and weights, irrespective of ordering. In
3629   other words, FSTs A, B are isomorphic if and only if the states of A can be
3630   renumbered and the transitions leaving each state reordered so the two are
3631   equal (according to the definition given in `equal`).
3632
3633   Args:
3634     ifst1: The first input FST.
3635     ifst2: The second input FST.
3636     delta: Comparison/quantization delta.
3637
3638   Returns:
3639     True if the two transducers satisfy the above condition, else False.
3640
3641   See also: `equal`, `equivalent`, `randequivalent`.
3642   """
3643   return fst.Isomorphic(deref(ifst1._fst), deref(ifst2._fst), delta)
3644
3645
3646 cpdef _MutableFst prune(_Fst ifst,
3647                         float delta=fst.kDelta,
3648                         int64 nstate=fst.kNoStateId,
3649                         weight=None):
3650   """
3651   prune(ifst, delta=0.0009765625, nstate=NO_STATE_ID, weight=None)
3652
3653   Constructively removes paths with weights below a certain threshold.
3654
3655   This operation deletes states and arcs in the input FST that do not belong
3656   to a successful path whose weight is no more (w.r.t the natural semiring
3657   order) than the threshold t \otimes-times the weight of the shortest path in
3658   the input FST. Weights must be commutative and have the path property.
3659
3660   Args:
3661     ifst: The input FST.
3662     delta: Comparison/quantization delta.
3663     nstate: State number threshold.
3664     weight: A Weight or weight string indicating the desired weight threshold
3665         below which paths are pruned; if omitted, no paths are pruned.
3666
3667   Returns:
3668     A pruned FST.
3669
3670   See also: The destructive variant.
3671   """
3672   cdef unique_ptr[fst.VectorFstClass] tfst
3673   tfst.reset(new fst.VectorFstClass(ifst.arc_type()))
3674   cdef fst.WeightClass wc = _get_WeightClass_or_Zero(ifst.weight_type(), weight)
3675   fst.Prune(deref(ifst._fst), tfst.get(), wc, nstate, delta)
3676   return _init_MutableFst(tfst.release())
3677
3678
3679 cpdef _MutableFst push(_Fst ifst,
3680                        float delta=fst.kDelta,
3681                        bool push_weights=False,
3682                        bool push_labels=False,
3683                        bool remove_common_affix=False,
3684                        bool remove_total_weight=False,
3685                        bool to_final=False):
3686   """
3687   push(ifst, delta=0.0009765625, push_weights=False, push_labels=False,
3688        remove_common_affix=False, remove_total_weight=False, to_final=False)
3689
3690   Constructively pushes weights/labels towards initial or final states.
3691
3692   This operation produces an equivalent transducer by pushing the weights
3693   and/or the labels towards the initial state or toward the final states.
3694
3695   When pushing weights towards the initial state, the sum of the weight of the
3696   outgoing transitions and final weight at any non-initial state is equal to 1
3697   in the resulting machine. When pushing weights towards the final states, the
3698   sum of the weight of the incoming transitions at any state is equal to 1.
3699   Weights need to be left distributive when pushing towards the initial state
3700   and right distributive when pushing towards the final states.
3701
3702   Pushing labels towards the initial state consists in minimizing at every
3703   state the length of the longest common prefix of the output labels of the
3704   outgoing paths. Pushing labels towards the final states consists in
3705   minimizing at every state the length of the longest common suffix of the
3706   output labels of the incoming paths.
3707
3708   Args:
3709     ifst: The input FST.
3710     delta: Comparison/quantization delta.
3711     push_weights: Should weights be pushed?
3712     push_labels: Should labels be pushed?
3713     remove_common_affix: If pushing labels, should common prefix/suffix be
3714         removed?
3715     remove_total_weight: If pushing weights, should total weight be removed?
3716     to_final: Push towards final states?
3717
3718   Returns:
3719     An equivalent pushed FST.
3720
3721   See also: The destructive variant.
3722   """
3723   # This is copied, almost verbatim, from ./fstpush.cc.
3724   cdef unique_ptr[fst.VectorFstClass] tfst
3725   tfst.reset(new fst.VectorFstClass(ifst.arc_type()))
3726   cdef uint32 flags = fst.GetPushFlags(push_weights, push_labels,
3727                                        remove_common_affix, remove_total_weight)
3728   fst.Push(deref(ifst._fst), tfst.get(), flags, fst.GetReweightType(to_final),
3729            delta)
3730   return _init_MutableFst(tfst.release())
3731
3732
3733 cpdef bool randequivalent(_Fst ifst1,
3734                           _Fst ifst2,
3735                           int32 npath=1,
3736                           float delta=fst.kDelta,
3737                           time_t seed=0,
3738                           select=b"uniform",
3739                           int32 max_length=INT32_MAX) except *:
3740   """
3741   randequivalent(ifst1, ifst2, npath=1, delta=0.0009765625, seed=0,
3742                  select="uniform", max_length=2147483647)
3743
3744   Are two acceptors stochastically equivalent?
3745
3746   This operation tests whether two FSTs are equivalent by randomly generating
3747   paths alternatively in each of the two FSTs. For each randomly generated path,
3748   the algorithm computes for each of the two FSTs the sum of the weights of all
3749   the successful paths sharing the same input and output labels as the randomly
3750   generated path and checks that these two values are within `delta`.
3751
3752   Args:
3753     ifst1: The first input FST.
3754     ifst2: The second input FST.
3755     npath: The number of random paths to generate.
3756     delta: Comparison/quantization delta.
3757     seed: An optional seed value for random path generation; if zero, the
3758         current time and process ID is used.
3759     select: A string matching a known random arc selection type; one of:
3760         "uniform", "log_prob", "fast_log_prob".
3761     max_length: The maximum length of each random path.
3762
3763   Returns:
3764     True if the two transducers satisfy the above condition, else False.
3765
3766   See also: `equal`, `equivalent`, `isomorphic`, `randgen`.
3767   """
3768   cdef fst.RandArcSelection ras = _get_rand_arc_selection(tostring(select))
3769   cdef unique_ptr[fst.RandGenOptions[fst.RandArcSelection]] opts
3770   # The three trailing options will be ignored by RandEquivalent.
3771   opts.reset(new fst.RandGenOptions[fst.RandArcSelection](ras, max_length,
3772                                                           1, False, False))
3773   if seed == 0:
3774     seed = time(NULL) + getpid()
3775   return fst.RandEquivalent(deref(ifst1._fst), deref(ifst2._fst), npath, delta,
3776                            seed, deref(opts))
3777
3778
3779 cpdef _MutableFst randgen(_Fst ifst,
3780                           int32 npath=1,
3781                           time_t seed=0,
3782                           select=b"uniform",
3783                           int32 max_length=INT32_MAX,
3784                           bool weighted=False,
3785                           bool remove_total_weight=False):
3786   """
3787   randgen(ifst, npath=1, seed=0, select="uniform", max_length=2147483647,
3788           weight=False, remove_total_weight=False)
3789
3790   Randomly generate successful paths in an FST.
3791
3792   This operation randomly generates a set of successful paths in the input FST.
3793   This relies on a mechanism for selecting arcs, specified using the `select`
3794   argument. The default selector, "uniform", randomly selects a transition
3795   using a uniform distribution. The "log_prob" selector randomly selects a
3796   transition w.r.t. the weights treated as negative log probabilities after
3797   normalizing for the total weight leaving the state. In all cases, finality is
3798   treated as a transition to a super-final state.
3799
3800   Args:
3801     ifst: The input FST.
3802     npath: The number of random paths to generate.
3803     seed: An optional seed value for random path generation; if zero, the
3804         current time and process ID is used.
3805     select: A string matching a known random arc selection type; one of:
3806         "uniform", "log_prob", "fast_log_prob".
3807     max_length: The maximum length of each random path.
3808     weighted: Should the output be weighted by path count?
3809     remove_total_weight: Should the total weight be removed (ignored when
3810         `weighted` is False)?
3811
3812   Returns:
3813     An FST containing one or more random paths.
3814
3815   See also: `randequivalent`.
3816   """
3817   cdef fst.RandArcSelection ras = _get_rand_arc_selection(tostring(select))
3818   cdef unique_ptr[fst.RandGenOptions[fst.RandArcSelection]] opts
3819   opts.reset(new fst.RandGenOptions[fst.RandArcSelection](ras, max_length,
3820                                                           npath, weighted,
3821                                                           remove_total_weight))
3822   cdef unique_ptr[fst.VectorFstClass] tfst
3823   tfst.reset(new fst.VectorFstClass(ifst.arc_type()))
3824   if seed == 0:
3825     seed = time(NULL) + getpid()
3826   fst.RandGen(deref(ifst._fst), tfst.get(), seed, deref(opts))
3827   return _init_MutableFst(tfst.release())
3828
3829
3830 cpdef _MutableFst replace(pairs,
3831                           call_arc_labeling=b"input",
3832                           return_arc_labeling=b"neither",
3833                           bool epsilon_on_replace=False,
3834                           int64 return_label=0):
3835   """
3836   replace(pairs, call_arc_labeling="input", return_arc_labeling="neither",
3837           epsilon_on_replace=False, return_label=0)
3838
3839   Recursively replaces arcs in the FST with other FST(s).
3840
3841   This operation performs the dynamic replacement of arcs in one FST with
3842   another FST, allowing the definition of FSTs analogous to RTNs. It takes as
3843   input a set of pairs of a set of pairs formed by a non-terminal label and
3844   its corresponding FST, and a label identifying the root FST in that set.
3845   The resulting FST is obtained by taking the root FST and recursively replacing
3846   each arc having a nonterminal as output label by its corresponding FST. More
3847   precisely, an arc from state s to state d with (nonterminal) output label n in
3848   this FST is replaced by redirecting this "call" arc to the initial state of a
3849   copy F of the FST for n, and adding "return" arcs from each final state of F
3850   to d. Optional arguments control how the call and return arcs are labeled; by
3851   default, the only non-epsilon label is placed on the call arc.
3852
3853   Args:
3854
3855     pairs: An iterable of (nonterminal label, FST) pairs, where the former is an
3856         unsigned integer and the latter is an Fst instance.
3857     call_arc_labeling: A string indicating which call arc labels should be
3858         non-epsilon. One of: "input" (default), "output", "both", "neither".
3859         This value is set to "neither" if epsilon_on_replace is True.
3860     return_arc_labeling: A string indicating which return arc labels should be
3861         non-epsilon. One of: "input", "output", "both", "neither" (default).
3862         This value is set to "neither" if epsilon_on_replace is True.
3863     epsilon_on_replace: Should call and return arcs be epsilon arcs? If True,
3864         this effectively overrides call_arc_labeling and return_arc_labeling,
3865         setting both to "neither".
3866     return_label: The integer label for return arcs.
3867
3868   Returns:
3869     An FST resulting from expanding the input RTN.
3870   """
3871   cdef vector[fst.LabelFstClassPair] _pairs
3872   cdef int64 root_label
3873   cdef int64 label
3874   cdef _Fst ifst
3875   it = iter(pairs)
3876   (root_label, ifst) = next(it)
3877   _pairs.push_back(fst.LabelFstClassPair(root_label, ifst._fst.get()))
3878   cdef unique_ptr[fst.VectorFstClass] tfst
3879   tfst.reset(new fst.VectorFstClass(ifst.arc_type()))
3880   for (label, ifst) in it:
3881     _pairs.push_back(fst.LabelFstClassPair(label, ifst._fst.get()))
3882   cdef fst.ReplaceLabelType cal = _get_replace_label_type(
3883       tostring(call_arc_labeling), epsilon_on_replace)
3884   cdef fst.ReplaceLabelType ral = _get_replace_label_type(
3885       tostring(return_arc_labeling), epsilon_on_replace)
3886   cdef unique_ptr[fst.ReplaceOptions] opts
3887   opts.reset(new fst.ReplaceOptions(root_label, cal, ral, return_label))
3888   fst.Replace(_pairs, tfst.get(), deref(opts))
3889   return _init_MutableFst(tfst.release())
3890
3891
3892 cpdef _MutableFst reverse(_Fst ifst, bool require_superinitial=True):
3893   """
3894   reverse(ifst, require_superinitial=True)
3895
3896   Constructively reverses an FST's transduction.
3897
3898   This operation reverses an FST. If A transduces string x to y with weight a,
3899   then the reverse of A transduces the reverse of x to the reverse of y with
3900   weight a.Reverse(). (Typically, a = a.Reverse() and Arc = RevArc, e.g.,
3901   TropicalWeight and LogWeight.) In general, e.g., when the weights only form a
3902   left or right semiring, the output arc type must match the input arc type.
3903
3904   Args:
3905     ifst: The input FST.
3906     require_superinitial: Should a superinitial state be created?
3907
3908   Returns:
3909     A reversed FST.
3910   """
3911   cdef unique_ptr[fst.VectorFstClass] tfst
3912   tfst.reset(new fst.VectorFstClass(ifst.arc_type()))
3913   fst.Reverse(deref(ifst._fst), tfst.get(), require_superinitial)
3914   return _init_MutableFst(tfst.release())
3915
3916
3917 # Pure C++ helper for shortestdistance.
3918
3919
3920 cdef vector[fst.WeightClass] *_shortestdistance(_Fst ifst,
3921     float delta=fst.kDelta, int64 nstate=fst.kNoStateId, queue_type=b"auto",
3922     bool reverse=False) except *:
3923   cdef unique_ptr[vector[fst.WeightClass]] distance
3924   distance.reset(new vector[fst.WeightClass]())
3925   # For scoping reasons, these have to be declared here even though they may
3926   # not be used in all cases.
3927   cdef unique_ptr[fst.ShortestDistanceOptions] opts
3928   if reverse:
3929     # Only the simpler signature supports shortest distance to final states;
3930     # `nstate` and `queue_type` arguments are ignored.
3931     fst.ShortestDistance(deref(ifst._fst), distance.get(), True, delta)
3932   else:
3933     opts.reset(new fst.ShortestDistanceOptions(
3934         _get_queue_type(tostring(queue_type)), fst.ANY_ARC_FILTER, nstate,
3935         delta))
3936     fst.ShortestDistance(deref(ifst._fst), distance.get(), deref(opts))
3937   return distance.release()
3938
3939
3940 def shortestdistance(_Fst ifst,
3941                      float delta=fst.kDelta,
3942                      int64 nstate=fst.kNoStateId,
3943                      queue_type=b"auto",
3944                      bool reverse=False):
3945   """
3946   shortestdistance(ifst, delta=0.0009765625, nstate=NO_STATE_ID,
3947                    queue_type="auto", reverse=False)
3948
3949   Compute the shortest distance from the initial or final state.
3950
3951   This operation computes the shortest distance from the initial state (when
3952   `reverse` is False) or from every state to the final state (when `reverse` is
3953   True). The shortest distance from p to q is the \otimes-sum of the weights of
3954   all the paths between p and q. The weights must be right (if `reverse` is
3955   False) or left (if `reverse` is True) distributive, and k-closed (i.e., 1
3956   \otimes x \otimes x^2 \otimes ... \otimes x^{k + 1} = 1 \otimes x \otimes x^2
3957   \otimes ... \otimes x^k; e.g., TropicalWeight).
3958
3959   Args:
3960     ifst: The input FST.
3961     delta: Comparison/quantization delta.
3962     nstate: State number threshold (ignored if `reverse` is True).
3963     queue_type: A string matching a known queue type; one of: "auto", "fifo",
3964         "lifo", "shortest", "state", "top" (ignored if `reverse` is True).
3965     reverse: Should the reverse distance (from each state to the final state)
3966         be computed?
3967
3968   Returns:
3969     A list of Weight objects representing the shortest distance for each state.
3970   """
3971   cdef unique_ptr[vector[fst.WeightClass]] distance
3972   distance.reset(_shortestdistance(ifst, delta, nstate, queue_type, reverse))
3973   cdef string weight_type = ifst.weight_type()
3974   return [Weight(weight_type, weight.ToString()) for weight in deref(distance)]
3975
3976
3977 cpdef _MutableFst shortestpath(_Fst ifst,
3978                                float delta=fst.kDelta,
3979                                int32 nshortest=1,
3980                                int64 nstate=fst.kNoStateId,
3981                                queue_type=b"auto",
3982                                bool unique=False,
3983                                weight=None):
3984   """
3985   shortestpath(ifst, delta=0.0009765625, nshortest=1, nstate=NO_STATE_ID,
3986                queue_type="auto", unique=False, weight=None)
3987
3988   Construct an FST containing the shortest path(s) in the input FST.
3989
3990   This operation produces an FST containing the n-shortest paths in the input
3991   FST. The n-shortest paths are the n-lowest weight paths w.r.t. the natural
3992   semiring order. The single path that can be read from the ith of at most n
3993   transitions leaving the initial state of the resulting FST is the ith
3994   shortest path. The weights need to be right distributive and have the path
3995   property. They also need to be left distributive as well for n-shortest with
3996   n > 1 (e.g., TropicalWeight).
3997
3998   Args:
3999     ifst: The input FST.
4000     delta: Comparison/quantization delta.
4001     nshortest: The number of paths to return.
4002     nstate: State number threshold.
4003     queue_type: A string matching a known queue type; one of: "auto", "fifo",
4004         "lifo", "shortest", "state", "top".
4005     unique: Should the resulting FST only contain distinct paths? (Requires
4006         the input FST to be an acceptor; epsilons are treated as if they are
4007         regular symbols.)
4008     weight: A Weight or weight string indicating the desired weight threshold
4009         below which paths are pruned; if omitted, no paths are pruned.
4010
4011   Returns:
4012     An FST containing the n-shortest paths.
4013   """
4014   cdef unique_ptr[fst.VectorFstClass] tfst
4015   tfst.reset(new fst.VectorFstClass(ifst.arc_type()))
4016   # Threshold is set to semiring Zero (no pruning) if no weight is specified.
4017   cdef fst.WeightClass wc = _get_WeightClass_or_Zero(ifst.weight_type(), weight)
4018   cdef unique_ptr[fst.ShortestPathOptions] opts
4019   opts.reset(new fst.ShortestPathOptions(_get_queue_type(tostring(queue_type)),
4020                                          nshortest, unique, delta, wc, nstate))
4021   fst.ShortestPath(deref(ifst._fst), tfst.get(), deref(opts))
4022   return _init_MutableFst(tfst.release())
4023
4024
4025 cpdef _Fst statemap(_Fst ifst, map_type):
4026   """
4027   state_map(ifst, map_type)
4028
4029   Constructively applies a transform to all states.
4030
4031   This operation transforms each state according to the requested map type.
4032   Note that currently, only one state-mapping operation is supported.
4033
4034   Args:
4035     ifst: The input FST.
4036     map_type: A string matching a known mapping operation; one of: "arc_sum"
4037         (sum weights of identically-labeled multi-arcs), "arc_unique" (deletes
4038         non-unique identically-labeled multi-arcs).
4039
4040   Returns:
4041     An FST with states remapped.
4042
4043   Raises:
4044     FstArgError: Unknown map type.
4045
4046   See also: `arcmap`.
4047   """
4048   return _map(ifst, fst.kDelta, map_type, None)
4049
4050
4051 cpdef _MutableFst synchronize(_Fst ifst):
4052   """
4053   synchronize(ifst)
4054
4055   Constructively synchronizes an FST.
4056
4057   This operation synchronizes a transducer. The result will be an equivalent
4058   FST that has the property that during the traversal of a path, the delay is
4059   either zero or strictly increasing, where the delay is the difference between
4060   the number of non-epsilon output labels and input labels along the path. For
4061   the algorithm to terminate, the input transducer must have bounded delay,
4062   i.e., the delay of every cycle must be zero.
4063
4064   Args:
4065     ifst: The input FST.
4066
4067   Returns:
4068     An equivalent synchronized FST.
4069   """
4070   cdef unique_ptr[fst.VectorFstClass] tfst
4071   tfst.reset(new fst.VectorFstClass(ifst.arc_type()))
4072   fst.Synchronize(deref(ifst._fst), tfst.get())
4073   return _init_MutableFst(tfst.release())
4074
4075
4076 ## Compiler.
4077
4078
4079 cdef class Compiler(object):
4080
4081   """
4082   Compiler(fst_type="vector", arc_type="standard", isymbols=None,
4083            osymbols=None, ssymbols=None, acceptor=False, keep_isymbols=False,
4084            keep_osymbols=False, keep_state_numbering=False,
4085            allow_negative_labels=False)
4086
4087   Class used to compile FSTs from strings.
4088
4089   This class is used to compile FSTs specified using the AT&T FSM library
4090   format described here:
4091
4092   http://web.eecs.umich.edu/~radev/NLP-fall2015/resources/fsm_archive/fsm.5.html
4093
4094   This is the same format used by the `fstcompile` executable.
4095
4096   Compiler options (symbol tables, etc.) are set at construction time.
4097
4098       compiler = fst.Compiler(isymbols=ascii_syms, osymbols=ascii_syms)
4099
4100   Once constructed, Compiler instances behave like a file handle opened for
4101   writing:
4102
4103       # /ba+/
4104       print >> compiler, "0 1 50 50"
4105       print >> compiler, "1 2 49 49"
4106       print >> compiler, "2 2 49 49"
4107       print >> compiler, "2"
4108
4109   The `compile` method returns an actual FST instance:
4110
4111       sheep_machine = compiler.compile()
4112
4113   Compilation flushes the internal buffer, so the compiler instance can be
4114   reused to compile new machines with the same symbol tables (etc.)
4115
4116   Args:
4117     fst_type: A string indicating the container type for the compiled FST.
4118     arc_type: A string indicating the arc type for the compiled FST.
4119     isymbols: An optional SymbolTable used to label input symbols.
4120     osymbols: An optional SymbolTable used to label output symbols.
4121     ssymbols: An optional SymbolTable used to label states.
4122     acceptor: Should the FST be rendered in acceptor format if possible?
4123     keep_isymbols: Should the input symbol table be stored in the FST?
4124     keep_osymbols: Should the output symbol table be stored in the FST?
4125     keep_state_numbering: Should the state numbering be preserved?
4126     allow_negative_labels: Should negative labels be allowed? (Not
4127         recommended; may cause conflicts).
4128   """
4129
4130   def __cinit__(self,
4131                 string fst_type=b"vector",
4132                 string arc_type=b"standard",
4133                 SymbolTable isymbols=None,
4134                 SymbolTable osymbols=None,
4135                 SymbolTable ssymbols=None,
4136                 bool acceptor=False,
4137                 bool keep_isymbols=False,
4138                 bool keep_osymbols=False,
4139                 bool keep_state_numbering=False,
4140                 bool allow_negative_labels=False):
4141     self._sstrm.reset(new stringstream())
4142     self._fst_type = tostring(fst_type)
4143     self._arc_type = tostring(arc_type)
4144     self._isymbols = NULL
4145     if isymbols is not None:
4146       self._isymbols = isymbols._table
4147     self._osymbols = NULL
4148     if osymbols is not None:
4149       self._osymbols = osymbols._table
4150     self._ssymbols = NULL
4151     if ssymbols is not None:
4152       self._ssymbols = ssymbols._table
4153     self._acceptor = acceptor
4154     self._keep_isymbols = keep_isymbols
4155     self._keep_osymbols = keep_osymbols
4156     self._keep_state_numbering = keep_state_numbering
4157     self._allow_negative_labels = allow_negative_labels
4158
4159   cpdef _Fst compile(self):
4160     """
4161     compile()
4162
4163     Compiles the FST in the compiler string buffer.
4164
4165     This method compiles the FST and returns the resulting machine.
4166
4167     Returns:
4168       The FST described by the compiler string buffer.
4169
4170     Raises:
4171       FstOpError: Compilation failed.
4172     """
4173     cdef unique_ptr[fst.FstClass] tfst
4174     tfst.reset(fst.CompileFstInternal(deref(self._sstrm),
4175         b"<pywrapfst>", self._fst_type, self._arc_type, self._isymbols,
4176         self._osymbols, self._ssymbols, self._acceptor, self._keep_isymbols,
4177         self._keep_osymbols, self._keep_state_numbering,
4178         self._allow_negative_labels))
4179     self._sstrm.reset(new stringstream())
4180     if tfst.get() == NULL:
4181       raise FstOpError("Compilation failed")
4182     return _init_XFst(tfst.release())
4183
4184   cpdef void write(self, expression):
4185     """
4186     write(expression)
4187
4188     Writes a string into the compiler string buffer.
4189
4190     This method adds a line to the compiler string buffer. It is normally
4191     invoked using the right shift operator, like so:
4192
4193         compiler = fst.Compiler()
4194         print >> compiler, "0 0 49 49"
4195         print >> compiler, "0"
4196
4197     Args:
4198       expression: A string expression to add to compiler string buffer.
4199     """
4200     deref(self._sstrm) << tostring(expression)
4201
4202
4203 ## FarReader and FarWriter.
4204
4205
4206 cdef class FarReader(object):
4207
4208   """
4209   (No constructor.)
4210
4211   FAR ("Fst ARchive") reader object.
4212
4213   This class is used to read a FAR from disk. FARs contain one or more FSTs (of
4214   the same arc type) indexed by a unique string key. To construct a FarReader
4215   object, use the `open` class method.
4216
4217   Attributes:
4218     arc_type: A string indicating the arc type.
4219     far_type: A string indicating the FAR type.
4220   """
4221
4222   def __init__(self):
4223     raise FstDeletedConstructorError(
4224         "Cannot construct {}".format(self.__class__.__name__))
4225
4226   def __repr__(self):
4227     return "<{} FarReader at 0x{:x}>".format(self.far_type(), id(self))
4228
4229   @classmethod
4230   def open(cls, *filenames):
4231     """
4232     FarReader.open(*filenames)
4233
4234     Creates a FarReader object.
4235
4236     This class method creates a FarReader given the string location of one or
4237     more FAR files on disk.
4238
4239     Args:
4240       *filenames: The string location of one or more input FAR files.
4241
4242     Returns:
4243       A new FarReader instance.
4244
4245     Raises:
4246       FstIOError: Read failed.
4247     """
4248     filenames = [tostring(filename) for filename in filenames]
4249     cdef unique_ptr[fst.FarReaderClass] tfar
4250     tfar.reset(fst.FarReaderClass.Open(filenames))
4251     if tfar.get() == NULL:
4252       raise FstIOError("Read failed: {!r}".format(filenames))
4253     cdef FarReader result = FarReader.__new__(FarReader)
4254     result._reader.reset(tfar.release())
4255     return result
4256
4257   # This just registers this class as a possible iterator.
4258   def __iter__(self):
4259     return self
4260
4261   # Magic method used to get a Pythonic API out of the C++ API.
4262   def __next__(self):
4263     if self.done():
4264       raise StopIteration
4265     cdef string k = self.get_key()
4266     cdef _Fst f = self.get_fst()
4267     self.next()
4268     return (k, f)
4269
4270   cpdef string arc_type(self):
4271     """
4272     arc_type(self)
4273
4274     Returns a string indicating the arc type.
4275     """
4276     return self._reader.get().ArcType()
4277
4278   cpdef bool done(self):
4279     """
4280     done(self)
4281
4282     Indicates whether the iterator is exhausted or not.
4283
4284     Returns:
4285       True if the iterator is exhausted, False otherwise.
4286     """
4287     return self._reader.get().Done()
4288
4289   cpdef bool error(self):
4290     """
4291     error(self)
4292
4293     Indicates whether the FarReader has encountered an error.
4294
4295     Returns:
4296       True if the FarReader is in an errorful state, False otherwise.
4297     """
4298     return self._reader.get().Error()
4299
4300   cpdef string far_type(self):
4301     return fst.GetFarTypeString(self._reader.get().Type())
4302
4303   cpdef bool find(self, key):
4304     """
4305     find(self, key)
4306
4307     Sets the current position to the first entry greater than or equal to the
4308     key (a string) and indicates whether or not a match was found.
4309
4310     Args:
4311       key: A string key.
4312
4313     Returns:
4314       True if the key was found, False otherwise.
4315     """
4316     return self._reader.get().Find(tostring(key))
4317
4318   cpdef _Fst get_fst(self):
4319     """
4320     get_fst(self)
4321
4322     Returns the FST at the current position.
4323
4324     Returns:
4325       A copy of the FST at the current position.
4326     """
4327     return _init_XFst(new fst.FstClass(
4328         deref(self._reader.get().GetFstClass())))
4329
4330   cpdef string get_key(self):
4331     """
4332     get_key(self)
4333
4334     Returns the string key at the current position.
4335
4336     Returns:
4337       The string key at the current position.
4338     """
4339     return self._reader.get().GetKey()
4340
4341   cpdef void next(self):
4342     """
4343     next(self)
4344
4345     Advances the iterator.
4346     """
4347     self._reader.get().Next()
4348
4349   cpdef void reset(self):
4350     """
4351     reset(self)
4352
4353     Resets the iterator to the initial position.
4354     """
4355     self._reader.get().Reset()
4356
4357   # Dictionary-like access by combining `find` and `get_fst`.
4358   def __getitem__(self, key):
4359     if not self.find(key):
4360       raise KeyError(key)
4361     return self.get_fst()
4362
4363
4364 cdef class FarWriter(object):
4365
4366   """
4367   (No constructor.)
4368
4369   FAR ("Fst ARchive") writer object.
4370
4371   This class is used to write FSTs (of the same arc type) to a FAR on disk. To
4372   construct a FarWriter, use the `create` class method.
4373
4374   Note that the data is not guaranteed to flush to disk until the FarWriter
4375   is garbage-collected. If a FarWriter has been assigned to only one variable,
4376   then calling `del` on that variable should decrement the object's reference
4377   count from 1 to 0, triggering a flush to disk on the next GC cycle.
4378
4379   Attributes:
4380     arc_type: A string indicating the arc type.
4381     far_type: A string indicating the FAR type.
4382   """
4383
4384   def __init__(self):
4385     raise FstDeletedConstructorError(
4386         "Cannot construct {}".format(self.__class__.__name__))
4387
4388   def __repr__(self):
4389     return "<{} FarWriter at 0x{:x}>".format(self.far_type(), id(self))
4390
4391   @classmethod
4392   def create(cls, filename, arc_type=b"standard", far_type=b"default"):
4393     """
4394     FarWriter.
4395
4396     Creates a FarWriter object.
4397
4398     This class method creates a FarWriter given the desired output location,
4399     arc type, and FAR type.
4400
4401     Args:
4402       filename: The string location for the output FAR files.
4403       arc_type: A string indicating the arc type.
4404       far_type: A string indicating the FAR type; one of: "fst", "stlist",
4405           "sttable", "sstable", "default".
4406
4407     Returns:
4408       A new FarWriter instance.
4409
4410     Raises:
4411       FstIOError: Read failed.
4412     """
4413     cdef fst.FarType ft = fst.GetFarType(tostring(far_type))
4414     cdef fst.FarWriterClass *tfar = fst.FarWriterClass.Create(
4415         tostring(filename), tostring(arc_type), ft)
4416     if tfar == NULL:
4417       raise FstIOError("Open failed: {!r}".format(filename))
4418     cdef FarWriter result = FarWriter.__new__(FarWriter)
4419     result._writer.reset(tfar)
4420     return result
4421
4422   # NB: Invoking this method is DANGEROUS: calling any other method on the
4423   # instance after this is invoked may result in a null dereference.
4424   cdef void _close(self):
4425     self._writer.reset()
4426
4427   cpdef void add(self, key, _Fst ifst) except *:
4428     """
4429     add(self, key, ifst)
4430
4431     Adds an FST to the FAR.
4432
4433     This method adds an FST to the FAR which can be retrieved with the
4434     specified string key.
4435
4436     Args:
4437       key: The string used to key the input FST.
4438       ifst: The FST to write to the FAR.
4439
4440     Raises:
4441       FstArgError: Key out of order.
4442       FstOpError: Incompatible or invalid arc type.
4443     """
4444     # Failure here results from passing an FST with a different arc type than
4445     # used by the FAR was initialized to use.
4446     if not self._writer.get().Add(tostring(key), deref(ifst._fst)):
4447       raise FstOpError("Incompatible or invalid arc type")
4448     # An error here usually indicates a key out of order.
4449     if self._writer.get().Error():
4450       raise FstArgError("Key out of order")
4451
4452   cpdef string arc_type(self):
4453     """
4454     arc_type(self)
4455
4456     Returns a string indicating the arc type.
4457     """
4458     return self._writer.get().ArcType()
4459
4460   cpdef bool error(self):
4461     """
4462     error(self)
4463
4464     Indicates whether the FarWriter has encountered an error.
4465
4466     Returns:
4467       True if the FarWriter is in an errorful state, False otherwise.
4468     """
4469     return self._writer.get().Error()
4470
4471   cpdef string far_type(self):
4472     """
4473     far_type(self)
4474
4475     Returns a string indicating the FAR type.
4476     """
4477     return fst.GetFarTypeString(self._writer.get().Type())
4478
4479   # Dictionary-like assignment.
4480   def __setitem__(self, key, _Fst fst):
4481     self.add(key, fst)
4482
4483
4484 ## Cleanup operations for module entrance and exit.
4485
4486
4487 # Masks fst_error_fatal flags while this module is running, returning to the
4488 # previous state upon module exit.
4489
4490
4491 _fst_error_fatal_old = fst.FLAGS_fst_error_fatal
4492 fst.FLAGS_fst_error_fatal = False
4493
4494
4495 @atexit.register
4496 def _reset_fst_error_fatal():
4497   fst.FLAGS_fst_error_fatal = _fst_error_fatal_old