1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
//! Private module for selective re-export.

use crate::{fingerprint, Fingerprint, Model};
use std::collections::VecDeque;
use std::fmt::{Debug, Display, Formatter};
use std::hash::Hash;

/// A path of states including actions. i.e. `state --action--> state ... --action--> state`.
///
/// You can convert to a `Vec<_>` with [`path.into_vec()`]. If you only need the actions, then use
/// [`path.into_actions()`].
///
/// [`path.into_vec()`]: Path::into_vec
/// [`path.into_actions()`]: Path::into_actions
#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct Path<State, Action>(Vec<(State, Option<Action>)>);

impl<State, Action> Path<State, Action> {
    /// Constructs a path from a model and a sequence of fingerprints.
    pub(crate) fn from_fingerprints<M>(model: &M, mut fingerprints: VecDeque<Fingerprint>) -> Self
    where
        M: Model<State = State, Action = Action>,
        M::State: Hash,
    {
        // FIXME: Don't panic. Return a `Result` so the details can be bubbled up to Explorer/etc.
        //        Also serialize the states rather than printing the fingerprints.
        let init_print = match fingerprints.pop_front() {
            Some(init_print) => init_print,
            None => panic!("empty path is invalid"),
        };
        let mut last_state = model
            .init_states()
            .into_iter()
            .find(|s| fingerprint(&s) == init_print)
            .unwrap_or_else(|| {
                panic!(
                    r#"
Unable to reconstruct a `Path` based on digests ("fingerprints") from states visited earlier. No
init state has the expected fingerprint ({:?}). This usually happens when the return value of
`Model::init_states` varies.

The most obvious cause would be a model that operates directly upon untracked external state such
as the file system, a thread local `RefCell`, or a source of randomness. Note that this is often
inadvertent. For example, iterating over a `HashMap` or `HashableHashMap` does not always happen in
the same order (depending on the random seed), which can lead to unexpected nondeterminism.

Available init fingerprints (none of which match): {:?}"#,
                    init_print,
                    model
                        .init_states()
                        .into_iter()
                        .map(|s| fingerprint(&s))
                        .collect::<Vec<_>>()
                );
            });
        let mut output = Vec::new();
        while let Some(next_fp) = fingerprints.pop_front() {
            let (action, next_state) = model
                .next_steps(&last_state)
                .into_iter()
                .find_map(|(a, s)| {
                    if fingerprint(&s) == next_fp {
                        Some((a, s))
                    } else {
                        None
                    }
                })
                .unwrap_or_else(|| {
                    panic!(
                        r#"
Unable to reconstruct a `Path` based on digests ("fingerprints") from states visited earlier. {}
previous state(s) of the path were able to be reconstructed, but no subsequent state has the next
fingerprint ({:?}). This usually happens when `Model::actions` or `Model::next_state` vary even
when given the same input arguments.

The most obvious cause would be a model that operates directly upon untracked external state such
as the file system, a thread local `RefCell`, or a source of randomness. Note that this is often
inadvertent. For example, iterating over a `HashMap` or `HashableHashMap` does not always happen in
the same order (depending on the random seed), which can lead to unexpected nondeterminism.

Available next fingerprints (none of which match): {:?}"#,
                        1 + output.len(),
                        next_fp,
                        model
                            .next_states(&last_state)
                            .into_iter()
                            .map(|s| fingerprint(&s))
                            .collect::<Vec<_>>()
                    );
                });
            output.push((last_state, Some(action)));

            last_state = next_state;
        }
        output.push((last_state, None));
        Path(output)
    }

    /// Constructs a path from a model, initial state, and a sequence of actions. Panics for inputs
    /// unreachable via the model.
    pub fn from_actions<'a, M>(
        model: &M,
        init_state: State,
        actions: impl IntoIterator<Item = &'a Action>,
    ) -> Option<Self>
    where
        M: Model<State = State, Action = Action>,
        State: PartialEq,
        Action: PartialEq + 'a,
    {
        let mut output = Vec::new();
        if !model.init_states().contains(&init_state) {
            return None;
        }
        let mut prev_state = init_state;
        for action in actions {
            let (action, next_state) = match model
                .next_steps(&prev_state)
                .into_iter()
                .find(|(a, _)| a == action)
            {
                None => return None,
                Some(found) => found,
            };
            output.push((prev_state, Some(action)));
            prev_state = next_state;
        }
        output.push((prev_state, None));

        Some(Path(output))
    }

    /// Determines the final state associated with a particular fingerprint path.
    pub(crate) fn final_state<M>(
        model: &M,
        mut fingerprints: VecDeque<Fingerprint>,
    ) -> Option<M::State>
    where
        M: Model<State = State, Action = Action>,
        M::State: Hash,
    {
        let init_print = match fingerprints.pop_front() {
            Some(init_print) => init_print,
            None => return None,
        };
        let mut matching_state = match model
            .init_states()
            .into_iter()
            .find(|s| fingerprint(&s) == init_print)
        {
            Some(matching_state) => matching_state,
            None => return None,
        };
        while let Some(next_print) = fingerprints.pop_front() {
            matching_state = match model
                .next_states(&matching_state)
                .into_iter()
                .find(|s| fingerprint(&s) == next_print)
            {
                Some(matching_state) => matching_state,
                None => return None,
            };
        }
        Some(matching_state)
    }

    /// Extracts the last state.
    pub fn last_state(&self) -> &State {
        &self.0.last().unwrap().0
    }

    /// Extracts the states.
    pub fn into_states(self) -> Vec<State> {
        self.0.into_iter().map(|(s, _a)| s).collect()
    }

    /// Extracts the actions.
    pub fn into_actions(self) -> Vec<Action> {
        self.0.into_iter().filter_map(|(_s, a)| a).collect()
    }

    /// Convenience method for `Into<Vec<_>>`.
    pub fn into_vec(self) -> Vec<(State, Option<Action>)> {
        self.into()
    }

    /// Encodes the path as a sequence of opaque "fingerprints" delimited by forward
    /// slash (`/`) characters.
    pub fn encode(&self) -> String
    where
        State: Hash,
    {
        self.0
            .iter()
            .map(|(s, _a)| format!("{}", fingerprint(s)))
            .collect::<Vec<String>>()
            .join("/")
    }
}

impl<State, Action> From<Path<State, Action>> for Vec<(State, Option<Action>)> {
    fn from(source: Path<State, Action>) -> Self {
        source.0
    }
}

impl<State, Action> Display for Path<State, Action>
where
    Action: Debug,
    State: Debug,
{
    fn fmt(&self, f: &mut Formatter<'_>) -> std::result::Result<(), std::fmt::Error> {
        writeln!(f, "Path[{}]:", self.0.len() - 1)?;
        for (_state, action) in &self.0 {
            if let Some(action) = action {
                writeln!(f, "- {:?}", action)?;
            }
        }
        Ok(())
    }
}

#[cfg(test)]
mod test {
    use super::*;
    use std::iter::FromIterator;
    use std::panic::catch_unwind;

    #[test]
    fn panics_if_unable_to_reconstruct_init_state() {
        let model: fn(Option<&_>, &mut Vec<_>) = |prev_state, next_states| {
            if prev_state.is_none() {
                next_states.push("UNEXPECTED");
            }
        };
        let err_result = catch_unwind(|| {
            Path::from_fingerprints(&model, VecDeque::from_iter(vec![fingerprint(&"expected")]));
        });
        assert!(err_result.is_err());
    }

    #[test]
    fn panics_if_unable_to_reconstruct_next_state() {
        let model: fn(Option<&_>, &mut Vec<_>) = |prev_state, next_states| match prev_state {
            None => next_states.push("expected"),
            Some(_) => next_states.push("UNEXPECTED"),
        };
        let err_result = catch_unwind(|| {
            Path::from_fingerprints(
                &model,
                VecDeque::from_iter(vec![fingerprint(&"expected"), fingerprint(&"expected")]),
            );
        });
        assert!(err_result.is_err());
    }
}