1 // See www.openfst.org for extensive documentation on this weighted
2 // finite-state transducer library.
4 // Functions and classes for various FST state queues with a unified interface.
11 #include <type_traits>
17 #include <fst/arcfilter.h>
18 #include <fst/connect.h>
20 #include <fst/topsort.h>
25 // The Queue interface is:
32 // // Constructor: may need args (e.g., FST, comparator) for some queues.
33 // Queue(...) override;
35 // // Returns the head of the queue.
36 // StateId Head() const override;
38 // // Inserts a state.
39 // void Enqueue(StateId s) override;
41 // // Removes the head of the queue.
42 // void Dequeue() override;
44 // // Updates ordering of state s when weight changes, if necessary.
45 // void Update(StateId s) override;
47 // // Is the queue empty?
48 // bool Empty() const override;
50 // // Removes all states from the queue.
51 // void Clear() override;
56 TRIVIAL_QUEUE = 0, // Single state queue.
57 FIFO_QUEUE = 1, // First-in, first-out queue.
58 LIFO_QUEUE = 2, // Last-in, first-out queue.
59 SHORTEST_FIRST_QUEUE = 3, // Shortest-first queue.
60 TOP_ORDER_QUEUE = 4, // Topologically-ordered queue.
61 STATE_ORDER_QUEUE = 5, // State ID-ordered queue.
62 SCC_QUEUE = 6, // Component graph top-ordered meta-queue.
63 AUTO_QUEUE = 7, // Auto-selected queue.
67 // QueueBase, templated on the StateId, is a virtual base class shared by all
68 // queues considered by AutoQueue.
74 virtual ~QueueBase() {}
76 // Concrete implementation.
78 explicit QueueBase(QueueType type) : queue_type_(type), error_(false) {}
80 void SetError(bool error) { error_ = error; }
82 bool Error() const { return error_; }
84 QueueType Type() const { return queue_type_; }
88 virtual StateId Head() const = 0;
89 virtual void Enqueue(StateId) = 0;
90 virtual void Dequeue() = 0;
91 virtual void Update(StateId) = 0;
92 virtual bool Empty() const = 0;
93 virtual void Clear() = 0;
96 QueueType queue_type_;
100 // Trivial queue discipline; one may enqueue at most one state at a time. It
101 // can be used for strongly connected components with only one state and no
104 class TrivialQueue final : public QueueBase<S> {
108 TrivialQueue() : QueueBase<StateId>(TRIVIAL_QUEUE), front_(kNoStateId) {}
110 virtual ~TrivialQueue() = default;
112 StateId Head() const override { return front_; }
114 void Enqueue(StateId s) override { front_ = s; }
116 void Dequeue() override { front_ = kNoStateId; }
118 void Update(StateId) override {}
120 bool Empty() const override { return front_ == kNoStateId; }
122 void Clear() override { front_ = kNoStateId; }
128 // First-in, first-out queue discipline.
130 // This is not a final class.
132 class FifoQueue : public QueueBase<S> {
136 FifoQueue() : QueueBase<StateId>(FIFO_QUEUE) {}
138 virtual ~FifoQueue() = default;
140 StateId Head() const override { return queue_.back(); }
142 void Enqueue(StateId s) override { queue_.push_front(s); }
144 void Dequeue() override { queue_.pop_back(); }
146 void Update(StateId) override {}
148 bool Empty() const override { return queue_.empty(); }
150 void Clear() override { queue_.clear(); }
153 std::deque<StateId> queue_;
156 // Last-in, first-out queue discipline.
158 class LifoQueue final : public QueueBase<S> {
162 LifoQueue() : QueueBase<StateId>(LIFO_QUEUE) {}
164 virtual ~LifoQueue() = default;
166 StateId Head() const override { return queue_.front(); }
168 void Enqueue(StateId s) override { queue_.push_front(s); }
170 void Dequeue() override { queue_.pop_front(); }
172 void Update(StateId) override {}
174 bool Empty() const override { return queue_.empty(); }
176 void Clear() override { queue_.clear(); }
179 std::deque<StateId> queue_;
182 // Shortest-first queue discipline, templated on the StateId and as well as a
183 // comparison functor used to compare two StateIds. If a (single) state's order
184 // changes, it can be reordered in the queue with a call to Update(). If update
185 // is false, call to Update() does not reorder the queue.
187 // This is not a final class.
188 template <typename S, typename Compare, bool update = true>
189 class ShortestFirstQueue : public QueueBase<S> {
193 static constexpr StateId kNoKey = -1;
195 explicit ShortestFirstQueue(Compare comp)
196 : QueueBase<StateId>(SHORTEST_FIRST_QUEUE), heap_(comp) {}
198 StateId Head() const override { return heap_.Top(); }
200 void Enqueue(StateId s) override {
202 for (StateId i = key_.size(); i <= s; ++i) key_.push_back(kNoKey);
203 key_[s] = heap_.Insert(s);
209 void Dequeue() override {
211 key_[heap_.Pop()] = kNoKey;
217 void Update(StateId s) override {
219 if (s >= key_.size() || key_[s] == kNoKey) {
222 heap_.Update(key_[s], s);
226 bool Empty() const override { return heap_.Empty(); }
228 void Clear() override {
230 if (update) key_.clear();
234 Heap<StateId, Compare> heap_;
235 std::vector<ssize_t> key_;
238 template <typename StateId, typename Compare, bool update>
239 constexpr StateId ShortestFirstQueue<StateId, Compare, update>::kNoKey;
243 // Given a vector that maps from states to weights, and a comparison functor
244 // for weights, this class defines a comparison function object between states.
245 template <typename StateId, typename Less>
246 class StateWeightCompare {
248 using Weight = typename Less::Weight;
250 StateWeightCompare(const std::vector<Weight> &weights, const Less &less)
251 : weights_(weights), less_(less) {}
253 bool operator()(const StateId s1, const StateId s2) const {
254 return less_(weights_[s1], weights_[s2]);
258 // Borrowed references.
259 const std::vector<Weight> &weights_;
263 } // namespace internal
265 // Shortest-first queue discipline, templated on the StateId and Weight, is
266 // specialized to use the weight's natural order for the comparison function.
267 template <typename S, typename Weight>
268 class NaturalShortestFirstQueue final
269 : public ShortestFirstQueue<
270 S, internal::StateWeightCompare<S, NaturalLess<Weight>>> {
273 using Compare = internal::StateWeightCompare<StateId, NaturalLess<Weight>>;
275 explicit NaturalShortestFirstQueue(const std::vector<Weight> &distance)
276 : ShortestFirstQueue<StateId, Compare>(Compare(distance, less_)) {}
278 virtual ~NaturalShortestFirstQueue() = default;
281 // This is non-static because the constructor for non-idempotent weights will
282 // result in a an error.
283 const NaturalLess<Weight> less_{};
286 // Topological-order queue discipline, templated on the StateId. States are
287 // ordered in the queue topologically. The FST must be acyclic.
289 class TopOrderQueue final : public QueueBase<S> {
293 // This constructor computes the topological order. It accepts an arc filter
294 // to limit the transitions considered in that computation (e.g., only the
296 template <class Arc, class ArcFilter>
297 TopOrderQueue(const Fst<Arc> &fst, ArcFilter filter)
298 : QueueBase<StateId>(TOP_ORDER_QUEUE),
304 TopOrderVisitor<Arc> top_order_visitor(&order_, &acyclic);
305 DfsVisit(fst, &top_order_visitor, filter);
307 FSTERROR() << "TopOrderQueue: FST is not acyclic";
308 QueueBase<S>::SetError(true);
310 state_.resize(order_.size(), kNoStateId);
313 // This constructor is passed the pre-computed topological order.
314 explicit TopOrderQueue(const std::vector<StateId> &order)
315 : QueueBase<StateId>(TOP_ORDER_QUEUE),
319 state_(order.size(), kNoStateId) {}
321 virtual ~TopOrderQueue() = default;
323 StateId Head() const override { return state_[front_]; }
325 void Enqueue(StateId s) override {
326 if (front_ > back_) {
327 front_ = back_ = order_[s];
328 } else if (order_[s] > back_) {
330 } else if (order_[s] < front_) {
333 state_[order_[s]] = s;
336 void Dequeue() override {
337 state_[front_] = kNoStateId;
338 while ((front_ <= back_) && (state_[front_] == kNoStateId)) ++front_;
341 void Update(StateId) override {}
343 bool Empty() const override { return front_ > back_; }
345 void Clear() override {
346 for (StateId s = front_; s <= back_; ++s) state_[s] = kNoStateId;
354 std::vector<StateId> order_;
355 std::vector<StateId> state_;
358 // State order queue discipline, templated on the StateId. States are ordered in
359 // the queue by state ID.
361 class StateOrderQueue final : public QueueBase<S> {
366 : QueueBase<StateId>(STATE_ORDER_QUEUE), front_(0), back_(kNoStateId) {}
368 virtual ~StateOrderQueue() = default;
370 StateId Head() const override { return front_; }
372 void Enqueue(StateId s) override {
373 if (front_ > back_) {
375 } else if (s > back_) {
377 } else if (s < front_) {
380 while (enqueued_.size() <= s) enqueued_.push_back(false);
384 void Dequeue() override {
385 enqueued_[front_] = false;
386 while ((front_ <= back_) && (enqueued_[front_] == false)) ++front_;
389 void Update(StateId) override {}
391 bool Empty() const override { return front_ > back_; }
393 void Clear() override {
394 for (StateId i = front_; i <= back_; ++i) enqueued_[i] = false;
402 std::vector<bool> enqueued_;
405 // SCC topological-order meta-queue discipline, templated on the StateId and a
406 // queue used inside each SCC. It visits the SCCs of an FST in topological
407 // order. Its constructor is passed the queues to to use within an SCC.
408 template <class S, class Queue>
409 class SccQueue final : public QueueBase<S> {
413 // Constructor takes a vector specifying the SCC number per state and a
414 // vector giving the queue to use per SCC number.
415 SccQueue(const std::vector<StateId> &scc,
416 std::vector<std::unique_ptr<Queue>> *queue)
417 : QueueBase<StateId>(SCC_QUEUE),
423 virtual ~SccQueue() = default;
425 StateId Head() const override {
426 while ((front_ <= back_) &&
427 (((*queue_)[front_] && (*queue_)[front_]->Empty()) ||
428 (((*queue_)[front_] == nullptr) &&
429 ((front_ >= trivial_queue_.size()) ||
430 (trivial_queue_[front_] == kNoStateId))))) {
433 if ((*queue_)[front_]) {
434 return (*queue_)[front_]->Head();
436 return trivial_queue_[front_];
440 void Enqueue(StateId s) override {
441 if (front_ > back_) {
442 front_ = back_ = scc_[s];
443 } else if (scc_[s] > back_) {
445 } else if (scc_[s] < front_) {
448 if ((*queue_)[scc_[s]]) {
449 (*queue_)[scc_[s]]->Enqueue(s);
451 while (trivial_queue_.size() <= scc_[s]) {
452 trivial_queue_.push_back(kNoStateId);
454 trivial_queue_[scc_[s]] = s;
458 void Dequeue() override {
459 if ((*queue_)[front_]) {
460 (*queue_)[front_]->Dequeue();
461 } else if (front_ < trivial_queue_.size()) {
462 trivial_queue_[front_] = kNoStateId;
466 void Update(StateId s) override {
467 if ((*queue_)[scc_[s]]) (*queue_)[scc_[s]]->Update(s);
470 bool Empty() const override {
471 // Queue SCC number back_ is not empty unless back_ == front_.
472 if (front_ < back_) {
474 } else if (front_ > back_) {
476 } else if ((*queue_)[front_]) {
477 return (*queue_)[front_]->Empty();
479 return (front_ >= trivial_queue_.size()) ||
480 (trivial_queue_[front_] == kNoStateId);
484 void Clear() override {
485 for (StateId i = front_; i <= back_; ++i) {
487 (*queue_)[i]->Clear();
488 } else if (i < trivial_queue_.size()) {
489 trivial_queue_[i] = kNoStateId;
497 std::vector<std::unique_ptr<Queue>> *queue_;
498 const std::vector<StateId> &scc_;
499 mutable StateId front_;
501 std::vector<StateId> trivial_queue_;
504 // Automatic queue discipline. It selects a queue discipline for a given FST
505 // based on its properties.
507 class AutoQueue final : public QueueBase<S> {
511 // This constructor takes a state distance vector that, if non-null and if
512 // the Weight type has the path property, will entertain the shortest-first
513 // queue using the natural order w.r.t to the distance.
514 template <class Arc, class ArcFilter>
515 AutoQueue(const Fst<Arc> &fst,
516 const std::vector<typename Arc::Weight> *distance, ArcFilter filter)
517 : QueueBase<StateId>(AUTO_QUEUE) {
518 using Weight = typename Arc::Weight;
519 // TrivialLess is never instantiated since the construction of Less is
520 // guarded by Properties() & kPath. It is only here to avoid instantiating
521 // NaturalLess for non-path weights.
523 using Weight = typename Arc::Weight;
524 bool operator()(const Weight &, const Weight &) const { return false; }
527 typename std::conditional<(Weight::Properties() & kPath) == kPath,
528 NaturalLess<Weight>, TrivialLess>::type;
529 using Compare = internal::StateWeightCompare<StateId, Less>;
530 // First checks if the FST is known to have these properties.
532 fst.Properties(kAcyclic | kCyclic | kTopSorted | kUnweighted, false);
533 if ((props & kTopSorted) || fst.Start() == kNoStateId) {
534 queue_.reset(new StateOrderQueue<StateId>());
535 VLOG(2) << "AutoQueue: using state-order discipline";
536 } else if (props & kAcyclic) {
537 queue_.reset(new TopOrderQueue<StateId>(fst, filter));
538 VLOG(2) << "AutoQueue: using top-order discipline";
539 } else if ((props & kUnweighted) && (Weight::Properties() & kIdempotent)) {
540 queue_.reset(new LifoQueue<StateId>());
541 VLOG(2) << "AutoQueue: using LIFO discipline";
544 // Decomposes into strongly-connected components.
545 SccVisitor<Arc> scc_visitor(&scc_, nullptr, nullptr, &properties);
546 DfsVisit(fst, &scc_visitor, filter);
547 auto nscc = *std::max_element(scc_.begin(), scc_.end()) + 1;
548 std::vector<QueueType> queue_types(nscc);
549 std::unique_ptr<Less> less;
550 std::unique_ptr<Compare> comp;
551 if (distance && (Weight::Properties() & kPath)) {
552 less.reset(new Less);
553 comp.reset(new Compare(*distance, *less));
555 // Finds the queue type to use per SCC.
558 SccQueueType(fst, scc_, &queue_types, filter, less.get(), &all_trivial,
560 // If unweighted and semiring is idempotent, uses LIFO queue.
562 queue_.reset(new LifoQueue<StateId>());
563 VLOG(2) << "AutoQueue: using LIFO discipline";
566 // If all the SCC are trivial, the FST is acyclic and the scc number gives
567 // the topological order.
569 queue_.reset(new TopOrderQueue<StateId>(scc_));
570 VLOG(2) << "AutoQueue: using top-order discipline";
573 VLOG(2) << "AutoQueue: using SCC meta-discipline";
574 queues_.resize(nscc);
575 for (StateId i = 0; i < nscc; ++i) {
576 switch (queue_types[i]) {
579 VLOG(3) << "AutoQueue: SCC #" << i << ": using trivial discipline";
581 case SHORTEST_FIRST_QUEUE:
583 new ShortestFirstQueue<StateId, Compare, false>(*comp));
584 VLOG(3) << "AutoQueue: SCC #" << i
585 << ": using shortest-first discipline";
588 queues_[i].reset(new LifoQueue<StateId>());
589 VLOG(3) << "AutoQueue: SCC #" << i << ": using LIFO discipline";
593 queues_[i].reset(new FifoQueue<StateId>());
594 VLOG(3) << "AutoQueue: SCC #" << i << ": using FIFO discipine";
598 queue_.reset(new SccQueue<StateId, QueueBase<StateId>>(scc_, &queues_));
602 virtual ~AutoQueue() = default;
604 StateId Head() const override { return queue_->Head(); }
606 void Enqueue(StateId s) override { queue_->Enqueue(s); }
608 void Dequeue() override { queue_->Dequeue(); }
610 void Update(StateId s) override { queue_->Update(s); }
612 bool Empty() const override { return queue_->Empty(); }
614 void Clear() override { queue_->Clear(); }
617 template <class Arc, class ArcFilter, class Less>
618 static void SccQueueType(const Fst<Arc> &fst, const std::vector<StateId> &scc,
619 std::vector<QueueType> *queue_types,
620 ArcFilter filter, Less *less, bool *all_trivial,
623 std::unique_ptr<QueueBase<StateId>> queue_;
624 std::vector<std::unique_ptr<QueueBase<StateId>>> queues_;
625 std::vector<StateId> scc_;
628 // Examines the states in an FST's strongly connected components and determines
629 // which type of queue to use per SCC. Stores result as a vector of QueueTypes
630 // which is assumed to have length equal to the number of SCCs. An arc filter
631 // is used to limit the transitions considered (e.g., only the epsilon graph).
632 // The argument all_trivial is set to true if every queue is the trivial queue.
633 // The argument unweighted is set to true if the semiring is idempotent and all
634 // the arc weights are equal to Zero() or One().
635 template <class StateId>
636 template <class Arc, class ArcFilter, class Less>
637 void AutoQueue<StateId>::SccQueueType(const Fst<Arc> &fst,
638 const std::vector<StateId> &scc,
639 std::vector<QueueType> *queue_type,
640 ArcFilter filter, Less *less,
641 bool *all_trivial, bool *unweighted) {
642 using StateId = typename Arc::StateId;
643 using Weight = typename Arc::Weight;
646 for (StateId i = 0; i < queue_type->size(); ++i) {
647 (*queue_type)[i] = TRIVIAL_QUEUE;
649 for (StateIterator<Fst<Arc>> sit(fst); !sit.Done(); sit.Next()) {
650 const auto state = sit.Value();
651 for (ArcIterator<Fst<Arc>> ait(fst, state); !ait.Done(); ait.Next()) {
652 const auto &arc = ait.Value();
653 if (!filter(arc)) continue;
654 if (scc[state] == scc[arc.nextstate]) {
655 auto &type = (*queue_type)[scc[state]];
656 if (!less || ((*less)(arc.weight, Weight::One()))) {
658 } else if ((type == TRIVIAL_QUEUE) || (type == LIFO_QUEUE)) {
659 if (!(Weight::Properties() & kIdempotent) ||
660 (arc.weight != Weight::Zero() && arc.weight != Weight::One())) {
661 type = SHORTEST_FIRST_QUEUE;
666 if (type != TRIVIAL_QUEUE) *all_trivial = false;
668 if (!(Weight::Properties() & kIdempotent) ||
669 (arc.weight != Weight::Zero() && arc.weight != Weight::One())) {
676 // An A* estimate is a function object that maps from a state ID to a an
677 // estimate of the shortest distance to the final states.
679 // The trivial A* estimate is always One().
680 template <typename StateId, typename Weight>
681 struct TrivialAStarEstimate {
682 Weight operator()(StateId) const { return Weight::One(); }
685 // Given a vector that maps from states to weights representing the shortest
686 // distance from the initial state, a comparison function object between
687 // weights, and an estimate of the shortest distance to the final states, this
688 // class defines a comparison function object between states.
689 template <typename S, typename Less, typename Estimate>
690 class AStarWeightCompare {
693 using Weight = typename Less::Weight;
695 AStarWeightCompare(const std::vector<Weight> &weights, const Less &less,
696 const Estimate &estimate)
697 : weights_(weights), less_(less), estimate_(estimate) {}
699 bool operator()(const StateId s1, const StateId s2) const {
700 const auto w1 = Times(weights_[s1], estimate_(s1));
701 const auto w2 = Times(weights_[s2], estimate_(s2));
702 return less_(w1, w2);
706 // Borrowed references.
707 const std::vector<Weight> &weights_;
709 const Estimate &estimate_;
712 // A* queue discipline templated on StateId, Weight, and Estimate.
713 template <typename S, typename Weight, typename Estimate>
714 class NaturalAStarQueue final
715 : public ShortestFirstQueue<
716 S, AStarWeightCompare<S, NaturalLess<Weight>, Estimate>> {
719 using Compare = AStarWeightCompare<StateId, NaturalLess<Weight>, Estimate>;
721 NaturalAStarQueue(const std::vector<Weight> &distance,
722 const Estimate &estimate)
723 : ShortestFirstQueue<StateId, Compare>(
724 Compare(distance, less_, estimate)) {}
726 virtual ~NaturalAStarQueue() = default;
729 // This is non-static because the constructor for non-idempotent weights will
730 // result in a an error.
731 const NaturalLess<Weight> less_{};
734 // A state equivalence class is a function object that maps from a state ID to
735 // an equivalence class (state) ID. The trivial equivalence class maps a state
737 template <typename StateId>
738 struct TrivialStateEquivClass {
739 StateId operator()(StateId s) const { return s; }
742 // Distance-based pruning queue discipline: Enqueues a state only when its
743 // shortest distance (so far), as specified by distance, is less than (as
744 // specified by comp) the shortest distance Times() the threshold to any state
745 // in the same equivalence class, as specified by the functor class_func. The
746 // underlying queue discipline is specified by queue. The ownership of queue is
747 // given to this class.
749 // This is not a final class.
750 template <typename Queue, typename Less, typename ClassFnc>
751 class PruneQueue : public QueueBase<typename Queue::StateId> {
753 using StateId = typename Queue::StateId;
754 using Weight = typename Less::Weight;
756 PruneQueue(const std::vector<Weight> &distance, Queue *queue,
757 const Less &less, const ClassFnc &class_fnc, Weight threshold)
758 : QueueBase<StateId>(OTHER_QUEUE),
762 class_fnc_(class_fnc),
763 threshold_(std::move(threshold)) {}
765 virtual ~PruneQueue() = default;
767 StateId Head() const override { return queue_->Head(); }
769 void Enqueue(StateId s) override {
770 const auto c = class_fnc_(s);
771 if (c >= class_distance_.size()) {
772 class_distance_.resize(c + 1, Weight::Zero());
774 if (less_(distance_[s], class_distance_[c])) {
775 class_distance_[c] = distance_[s];
777 // Enqueues only if below threshold limit.
778 const auto limit = Times(class_distance_[c], threshold_);
779 if (less_(distance_[s], limit)) queue_->Enqueue(s);
782 void Dequeue() override { queue_->Dequeue(); }
784 void Update(StateId s) override {
785 const auto c = class_fnc_(s);
786 if (less_(distance_[s], class_distance_[c])) {
787 class_distance_[c] = distance_[s];
792 bool Empty() const override { return queue_->Empty(); }
794 void Clear() override { queue_->Clear(); }
797 const std::vector<Weight> &distance_; // Shortest distance to state.
798 std::unique_ptr<Queue> queue_;
799 const Less &less_; // Borrowed reference.
800 const ClassFnc &class_fnc_; // Equivalence class functor.
801 Weight threshold_; // Pruning weight threshold.
802 std::vector<Weight> class_distance_; // Shortest distance to class.
805 // Pruning queue discipline (see above) using the weight's natural order for the
806 // comparison function. The ownership of the queue argument is given to this
808 template <typename Queue, typename Weight, typename ClassFnc>
809 class NaturalPruneQueue final
810 : public PruneQueue<Queue, NaturalLess<Weight>, ClassFnc> {
812 using StateId = typename Queue::StateId;
814 NaturalPruneQueue(const std::vector<Weight> &distance, Queue *queue,
815 const ClassFnc &class_fnc, Weight threshold)
816 : PruneQueue<Queue, NaturalLess<Weight>, ClassFnc>(
817 distance, queue, NaturalLess<Weight>(), class_fnc, threshold) {}
819 virtual ~NaturalPruneQueue() = default;
822 // Filter-based pruning queue discipline: enqueues a state only if allowed by
823 // the filter, specified by the state filter functor argument. The underlying
824 // queue discipline is specified by the queue argument. The ownership of the
825 // queue is given to this class.
826 template <typename Queue, typename Filter>
827 class FilterQueue final : public QueueBase<typename Queue::StateId> {
829 using StateId = typename Queue::StateId;
831 FilterQueue(Queue *queue, const Filter &filter)
832 : QueueBase<StateId>(OTHER_QUEUE), queue_(queue), filter_(filter) {}
834 virtual ~FilterQueue() = default;
836 StateId Head() const override { return queue_->Head(); }
838 // Enqueues only if allowed by state filter.
839 void Enqueue(StateId s) override {
840 if (filter_(s)) queue_->Enqueue(s);
843 void Dequeue() override { queue_->Dequeue(); }
845 void Update(StateId s) override {}
847 bool Empty() const override { return queue_->Empty(); }
849 void Clear() override { queue_->Clear(); }
852 std::unique_ptr<Queue> queue_;
853 const Filter &filter_;
858 #endif // FST_QUEUE_H_