suiron/
solutions.rs

1//! Functions which search for and format solutions.
2
3use std::env;
4use std::rc::Rc;
5use std::cell::RefCell;
6
7use super::goal::Goal;
8use super::time_out::*;
9use super::solution_node::*;
10use super::unifiable::Unifiable;
11
12const S_TIMEOUT: u64 = 1000; // milliseconds
13const NO_MORE: &str = "No more.";
14
15/// Finds one solution for the given solution node.
16///
17/// # Arguments
18/// * `sn` - reference to a [SolutionNode](../solution_node/struct.SolutionNode.html)
19/// # Return
20/// * `solution` - String
21/// # Usage
22/// ```
23/// use std::rc::Rc;
24/// use suiron::*;
25///
26/// let mut kb = test_kb();
27/// let query = parse_query("loves($Who, $Whom)").unwrap();
28/// let sn = make_base_node(Rc::new(query), &kb); // solution node
29///
30/// println!("{}", solve(Rc::clone(&sn)));
31/// // Prints: $Who = Leonard, $Whom = Penny
32/// println!("{}", solve(Rc::clone(&sn)));
33/// // Prints: $Who = Penny, $Whom = Leonard
34/// println!("{}", solve(Rc::clone(&sn)));
35/// // Prints: No.
36/// ```
37pub fn solve<'a>(sn: Rc<RefCell<SolutionNode<'a>>>) -> String {
38
39    let timer = start_query_timer(S_TIMEOUT);
40
41    let solution = next_solution(Rc::clone(&sn));
42    cancel_timer(timer);
43
44    if query_stopped() {
45        return format!("Query timed out after {} \
46                        milliseconds.", S_TIMEOUT);
47    }
48
49    match solution {
50        Some(ss) => {
51            let query = sn.borrow().goal.clone();
52            let result = query.replace_variables(&ss);
53            return format_solution(&query, &result);
54        },
55        None => { return NO_MORE.to_string(); },
56    } // match solution
57
58} // solve()
59
60/// Finds all solutions for the given query.
61///
62/// # Arguments
63/// * `sn` - reference to a [SolutionNode](../solution_node/struct.SolutionNode.html)
64/// # Return
65/// * `solutions` - vector of Strings
66/// # Usage
67/// ```
68/// use std::rc::Rc;
69/// use suiron::*;
70///
71/// let mut kb = test_kb();
72/// let query = parse_query("loves($Who, $Whom)").unwrap();
73/// let sn = make_base_node(Rc::new(query), &kb); // solution node
74///
75/// let results = solve_all(Rc::clone(&sn));
76/// for result in results { println!("{}", result); }
77/// // Prints:
78/// // $Who = Leonard, $Whom = Penny
79/// // $Who = Penny, $Whom = Leonard
80/// // No.
81/// ```
82pub fn solve_all<'a>(sn: Rc<RefCell<SolutionNode<'a>>>) -> Vec<String> {
83
84    let mut results: Vec<String> = vec![];
85
86    let query = sn.borrow().goal.clone();
87    let timer = start_query_timer(S_TIMEOUT);
88
89    loop {
90
91        let solution = next_solution(Rc::clone(&sn));
92        if query_stopped() { break; }
93
94        match solution {
95            Some(ss) => {
96                let result = query.replace_variables(&ss);
97                let s = format_solution(&query, &result);
98                results.push(s);
99            },
100            None => { break; }
101        } // match solution
102
103    } // loop
104
105    cancel_timer(timer);
106    if query_stopped() {
107        let s = format!("Query timed out after {} milliseconds.", S_TIMEOUT);
108        results.push(s);
109    }
110
111    return results;
112
113} // solve_all()
114
115/// Formats the results of a query for display.
116///
117/// For example, if the query were `loves(Leonard, $Whom)`, and
118/// the result were `loves(Leonard, Penny)`, then the function
119/// would return: `$Whom = Penny`
120///
121/// This function iterates through the query's terms. For every
122/// logic variable in the query, it prints the variable's name
123/// and the corresponding term from the result.
124///
125/// # Arguments
126/// * query - [Goal](../goal/enum.Goal.html)
127/// * [Unifiable](../unifiable/enum.Unifiable.html) term
128/// # Return
129/// * formatted solution
130/// # Usage
131/// ```
132/// use std::rc::Rc;
133/// use suiron::*;
134///
135/// let kb = test_kb();
136/// let query = parse_query("loves(Leonard, $Whom)").unwrap();
137/// let q = Rc::new(query);
138/// let sn = make_base_node(Rc::clone(&q), &kb); // solution node
139///
140/// if let Some(ss) = next_solution(Rc::clone(&sn)) {
141///     let result = q.replace_variables(&ss);
142///     println!("{}", format_solution(&q, &result));
143/// }
144/// // Prints: $Whom = Penny
145/// ```
146pub fn format_solution(query: &Goal, result: &Unifiable) -> String {
147    let mut out = "".to_string();
148    // Deconstruct the query.
149    if let Goal::ComplexGoal(Unifiable::SComplex(q_terms)) = query {
150        // Deconstruct the result.
151        if let Unifiable::SComplex(r_terms) = result {
152            let mut first = true;
153            for i in 1..q_terms.len() {
154                // Scan for logic variables.
155                match &q_terms[i] {
156                    Unifiable::LogicVar{id: _, name} => {
157                        // Output logic variable name and result.
158                        if first {
159                            out += &format!("{} = {}", name, r_terms[i]);
160                            first = false;
161                        }
162                        else {
163                            out += &format!(", {} = {}", name, r_terms[i]);
164                        }
165                    },
166                    _ => {},
167                } // match
168            } // for
169        } // if
170    } // if
171    return out;
172} // format_solution()
173
174
175/// Gets the environment variable RUST_MIN_STACK.
176///
177/// If RUST_MIN_STACK is not set, returns 0.
178///
179/// # Return
180/// * stack size (i32)
181pub fn get_stack_size() -> i32 {
182    let rust_min_stack = match env::var("RUST_MIN_STACK") {
183        Ok(val) => val,
184        Err(_e) => "0".to_string(),
185    };
186    match rust_min_stack.parse() {
187        Ok(i)  => { return i; },
188        Err(_) => { return 0; },
189    }
190} // get_stack_size()
191
192
193#[cfg(test)]
194mod test {
195
196    use std::rc::Rc;
197    use serial_test::serial;
198
199    use crate::*;
200    use super::*;
201
202    #[test]
203    #[serial]
204    fn test_format_solution() {
205
206        let kb = test_kb();
207        let query = parse_query("loves($Who, $Whom)").unwrap();
208        let q = Rc::new(query.clone());
209        let sn = make_base_node(q, &kb); // solution node
210
211        let mut ss = next_solution(Rc::clone(&sn));
212
213        match ss {
214            Some(ss2) => {
215                let result = query.replace_variables(&ss2);
216                let result = format_solution(&query, &result);
217                assert_eq!("$Who = Leonard, $Whom = Penny", result);
218                ss = next_solution(Rc::clone(&sn));
219            },
220            None => {
221                panic!("Missing Solution 1.");
222            },
223        }
224
225        match ss {
226            Some(ss2) => {
227                let result = query.replace_variables(&ss2);
228                let result = format_solution(&query, &result);
229                assert_eq!("$Who = Penny, $Whom = Leonard", result);
230                ss = next_solution(Rc::clone(&sn));
231            },
232            None => {
233                panic!("Missing Solution 2.");
234            },
235        }
236
237        match ss {
238            Some(_) => { panic!("The solutions should be exhausted."); },
239            None => {},
240        }
241
242    } // test_format_solution()
243
244    #[test]
245    #[serial]
246    fn test_solve() {
247
248        clear_id();
249        let kb = test_kb();
250        let query = parse_query("loves(Leonard, $Whom)").unwrap();
251        let sn = make_base_node(Rc::new(query), &kb); // solution node
252        let solution = solve(Rc::clone(&sn));
253        assert_eq!("$Whom = Penny", solution);
254
255    } // test_solve()
256
257    #[test]
258    #[serial]
259    fn test_solve_all() {
260
261        clear_id();
262        let kb = test_kb();
263        let query = parse_query("loves($Who, $Whom)").unwrap();
264        let sn = make_base_node(Rc::new(query), &kb); // solution node
265        let results = solve_all(Rc::clone(&sn));
266
267        assert_eq!(results.len(), 2, "There should be 2 solutions.");
268        assert_eq!("$Who = Leonard, $Whom = Penny", results[0]);
269        assert_eq!("$Who = Penny, $Whom = Leonard", results[1]);
270
271    } // test_solve_all()
272
273    // NOTE: The default stack for Rust is 2 MB.
274    // The following test contains an endless loop, which will
275    // overrun the stack before timeout, if the stack size is
276    // not increased. See: RUST_MIN_STACK in test.
277    // ALSO NOTE: None of this works.
278    /*
279    #[test]
280    #[serial]
281    fn test_solve_timeout() {
282
283        let stack_size = get_stack_size();
284        if stack_size < 8000000 { return; }
285
286        let mut kb = KnowledgeBase::new();
287
288        // Make an infinite loop rule.
289        let rule = parse_rule("loop :- loop.").unwrap();
290        add_rules!(&mut kb, rule);
291
292        let query = parse_query("loop").unwrap();
293        let sn = query.base_node(&kb); // solution node
294
295        let actual = solve(Rc::clone(&sn));
296        let expected = format!("Query timed out after {} milliseconds.", S_TIMEOUT);
297        assert_eq!(expected, actual);
298
299    } // test_solve_timeout()
300    */
301
302    // NOTE: The default stack for Rust is 2 MB.
303    // The following test contains an endless loop, which will
304    // overrun the stack before timeout, if the stack size is
305    // not increased. See: RUST_MIN_STACK in test.
306    // ALSO NOTE: None of this works.
307    /*
308    #[test]
309    #[serial]
310    fn test_solve_all_timeout() {
311
312        let stack_size = get_stack_size();
313        if stack_size < 8000000 { return; }
314
315        let mut kb = KnowledgeBase::new();
316
317        // Make an infinite loop rule.
318        let rule = parse_rule("loop :- loop.").unwrap();
319        add_rules!(&mut kb, rule);
320
321        let query = parse_query("loop").unwrap();
322        let sn = query.base_node(&kb); // solution node
323
324        let actual = solve_all(Rc::clone(&sn));
325        let expected = format!("Query timed out after {} milliseconds.", S_TIMEOUT);
326        assert_eq!(expected, actual[0]);
327
328    } // test_solve_all_timeout()
329    */
330
331} // test