1
// There are three letter x, y, and z, which are each integers between 0 and 1 inclusive, where its binary and accepts every binary triple except all zeros and all ones
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
3
fn test_negative_table_constraint() -> Result<(), MinionError> {
13
3
    let mut model = Model::new();
14

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

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

            
46
3
    model
47
3
        .constraints
48
3
        .push(Constraint::NegativeTable(vars, forbidden_table_data));
49

            
50
    // Runs the solver via the Minion interface
51
3
    minion_sys::run_minion(model, callback)?;
52

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

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