rustfst/fst_properties/
mutate_properties.rs

1use crate::algorithms::ProjectType;
2use crate::fst_properties::FstProperties;
3use crate::semirings::Semiring;
4use crate::{Label, Tr};
5use crate::{StateId, EPS_LABEL};
6
7pub fn set_start_properties(inprops: FstProperties) -> FstProperties {
8    let mut outprops = inprops & FstProperties::set_start_properties();
9    if inprops.contains(FstProperties::ACYCLIC) {
10        outprops |= FstProperties::INITIAL_ACYCLIC;
11    }
12    outprops
13}
14
15pub fn set_final_properties<W: Semiring>(
16    inprops: FstProperties,
17    old_weight: Option<&W>,
18    new_weight: Option<&W>,
19) -> FstProperties {
20    let mut outprops = inprops;
21    if let Some(w) = old_weight {
22        if !w.is_zero() && !w.is_one() {
23            outprops &= !FstProperties::WEIGHTED;
24        }
25    }
26
27    if let Some(w) = new_weight {
28        if !w.is_zero() && !w.is_one() {
29            outprops |= FstProperties::WEIGHTED;
30            outprops &= !FstProperties::UNWEIGHTED;
31        }
32    }
33
34    outprops &=
35        FstProperties::set_final_properties() | FstProperties::WEIGHTED | FstProperties::UNWEIGHTED;
36    outprops
37}
38
39pub fn add_state_properties(inprops: FstProperties) -> FstProperties {
40    inprops & FstProperties::add_state_properties()
41}
42
43pub fn add_tr_properties<W: Semiring>(
44    inprops: FstProperties,
45    state: StateId,
46    tr: &Tr<W>,
47    prev_tr: Option<&Tr<W>>,
48) -> FstProperties {
49    let mut outprops = inprops;
50
51    if tr.ilabel != tr.olabel {
52        outprops |= FstProperties::NOT_ACCEPTOR;
53        outprops &= !FstProperties::ACCEPTOR;
54    }
55    if tr.ilabel == EPS_LABEL {
56        outprops |= FstProperties::I_EPSILONS;
57        outprops &= !FstProperties::NO_I_EPSILONS;
58        if tr.olabel == EPS_LABEL {
59            outprops |= FstProperties::EPSILONS;
60            outprops &= !FstProperties::NO_EPSILONS;
61        }
62    }
63    if tr.olabel == EPS_LABEL {
64        outprops |= FstProperties::O_EPSILONS;
65        outprops &= !FstProperties::NO_O_EPSILONS;
66    }
67    if let Some(prev_tr) = prev_tr {
68        if prev_tr.ilabel > tr.ilabel {
69            outprops |= FstProperties::NOT_I_LABEL_SORTED;
70            outprops &= !FstProperties::I_LABEL_SORTED;
71        }
72        if prev_tr.olabel > tr.olabel {
73            outprops |= FstProperties::NOT_O_LABEL_SORTED;
74            outprops &= !FstProperties::O_LABEL_SORTED;
75        }
76    }
77    if !tr.weight.is_zero() && !tr.weight.is_one() {
78        outprops |= FstProperties::WEIGHTED;
79        outprops &= !FstProperties::UNWEIGHTED;
80    }
81    if tr.nextstate <= state {
82        outprops |= FstProperties::NOT_TOP_SORTED;
83        outprops &= !FstProperties::TOP_SORTED;
84    }
85    outprops &= FstProperties::add_arc_properties()
86        | FstProperties::ACCEPTOR
87        | FstProperties::NO_EPSILONS
88        | FstProperties::NO_I_EPSILONS
89        | FstProperties::NO_O_EPSILONS
90        | FstProperties::I_LABEL_SORTED
91        | FstProperties::O_LABEL_SORTED
92        | FstProperties::UNWEIGHTED
93        | FstProperties::TOP_SORTED;
94
95    if outprops.contains(FstProperties::TOP_SORTED) {
96        outprops |= FstProperties::ACYCLIC | FstProperties::INITIAL_ACYCLIC;
97    }
98
99    outprops
100}
101
102pub fn delete_states_properties(inprops: FstProperties) -> FstProperties {
103    inprops & FstProperties::delete_states_properties()
104}
105
106pub fn delete_all_states_properties() -> FstProperties {
107    FstProperties::null_properties()
108}
109
110pub fn delete_trs_properties(inprops: FstProperties) -> FstProperties {
111    inprops & FstProperties::delete_arcs_properties()
112}
113
114pub fn closure_properties(inprops: FstProperties, delayed: bool) -> FstProperties {
115    let mut outprops =
116        (FstProperties::ACCEPTOR | FstProperties::UNWEIGHTED | FstProperties::ACCESSIBLE) & inprops;
117    if inprops.contains(FstProperties::UNWEIGHTED) {
118        outprops |= FstProperties::UNWEIGHTED_CYCLES;
119    }
120    if !delayed {
121        outprops |= (FstProperties::COACCESSIBLE
122            | FstProperties::NOT_TOP_SORTED
123            | FstProperties::NOT_STRING)
124            & inprops;
125    }
126    if !delayed || inprops.contains(FstProperties::ACCESSIBLE) {
127        outprops |= (FstProperties::NOT_ACCEPTOR
128            | FstProperties::NOT_I_DETERMINISTIC
129            | FstProperties::NOT_O_DETERMINISTIC
130            | FstProperties::NOT_I_LABEL_SORTED
131            | FstProperties::NOT_O_LABEL_SORTED
132            | FstProperties::WEIGHTED
133            | FstProperties::WEIGHTED_CYCLES
134            | FstProperties::NOT_ACCESSIBLE
135            | FstProperties::NOT_COACCESSIBLE)
136            & inprops;
137        if inprops.contains(FstProperties::WEIGHTED)
138            && inprops.contains(FstProperties::ACCESSIBLE)
139            && inprops.contains(FstProperties::COACCESSIBLE)
140        {
141            outprops |= FstProperties::WEIGHTED_CYCLES;
142        }
143    }
144    outprops
145}
146
147pub fn complement_properties(_inprops: FstProperties) -> FstProperties {
148    unimplemented!()
149}
150
151pub fn compose_properties(inprops1: FstProperties, inprops2: FstProperties) -> FstProperties {
152    let mut outprops = FstProperties::empty();
153    if inprops1.contains(FstProperties::ACCEPTOR) && inprops2.contains(FstProperties::ACCEPTOR) {
154        outprops |= FstProperties::ACCEPTOR | FstProperties::ACCESSIBLE;
155        outprops |= (FstProperties::NO_EPSILONS
156            | FstProperties::NO_I_EPSILONS
157            | FstProperties::NO_O_EPSILONS
158            | FstProperties::ACYCLIC
159            | FstProperties::INITIAL_ACYCLIC)
160            & inprops1
161            & inprops2;
162        if inprops1.contains(FstProperties::NO_I_EPSILONS)
163            && inprops2.contains(FstProperties::NO_I_EPSILONS)
164        {
165            outprops |= (FstProperties::I_DETERMINISTIC | FstProperties::O_DETERMINISTIC)
166                & inprops1
167                & inprops2;
168        }
169    } else {
170        outprops |= FstProperties::ACCESSIBLE;
171        outprops |= (FstProperties::ACCEPTOR
172            | FstProperties::NO_I_EPSILONS
173            | FstProperties::ACYCLIC
174            | FstProperties::INITIAL_ACYCLIC)
175            & inprops1
176            & inprops2;
177        if inprops1.contains(FstProperties::NO_I_EPSILONS)
178            && inprops2.contains(FstProperties::NO_I_EPSILONS)
179        {
180            outprops |= FstProperties::I_DETERMINISTIC & inprops1 & inprops2;
181        }
182    }
183    outprops
184}
185
186pub fn concat_properties(
187    inprops1: FstProperties,
188    inprops2: FstProperties,
189    delayed: bool,
190) -> FstProperties {
191    let mut outprops = (FstProperties::ACCEPTOR
192        | FstProperties::UNWEIGHTED
193        | FstProperties::UNWEIGHTED_CYCLES
194        | FstProperties::ACYCLIC)
195        & inprops1
196        & inprops2;
197    let empty1 = delayed; // Can the first FST be the empty machine?
198    let empty2 = delayed; // Can the second FST be the empty machine?
199    if !delayed {
200        outprops |= (FstProperties::NOT_TOP_SORTED | FstProperties::NOT_STRING) & inprops1;
201        outprops |= (FstProperties::NOT_TOP_SORTED | FstProperties::NOT_STRING) & inprops2;
202    }
203    if !empty1 {
204        outprops |= (FstProperties::INITIAL_ACYCLIC | FstProperties::INITIAL_CYCLIC) & inprops1;
205    }
206    if !delayed || inprops1.contains(FstProperties::ACCESSIBLE) {
207        outprops |= (FstProperties::NOT_ACCEPTOR
208            | FstProperties::NOT_I_DETERMINISTIC
209            | FstProperties::NOT_O_DETERMINISTIC
210            | FstProperties::EPSILONS
211            | FstProperties::I_EPSILONS
212            | FstProperties::O_EPSILONS
213            | FstProperties::NOT_I_LABEL_SORTED
214            | FstProperties::NOT_O_LABEL_SORTED
215            | FstProperties::WEIGHTED
216            | FstProperties::WEIGHTED_CYCLES
217            | FstProperties::CYCLIC
218            | FstProperties::NOT_ACCESSIBLE
219            | FstProperties::NOT_COACCESSIBLE)
220            & inprops1;
221    }
222    if (inprops1.contains(FstProperties::ACCESSIBLE | FstProperties::COACCESSIBLE)) && !empty1 {
223        outprops |= FstProperties::ACCESSIBLE & inprops2;
224        if !empty2 {
225            outprops |= FstProperties::COACCESSIBLE & inprops2;
226        }
227        if !delayed || inprops2.contains(FstProperties::ACCESSIBLE) {
228            outprops |= (FstProperties::NOT_ACCEPTOR
229                | FstProperties::NOT_I_DETERMINISTIC
230                | FstProperties::NOT_O_DETERMINISTIC
231                | FstProperties::EPSILONS
232                | FstProperties::I_EPSILONS
233                | FstProperties::O_EPSILONS
234                | FstProperties::NOT_I_LABEL_SORTED
235                | FstProperties::NOT_O_LABEL_SORTED
236                | FstProperties::WEIGHTED
237                | FstProperties::WEIGHTED_CYCLES
238                | FstProperties::CYCLIC
239                | FstProperties::NOT_ACCESSIBLE
240                | FstProperties::NOT_COACCESSIBLE)
241                & inprops2;
242        }
243    }
244    outprops
245}
246
247pub fn determinize_properties(
248    inprops: FstProperties,
249    has_subsequential_label: bool,
250    distinct_psubsequential_labels: bool,
251) -> FstProperties {
252    let mut outprops = FstProperties::ACCESSIBLE;
253    if inprops.contains(FstProperties::ACCEPTOR)
254        || (inprops.contains(FstProperties::NO_I_EPSILONS) && distinct_psubsequential_labels)
255        || (has_subsequential_label && distinct_psubsequential_labels)
256    {
257        outprops |= FstProperties::I_DETERMINISTIC;
258    }
259    outprops |= (FstProperties::ACCEPTOR
260        | FstProperties::ACYCLIC
261        | FstProperties::INITIAL_ACYCLIC
262        | FstProperties::COACCESSIBLE
263        | FstProperties::STRING)
264        & inprops;
265    if inprops.contains(FstProperties::NO_I_EPSILONS) && distinct_psubsequential_labels {
266        outprops |= FstProperties::NO_EPSILONS & inprops;
267    }
268    if inprops.contains(FstProperties::ACCESSIBLE) {
269        outprops |= (FstProperties::I_EPSILONS | FstProperties::O_EPSILONS | FstProperties::CYCLIC)
270            & inprops;
271    }
272    if inprops.contains(FstProperties::ACCEPTOR) {
273        outprops |= (FstProperties::NO_I_EPSILONS | FstProperties::NO_O_EPSILONS) & inprops;
274    }
275    if inprops.contains(FstProperties::NO_I_EPSILONS) && has_subsequential_label {
276        outprops |= FstProperties::NO_I_EPSILONS;
277    }
278    outprops
279}
280
281pub fn factor_weight_properties(inprops: FstProperties) -> FstProperties {
282    let mut outprops = (FstProperties::ACCEPTOR
283        | FstProperties::ACYCLIC
284        | FstProperties::ACCESSIBLE
285        | FstProperties::COACCESSIBLE)
286        & inprops;
287    if inprops.contains(FstProperties::ACCESSIBLE) {
288        outprops |= (FstProperties::NOT_ACCEPTOR
289            | FstProperties::NOT_I_DETERMINISTIC
290            | FstProperties::NOT_O_DETERMINISTIC
291            | FstProperties::EPSILONS
292            | FstProperties::I_EPSILONS
293            | FstProperties::O_EPSILONS
294            | FstProperties::CYCLIC
295            | FstProperties::NOT_I_LABEL_SORTED
296            | FstProperties::NOT_O_LABEL_SORTED)
297            & inprops;
298    }
299    outprops
300}
301
302pub fn invert_properties(inprops: FstProperties) -> FstProperties {
303    let mut outprops = (FstProperties::ACCEPTOR
304        | FstProperties::NOT_ACCEPTOR
305        | FstProperties::EPSILONS
306        | FstProperties::NO_EPSILONS
307        | FstProperties::WEIGHTED
308        | FstProperties::UNWEIGHTED
309        | FstProperties::WEIGHTED_CYCLES
310        | FstProperties::UNWEIGHTED_CYCLES
311        | FstProperties::CYCLIC
312        | FstProperties::ACYCLIC
313        | FstProperties::INITIAL_CYCLIC
314        | FstProperties::INITIAL_ACYCLIC
315        | FstProperties::TOP_SORTED
316        | FstProperties::NOT_TOP_SORTED
317        | FstProperties::ACCESSIBLE
318        | FstProperties::NOT_ACCESSIBLE
319        | FstProperties::COACCESSIBLE
320        | FstProperties::NOT_COACCESSIBLE
321        | FstProperties::STRING
322        | FstProperties::NOT_STRING)
323        & inprops;
324    if inprops.contains(FstProperties::I_DETERMINISTIC) {
325        outprops |= FstProperties::O_DETERMINISTIC;
326    }
327    if inprops.contains(FstProperties::NOT_I_DETERMINISTIC) {
328        outprops |= FstProperties::NOT_O_DETERMINISTIC;
329    }
330    if inprops.contains(FstProperties::O_DETERMINISTIC) {
331        outprops |= FstProperties::I_DETERMINISTIC;
332    }
333    if inprops.contains(FstProperties::NOT_O_DETERMINISTIC) {
334        outprops |= FstProperties::NOT_I_DETERMINISTIC;
335    }
336
337    if inprops.contains(FstProperties::I_EPSILONS) {
338        outprops |= FstProperties::O_EPSILONS;
339    }
340    if inprops.contains(FstProperties::NO_I_EPSILONS) {
341        outprops |= FstProperties::NO_O_EPSILONS;
342    }
343    if inprops.contains(FstProperties::O_EPSILONS) {
344        outprops |= FstProperties::I_EPSILONS;
345    }
346    if inprops.contains(FstProperties::NO_O_EPSILONS) {
347        outprops |= FstProperties::NO_I_EPSILONS;
348    }
349
350    if inprops.contains(FstProperties::I_LABEL_SORTED) {
351        outprops |= FstProperties::O_LABEL_SORTED;
352    }
353    if inprops.contains(FstProperties::NOT_I_LABEL_SORTED) {
354        outprops |= FstProperties::NOT_O_LABEL_SORTED;
355    }
356    if inprops.contains(FstProperties::O_LABEL_SORTED) {
357        outprops |= FstProperties::I_LABEL_SORTED;
358    }
359    if inprops.contains(FstProperties::NOT_O_LABEL_SORTED) {
360        outprops |= FstProperties::NOT_I_LABEL_SORTED;
361    }
362    outprops
363}
364
365pub fn project_properties(inprops: FstProperties, project_type: ProjectType) -> FstProperties {
366    let mut outprops = FstProperties::ACCEPTOR;
367    outprops |= (FstProperties::WEIGHTED
368        | FstProperties::UNWEIGHTED
369        | FstProperties::WEIGHTED_CYCLES
370        | FstProperties::UNWEIGHTED_CYCLES
371        | FstProperties::CYCLIC
372        | FstProperties::ACYCLIC
373        | FstProperties::INITIAL_CYCLIC
374        | FstProperties::INITIAL_ACYCLIC
375        | FstProperties::TOP_SORTED
376        | FstProperties::NOT_TOP_SORTED
377        | FstProperties::ACCESSIBLE
378        | FstProperties::NOT_ACCESSIBLE
379        | FstProperties::COACCESSIBLE
380        | FstProperties::NOT_COACCESSIBLE
381        | FstProperties::STRING
382        | FstProperties::NOT_STRING)
383        & inprops;
384    match project_type {
385        ProjectType::ProjectInput => {
386            outprops |= (FstProperties::I_DETERMINISTIC
387                | FstProperties::NOT_I_DETERMINISTIC
388                | FstProperties::I_EPSILONS
389                | FstProperties::NO_I_EPSILONS
390                | FstProperties::I_LABEL_SORTED
391                | FstProperties::NOT_I_LABEL_SORTED)
392                & inprops;
393
394            if inprops.contains(FstProperties::I_DETERMINISTIC) {
395                outprops |= FstProperties::O_DETERMINISTIC;
396            }
397            if inprops.contains(FstProperties::NOT_I_DETERMINISTIC) {
398                outprops |= FstProperties::NOT_O_DETERMINISTIC;
399            }
400
401            if inprops.contains(FstProperties::I_EPSILONS) {
402                outprops |= FstProperties::O_EPSILONS | FstProperties::EPSILONS;
403            }
404            if inprops.contains(FstProperties::NO_I_EPSILONS) {
405                outprops |= FstProperties::NO_O_EPSILONS | FstProperties::NO_EPSILONS;
406            }
407
408            if inprops.contains(FstProperties::I_LABEL_SORTED) {
409                outprops |= FstProperties::O_LABEL_SORTED;
410            }
411            if inprops.contains(FstProperties::NOT_I_LABEL_SORTED) {
412                outprops |= FstProperties::NOT_O_LABEL_SORTED;
413            }
414        }
415        ProjectType::ProjectOutput => {
416            outprops |= (FstProperties::O_DETERMINISTIC
417                | FstProperties::NOT_O_DETERMINISTIC
418                | FstProperties::O_EPSILONS
419                | FstProperties::NO_O_EPSILONS
420                | FstProperties::O_LABEL_SORTED
421                | FstProperties::NOT_O_LABEL_SORTED)
422                & inprops;
423
424            if inprops.contains(FstProperties::O_DETERMINISTIC) {
425                outprops |= FstProperties::I_DETERMINISTIC;
426            }
427            if inprops.contains(FstProperties::NOT_O_DETERMINISTIC) {
428                outprops |= FstProperties::NOT_I_DETERMINISTIC;
429            }
430
431            if inprops.contains(FstProperties::O_EPSILONS) {
432                outprops |= FstProperties::I_EPSILONS | FstProperties::EPSILONS;
433            }
434            if inprops.contains(FstProperties::NO_O_EPSILONS) {
435                outprops |= FstProperties::NO_I_EPSILONS | FstProperties::NO_EPSILONS;
436            }
437
438            if inprops.contains(FstProperties::O_LABEL_SORTED) {
439                outprops |= FstProperties::I_LABEL_SORTED;
440            }
441            if inprops.contains(FstProperties::NOT_O_LABEL_SORTED) {
442                outprops |= FstProperties::NOT_I_LABEL_SORTED;
443            }
444        }
445    };
446    outprops
447}
448
449pub fn rand_gen_properties(inprops: FstProperties, weighted: bool) -> FstProperties {
450    let mut outprops = FstProperties::ACYCLIC
451        | FstProperties::INITIAL_ACYCLIC
452        | FstProperties::ACCESSIBLE
453        | FstProperties::UNWEIGHTED_CYCLES;
454    if weighted {
455        outprops |= FstProperties::TOP_SORTED;
456        outprops |= (FstProperties::ACCEPTOR
457            | FstProperties::NO_EPSILONS
458            | FstProperties::NO_I_EPSILONS
459            | FstProperties::NO_O_EPSILONS
460            | FstProperties::I_DETERMINISTIC
461            | FstProperties::O_DETERMINISTIC
462            | FstProperties::I_LABEL_SORTED
463            | FstProperties::O_LABEL_SORTED)
464            & inprops;
465    } else {
466        outprops |= FstProperties::UNWEIGHTED;
467        outprops |= (FstProperties::ACCEPTOR
468            | FstProperties::I_LABEL_SORTED
469            | FstProperties::O_LABEL_SORTED)
470            & inprops;
471    }
472    outprops
473}
474
475pub fn relabel_properties(inprops: FstProperties) -> FstProperties {
476    let outprops = FstProperties::WEIGHTED
477        | FstProperties::UNWEIGHTED
478        | FstProperties::WEIGHTED_CYCLES
479        | FstProperties::UNWEIGHTED_CYCLES
480        | FstProperties::CYCLIC
481        | FstProperties::ACYCLIC
482        | FstProperties::INITIAL_CYCLIC
483        | FstProperties::INITIAL_ACYCLIC
484        | FstProperties::TOP_SORTED
485        | FstProperties::NOT_TOP_SORTED
486        | FstProperties::ACCESSIBLE
487        | FstProperties::NOT_ACCESSIBLE
488        | FstProperties::COACCESSIBLE
489        | FstProperties::NOT_COACCESSIBLE
490        | FstProperties::STRING
491        | FstProperties::NOT_STRING;
492    outprops & inprops
493}
494
495#[allow(clippy::too_many_arguments)]
496pub fn replace_properties(
497    inprops: &[FstProperties],
498    root: Label,
499    epsilon_on_call: bool,
500    epsilon_on_return: bool,
501    out_epsilon_on_call: bool,
502    out_epsilon_on_return: bool,
503    replace_transducer: bool,
504    no_empty_fsts: bool,
505    all_ilabel_sorted: bool,
506    all_olabel_sorted: bool,
507    all_negative_or_dense: bool,
508) -> FstProperties {
509    if inprops.is_empty() {
510        return FstProperties::null_properties();
511    }
512    let mut outprops = FstProperties::empty();
513    let mut access_props = if no_empty_fsts {
514        FstProperties::ACCESSIBLE | FstProperties::COACCESSIBLE
515    } else {
516        FstProperties::empty()
517    };
518
519    for inprop in inprops {
520        access_props &= *inprop & (FstProperties::ACCESSIBLE | FstProperties::COACCESSIBLE);
521    }
522
523    if access_props == (FstProperties::ACCESSIBLE | FstProperties::COACCESSIBLE) {
524        outprops |= access_props;
525        if inprops[root as usize].contains(FstProperties::INITIAL_CYCLIC) {
526            outprops |= FstProperties::INITIAL_CYCLIC;
527        }
528        let mut props = FstProperties::empty();
529        let mut string = true;
530        for inprop in inprops {
531            if replace_transducer {
532                props |= FstProperties::NOT_ACCEPTOR & *inprop;
533            }
534            props |= (FstProperties::NOT_I_DETERMINISTIC
535                | FstProperties::NOT_O_DETERMINISTIC
536                | FstProperties::EPSILONS
537                | FstProperties::I_EPSILONS
538                | FstProperties::O_EPSILONS
539                | FstProperties::WEIGHTED
540                | FstProperties::WEIGHTED_CYCLES
541                | FstProperties::CYCLIC
542                | FstProperties::NOT_TOP_SORTED
543                | FstProperties::NOT_STRING)
544                & *inprop;
545            if !inprop.contains(FstProperties::STRING) {
546                string = false;
547            }
548        }
549        outprops |= props;
550        if string {
551            outprops |= FstProperties::STRING;
552        }
553    }
554    let mut acceptor = !replace_transducer;
555    let mut ideterministic = !epsilon_on_call && epsilon_on_return;
556    let mut no_iepsilons = !epsilon_on_call && !epsilon_on_return;
557    let mut acyclic = true;
558    let mut unweighted = true;
559    for (i, inprop) in inprops.iter().enumerate() {
560        if !inprop.contains(FstProperties::ACCEPTOR) {
561            acceptor = false;
562        }
563        if !inprop.contains(FstProperties::I_DETERMINISTIC) {
564            ideterministic = false;
565        }
566        if !inprop.contains(FstProperties::NO_I_EPSILONS) {
567            no_iepsilons = false;
568        }
569        if !inprop.contains(FstProperties::ACYCLIC) {
570            acyclic = false;
571        }
572        if !inprop.contains(FstProperties::UNWEIGHTED) {
573            unweighted = false;
574        }
575        if i != root as usize && !inprop.contains(FstProperties::NO_I_EPSILONS) {
576            ideterministic = false;
577        }
578    }
579    if acceptor {
580        outprops |= FstProperties::ACCEPTOR;
581    }
582    if ideterministic {
583        outprops |= FstProperties::I_DETERMINISTIC;
584    }
585    if no_iepsilons {
586        outprops |= FstProperties::NO_I_EPSILONS;
587    }
588    if acyclic {
589        outprops |= FstProperties::ACYCLIC;
590    }
591    if unweighted {
592        outprops |= FstProperties::UNWEIGHTED;
593    }
594    if inprops[root as usize].contains(FstProperties::INITIAL_ACYCLIC) {
595        outprops |= FstProperties::INITIAL_ACYCLIC;
596    }
597    // We assume that all terminals are positive. The resulting ReplaceFst is
598    // known to be FstProperties::I_LABEL_SORTED when: (1) all sub-FSTs are FstProperties::I_LABEL_SORTED, (2) the
599    // input label of the return arc is epsilon, and (3) one of the 3 following
600    // conditions is satisfied:
601    //
602    //  1. the input label of the call arc is not epsilon
603    //  2. all non-terminals are negative, or
604    //  3. all non-terninals are positive and form a dense range containing 1.
605    if all_ilabel_sorted && epsilon_on_return && (!epsilon_on_call || all_negative_or_dense) {
606        outprops |= FstProperties::I_LABEL_SORTED;
607    }
608    // Similarly, the resulting ReplaceFst is known to be FstProperties::O_LABEL_SORTED when: (1)
609    // all sub-FSTs are FstProperties::O_LABEL_SORTED, (2) the output label of the return arc is
610    // epsilon, and (3) one of the 3 following conditions is satisfied:
611    //
612    //  1. the output label of the call arc is not epsilon
613    //  2. all non-terminals are negative, or
614    //  3. all non-terninals are positive and form a dense range containing 1.
615    if all_olabel_sorted && out_epsilon_on_return && (!out_epsilon_on_call || all_negative_or_dense)
616    {
617        outprops |= FstProperties::O_LABEL_SORTED;
618    }
619    outprops
620}
621
622pub fn reverse_properties(inprops: FstProperties, has_superinitial: bool) -> FstProperties {
623    let mut outprops = (FstProperties::ACCEPTOR
624        | FstProperties::NOT_ACCEPTOR
625        | FstProperties::EPSILONS
626        | FstProperties::I_EPSILONS
627        | FstProperties::O_EPSILONS
628        | FstProperties::UNWEIGHTED
629        | FstProperties::CYCLIC
630        | FstProperties::ACYCLIC
631        | FstProperties::WEIGHTED_CYCLES
632        | FstProperties::UNWEIGHTED_CYCLES)
633        & inprops;
634    if has_superinitial {
635        outprops |= FstProperties::WEIGHTED & inprops;
636    }
637    outprops
638}
639
640pub fn reweight_properties(inprops: FstProperties) -> FstProperties {
641    let mut outprops = inprops & FstProperties::weight_invariant_properties();
642    outprops &= !FstProperties::COACCESSIBLE;
643    outprops
644}
645
646pub fn rmepsilon_properties(inprops: FstProperties, delayed: bool) -> FstProperties {
647    let mut outprops = FstProperties::NO_EPSILONS;
648    outprops |= (FstProperties::ACCEPTOR | FstProperties::ACYCLIC | FstProperties::INITIAL_ACYCLIC)
649        & inprops;
650    if inprops.contains(FstProperties::ACCEPTOR) {
651        outprops |= FstProperties::NO_I_EPSILONS | FstProperties::NO_O_EPSILONS;
652    }
653    if !delayed {
654        outprops |= FstProperties::TOP_SORTED & inprops;
655    }
656    if !delayed || inprops.contains(FstProperties::ACCESSIBLE) {
657        outprops |= FstProperties::NOT_ACCEPTOR & inprops;
658    }
659    outprops
660}
661
662pub fn shortest_path_properties(props: FstProperties, tree: bool) -> FstProperties {
663    let mut outprops = props
664        | FstProperties::ACYCLIC
665        | FstProperties::INITIAL_ACYCLIC
666        | FstProperties::ACCESSIBLE
667        | FstProperties::UNWEIGHTED_CYCLES;
668    if !tree {
669        outprops |= FstProperties::COACCESSIBLE;
670    }
671    outprops
672}
673
674pub fn synchronization_properties(inprops: FstProperties) -> FstProperties {
675    let mut outprops = (FstProperties::ACCEPTOR
676        | FstProperties::ACYCLIC
677        | FstProperties::ACCESSIBLE
678        | FstProperties::COACCESSIBLE
679        | FstProperties::UNWEIGHTED
680        | FstProperties::UNWEIGHTED_CYCLES)
681        & inprops;
682    if inprops.contains(FstProperties::ACCESSIBLE) {
683        outprops |= (FstProperties::CYCLIC
684            | FstProperties::NOT_COACCESSIBLE
685            | FstProperties::WEIGHTED
686            | FstProperties::WEIGHTED_CYCLES)
687            & inprops;
688    }
689    outprops
690}
691
692pub fn union_properties(
693    inprops1: FstProperties,
694    inprops2: FstProperties,
695    delayed: bool,
696) -> FstProperties {
697    let mut outprops = (FstProperties::ACCEPTOR
698        | FstProperties::UNWEIGHTED
699        | FstProperties::UNWEIGHTED_CYCLES
700        | FstProperties::ACYCLIC
701        | FstProperties::ACCESSIBLE)
702        & inprops1
703        & inprops2;
704    outprops |= FstProperties::INITIAL_ACYCLIC;
705    let empty1 = delayed; // Can the first FST be the empty machine?
706    let empty2 = delayed; // Can the second FST be the empty machine?
707    if !delayed {
708        outprops |= FstProperties::NOT_TOP_SORTED & inprops1;
709        outprops |= FstProperties::NOT_TOP_SORTED & inprops2;
710    }
711    if !empty1 && !empty2 {
712        outprops |= FstProperties::EPSILONS | FstProperties::I_EPSILONS | FstProperties::O_EPSILONS;
713        outprops |= FstProperties::COACCESSIBLE & inprops1 & inprops2;
714    }
715    // Note FstProperties::NOT_COACCESSIBLE does not hold because of FstProperties::INITIAL_ACYCLIC option.
716    if !delayed || inprops1.contains(FstProperties::ACCESSIBLE) {
717        outprops |= (FstProperties::NOT_ACCEPTOR
718            | FstProperties::NOT_I_DETERMINISTIC
719            | FstProperties::NOT_O_DETERMINISTIC
720            | FstProperties::EPSILONS
721            | FstProperties::I_EPSILONS
722            | FstProperties::O_EPSILONS
723            | FstProperties::NOT_I_LABEL_SORTED
724            | FstProperties::NOT_O_LABEL_SORTED
725            | FstProperties::WEIGHTED
726            | FstProperties::WEIGHTED_CYCLES
727            | FstProperties::CYCLIC
728            | FstProperties::NOT_ACCESSIBLE)
729            & inprops1;
730    }
731    if !delayed || inprops2.contains(FstProperties::ACCESSIBLE) {
732        outprops |= (FstProperties::NOT_ACCEPTOR
733            | FstProperties::NOT_I_DETERMINISTIC
734            | FstProperties::NOT_O_DETERMINISTIC
735            | FstProperties::EPSILONS
736            | FstProperties::I_EPSILONS
737            | FstProperties::O_EPSILONS
738            | FstProperties::NOT_I_LABEL_SORTED
739            | FstProperties::NOT_O_LABEL_SORTED
740            | FstProperties::WEIGHTED
741            | FstProperties::WEIGHTED_CYCLES
742            | FstProperties::CYCLIC
743            | FstProperties::NOT_ACCESSIBLE
744            | FstProperties::NOT_COACCESSIBLE)
745            & inprops2;
746    }
747    outprops
748}