1
use std::{fmt::Display, io::Write, sync::Arc};
2

            
3
use crate::{Reduction, Rule};
4
use multipeek::multipeek;
5
use uniplate::Uniplate;
6

            
7
/// Returns the first result if the iterator has only one, otherwise calls `select`.
8
46
pub(crate) fn one_or_select<T, M, R>(
9
46
    select: impl Fn(&T, &mut dyn Iterator<Item = (&R, Reduction<T, M>)>) -> Option<Reduction<T, M>>,
10
46
    t: &T,
11
46
    rs: &mut dyn Iterator<Item = (&R, Reduction<T, M>)>,
12
46
) -> Option<Reduction<T, M>>
13
46
where
14
46
    T: Uniplate,
15
46
    R: Rule<T, M>,
16
46
{
17
46
    let mut rs = multipeek(rs);
18
46
    if rs.peek_nth(1).is_none() {
19
45
        return rs.next().map(|(_, r)| r);
20
1
    }
21
1
    select(t, &mut rs)
22
46
}
23

            
24
/// Returns the first available `Reduction` if there is one, otherwise returns `None`.
25
///
26
/// This is a good default selection strategy, especially when you expect only one possible result.
27
1
pub fn select_first<T, M, R>(
28
1
    _: &T,
29
1
    rs: &mut dyn Iterator<Item = (&R, Reduction<T, M>)>,
30
1
) -> Option<Reduction<T, M>>
31
1
where
32
1
    T: Uniplate,
33
1
    R: Rule<T, M>,
34
1
{
35
1
    rs.next().map(|(_, r)| r)
36
1
}
37

            
38
/// Select the first result or panic if there is more than one.
39
///
40
/// This is useful when you expect exactly one rule to be applicable in all cases.
41
pub fn select_first_or_panic<T, M, R>(
42
    t: &T,
43
    rs: &mut dyn Iterator<Item = (&R, Reduction<T, M>)>,
44
) -> Option<Reduction<T, M>>
45
where
46
    T: Uniplate + Display,
47
    R: Rule<T, M>,
48
{
49
    let mut rs = multipeek(rs);
50
    if rs.peek_nth(1).is_some() {
51
        // TODO (Felix) Log list of rules
52
        panic!("Multiple rules applicable to expression \"{}\"", t);
53
    }
54
    rs.next().map(|(_, r)| r)
55
}
56

            
57
macro_rules! select_prompt {
58
    () => {
59
        "--- Current Expression ---
60
{}
61

            
62
--- Rules ---
63
{}
64

            
65
---
66
q   No change
67
<n> Apply rule n
68

            
69
:"
70
    };
71
}
72

            
73
pub fn select_user_input<T, M, R>(
74
    t: &T,
75
    rs: &mut dyn Iterator<Item = (&R, Reduction<T, M>)>,
76
) -> Option<Reduction<T, M>>
77
where
78
    T: Uniplate + Display,
79
    R: Rule<T, M> + Display,
80
{
81
    let mut choices: Vec<_> = rs.collect();
82

            
83
    let rules = choices
84
        .iter()
85
        .enumerate()
86
        .map(|(i, (r, Reduction { new_tree, .. }))| {
87
            format!(
88
                "{}. {}
89
   ~> {}",
90
                i + 1,
91
                r,
92
                new_tree
93
            )
94
        })
95
        .collect::<Vec<_>>()
96
        .join("\n\n");
97

            
98
    loop {
99
        print!(select_prompt!(), t, rules);
100
        std::io::stdout().flush().unwrap(); // Print the : on same line
101

            
102
        let mut line = String::new();
103
        std::io::stdin().read_line(&mut line).unwrap();
104

            
105
        match line.trim() {
106
            "q" => return None,
107
            n => {
108
                if let Ok(n) = n.parse::<usize>() {
109
                    if n > 0 && n <= choices.len() {
110
                        let ret = choices.swap_remove(n - 1).1;
111
                        return Some(ret);
112
                    }
113
                }
114
            }
115
        }
116
    }
117
}
118

            
119
/// Selects a random `Reduction` from the iterator.
120
pub fn select_random<T, M, R>(
121
    _: &T,
122
    rs: &mut dyn Iterator<Item = (&R, Reduction<T, M>)>,
123
) -> Option<Reduction<T, M>>
124
where
125
    T: Uniplate,
126
    R: Rule<T, M>,
127
{
128
    use rand::seq::IteratorRandom;
129
    let mut rng = rand::thread_rng();
130
    rs.choose(&mut rng).map(|(_, r)| r)
131
}
132

            
133
/// Selects the `Reduction` which results in the smallest subtree.
134
///
135
/// Subtree size is determined by maximum depth.
136
/// Among trees with the same depth, the first in the iterator order is selected.
137
pub fn select_smallest_subtree<T, M, R>(
138
    _: &T,
139
    rs: &mut dyn Iterator<Item = (&R, Reduction<T, M>)>,
140
) -> Option<Reduction<T, M>>
141
where
142
    T: Uniplate,
143
    R: Rule<T, M>,
144
{
145
    rs.min_by_key(|(_, r)| {
146
        r.new_tree.cata(Arc::new(|_, cs: Vec<i32>| {
147
            // Max subtree height + 1
148
            cs.iter().max().unwrap_or(&0) + 1
149
        }))
150
    })
151
    .map(|(_, r)| r)
152
}