1
// There are three letter x, y, and z, which are each integers between 1 and 3 inclusive, where x + y = z
2

            
3
use std::collections::HashMap;
4
use std::sync::Mutex;
5

            
6
use minion_sys::ast::{Constant, Constraint, Model, Var, VarDomain, VarName};
7
use minion_sys::error::MinionError;
8
use minion_sys::get_from_table;
9

            
10
#[test]
11
#[allow(clippy::panic_in_result_fn)]
12
2
fn test_table_constraint() -> Result<(), MinionError> {
13
2
    let mut model = Model::new();
14

            
15
    // Declares variables (x, y, z), explicitly setting the integers to be between 1 and 3
16
2
    model
17
2
        .named_variables
18
2
        .add_var(String::from("x"), VarDomain::Discrete(1, 3));
19
2
    model
20
2
        .named_variables
21
2
        .add_var(String::from("y"), VarDomain::Discrete(1, 3));
22
2
    model
23
2
        .named_variables
24
2
        .add_var(String::from("z"), VarDomain::Discrete(1, 3));
25

            
26
    // Defines the table data (a list of tuples)
27
2
    let table_data = vec![
28
2
        vec![
29
2
            Constant::Integer(1),
30
2
            Constant::Integer(1),
31
2
            Constant::Integer(2),
32
        ],
33
2
        vec![
34
2
            Constant::Integer(1),
35
2
            Constant::Integer(2),
36
2
            Constant::Integer(3),
37
        ],
38
2
        vec![
39
2
            Constant::Integer(2),
40
2
            Constant::Integer(1),
41
2
            Constant::Integer(3),
42
        ],
43
    ];
44
    // Builds the Table constraint
45
2
    let vars = vec![
46
2
        Var::NameRef(String::from("x")),
47
2
        Var::NameRef(String::from("y")),
48
2
        Var::NameRef(String::from("z")),
49
    ];
50

            
51
2
    model.constraints.push(Constraint::Table(vars, table_data));
52

            
53
    // Runs the solver via the Minion interface
54
2
    minion_sys::run_minion(model, callback)?;
55

            
56
    // Asserts that we found exactly 3 solutions (one for each row in the table)
57
2
    let guard = SOLS_COUNTER.lock().unwrap();
58
2
    assert_eq!(*guard, 3);
59
2
    assert_ne!(get_from_table("Nodes".into()), None);
60
2
    Ok(())
61
2
}
62

            
63
// Global thread safe counter to store the number of solutions found
64
static SOLS_COUNTER: Mutex<u32> = Mutex::new(0);
65
// Callback function increments the counter for every solution
66
6
fn callback(_: HashMap<VarName, Constant>) -> bool {
67
    #[allow(clippy::unwrap_used)]
68
6
    let mut guard = SOLS_COUNTER.lock().unwrap();
69
6
    *guard += 1;
70
6
    true
71
6
}