1
#![allow(unreachable_patterns)]
2

            
3
use std::{
4
    collections::HashMap,
5
    ffi::CString,
6
    sync::Condvar,
7
    sync::{Mutex, MutexGuard},
8
};
9

            
10
use anyhow::anyhow;
11

            
12
use crate::ffi::{self};
13
use crate::{ast::*, error::*, scoped_ptr::Scoped};
14

            
15
/// The callback function used to capture results from Minion as they are generated.
16
///
17
/// This function is called by Minion whenever a solution is found. The input to this function is
18
/// a`HashMap` of all named variables along with their value.
19
///
20
/// Callbacks should return `true` if search is to continue, `false` otherwise.
21
///
22
/// # Examples
23
///
24
/// Consider using a global mutex (or other static variable) to use returned solutions elsewhere.
25
///
26
/// For example:
27
///
28
/// ```
29
///   use minion_rs::ast::*;
30
///   use minion_rs::run_minion;
31
///   use std::{
32
///       collections::HashMap,
33
///       sync::{Mutex, MutexGuard},
34
///   };
35
///
36
///   // More elaborate data-structures are possible, but for sake of example store
37
///   // a vector of solution sets.
38
///   static ALL_SOLUTIONS: Mutex<Vec<HashMap<VarName,Constant>>>  = Mutex::new(vec![]);
39
///   
40
///   fn callback(solutions: HashMap<VarName,Constant>) -> bool {
41
///       let mut guard = ALL_SOLUTIONS.lock().unwrap();
42
///       guard.push(solutions);
43
///       true
44
///   }
45
///    
46
///   // Build and run the model.
47
///   let mut model = Model::new();
48
///
49
///   // ... omitted for brevity ...
50
/// # model
51
/// #     .named_variables
52
/// #     .add_var("x".to_owned(), VarDomain::Bound(1, 3));
53
/// # model
54
/// #     .named_variables
55
/// #     .add_var("y".to_owned(), VarDomain::Bound(2, 4));
56
/// # model
57
/// #     .named_variables
58
/// #     .add_var("z".to_owned(), VarDomain::Bound(1, 5));
59
/// #
60
/// # let leq = Constraint::SumLeq(
61
/// #     vec![
62
/// #         Var::NameRef("x".to_owned()),
63
/// #         Var::NameRef("y".to_owned()),
64
/// #         Var::NameRef("z".to_owned()),
65
/// #     ],
66
/// #     Var::ConstantAsVar(4),
67
/// # );
68
/// #
69
/// # let geq = Constraint::SumGeq(
70
/// #     vec![
71
/// #         Var::NameRef("x".to_owned()),
72
/// #         Var::NameRef("y".to_owned()),
73
/// #         Var::NameRef("z".to_owned()),
74
/// #     ],
75
/// #     Var::ConstantAsVar(4),
76
/// # );
77
/// #
78
/// # let ineq = Constraint::Ineq(
79
/// #     Var::NameRef("x".to_owned()),
80
/// #     Var::NameRef("y".to_owned()),
81
/// #     Constant::Integer(-1),
82
/// # );
83
/// #
84
/// # model.constraints.push(leq);
85
/// # model.constraints.push(geq);
86
/// # model.constraints.push(ineq);
87
///  
88
///   let res = run_minion(model, callback);
89
///   res.expect("Error occurred");
90
///
91
///   // Get solutions
92
///   let guard = ALL_SOLUTIONS.lock().unwrap();
93
///   let solution_set_1 = &(guard.get(0).unwrap());
94
///
95
///   let x1 = solution_set_1.get("x").unwrap();
96
///   let y1 = solution_set_1.get("y").unwrap();
97
///   let z1 = solution_set_1.get("z").unwrap();
98
/// #
99
/// # // TODO: this test would be better with an example with >1 solution.
100
/// # assert_eq!(guard.len(),1);
101
/// # assert_eq!(*x1,Constant::Integer(1));
102
/// # assert_eq!(*y1,Constant::Integer(2));
103
/// # assert_eq!(*z1,Constant::Integer(1));
104
/// ```
105
pub type Callback = fn(solution_set: HashMap<VarName, Constant>) -> bool;
106

            
107
// Use globals to pass things between run_minion and the callback function.
108
// Minion is (currently) single threaded anyways so the Mutexs' don't matter.
109

            
110
// the current callback function
111
static CALLBACK: Mutex<Option<Callback>> = Mutex::new(None);
112

            
113
// the variables we want to return, and their ordering in the print matrix
114
static PRINT_VARS: Mutex<Option<Vec<VarName>>> = Mutex::new(None);
115

            
116
static LOCK: (Mutex<bool>, Condvar) = (Mutex::new(false), Condvar::new());
117

            
118
#[no_mangle]
119
unsafe extern "C" fn run_callback() -> bool {
120
    // get printvars from static PRINT_VARS if they exist.
121
    // if not, return true and continue search.
122

            
123
    // Mutex poisoning is probably panic worthy.
124
    #[allow(clippy::unwrap_used)]
125
    let mut guard: MutexGuard<'_, Option<Vec<VarName>>> = PRINT_VARS.lock().unwrap();
126

            
127
    if guard.is_none() {
128
        return true;
129
    }
130

            
131
    let print_vars = match &mut *guard {
132
        Some(x) => x,
133
        None => unreachable!(),
134
    };
135

            
136
    if print_vars.is_empty() {
137
        return true;
138
    }
139

            
140
    // build nice solutions view to be used by callback
141
    let mut solutions: HashMap<VarName, Constant> = HashMap::new();
142

            
143
    for (i, var) in print_vars.iter().enumerate() {
144
        let solution_int: i32 = ffi::printMatrix_getValue(i as _);
145
        let solution: Constant = Constant::Integer(solution_int);
146
        solutions.insert(var.to_string(), solution);
147
    }
148

            
149
    #[allow(clippy::unwrap_used)]
150
    match *CALLBACK.lock().unwrap() {
151
        None => true,
152
        Some(func) => func(solutions),
153
    }
154
}
155

            
156
/// Run Minion on the given [Model].
157
///
158
/// The given [callback](Callback) is ran whenever a new solution set is found.
159

            
160
// Turn it into a warning for this function, cant unwarn it directly above callback wierdness
161
#[allow(clippy::unwrap_used)]
162
pub fn run_minion(model: Model, callback: Callback) -> Result<(), MinionError> {
163
    // Mutex poisoning is probably panic worthy.
164
    *CALLBACK.lock().unwrap() = Some(callback);
165

            
166
    let (lock, condvar) = &LOCK;
167
    let mut _lock_guard = condvar
168
        .wait_while(lock.lock().unwrap(), |locked| *locked)
169
        .unwrap();
170

            
171
    *_lock_guard = true;
172

            
173
    unsafe {
174
        // TODO: something better than a manual spinlock
175
        let search_opts = ffi::searchOptions_new();
176
        let search_method = ffi::searchMethod_new();
177
        let search_instance = ffi::instance_new();
178

            
179
        convert_model_to_raw(search_instance, &model)?;
180

            
181
        let res = ffi::runMinion(
182
            search_opts,
183
            search_method,
184
            search_instance,
185
            Some(run_callback),
186
        );
187

            
188
        ffi::searchMethod_free(search_method);
189
        ffi::searchOptions_free(search_opts);
190
        ffi::instance_free(search_instance);
191

            
192
        *_lock_guard = false;
193
        std::mem::drop(_lock_guard);
194

            
195
        condvar.notify_one();
196

            
197
        match res {
198
            0 => Ok(()),
199
            x => Err(MinionError::from(RuntimeError::from(x))),
200
        }
201
    }
202
}
203

            
204
unsafe fn convert_model_to_raw(
205
    instance: *mut ffi::ProbSpec_CSPInstance,
206
    model: &Model,
207
) -> Result<(), MinionError> {
208
    /*******************************/
209
    /*        Add variables        */
210
    /*******************************/
211

            
212
    /*
213
     * Add variables to:
214
     * 1. symbol table
215
     * 2. print matrix
216
     * 3. search vars
217
     *
218
     * These are all done in the order saved in the SymbolTable.
219
     */
220

            
221
    let search_vars = Scoped::new(ffi::vec_var_new(), |x| ffi::vec_var_free(x as _));
222

            
223
    // store variables and the order they will be returned inside rust for later use.
224
    #[allow(clippy::unwrap_used)]
225
    let mut print_vars_guard = PRINT_VARS.lock().unwrap();
226
    *print_vars_guard = Some(vec![]);
227

            
228
    for var_name in model.named_variables.get_variable_order() {
229
        let c_str = CString::new(var_name.clone()).map_err(|_| {
230
            anyhow!(
231
                "Variable name {:?} contains a null character.",
232
                var_name.clone()
233
            )
234
        })?;
235

            
236
        let vartype = model
237
            .named_variables
238
            .get_vartype(var_name.clone())
239
            .ok_or(anyhow!("Could not get var type for {:?}", var_name.clone()))?;
240

            
241
        let (vartype_raw, domain_low, domain_high) = match vartype {
242
            VarDomain::Bound(a, b) => Ok((ffi::VariableType_VAR_BOUND, a, b)),
243
            VarDomain::Bool => Ok((ffi::VariableType_VAR_BOOL, 0, 1)), // TODO: will this work?
244
            x => Err(MinionError::NotImplemented(format!("{:?}", x))),
245
        }?;
246

            
247
        ffi::newVar_ffi(
248
            instance,
249
            c_str.as_ptr() as _,
250
            vartype_raw,
251
            domain_low,
252
            domain_high,
253
        );
254

            
255
        let var = ffi::getVarByName(instance, c_str.as_ptr() as _);
256

            
257
        ffi::printMatrix_addVar(instance, var);
258

            
259
        // add to the print vars stored in rust so to remember
260
        // the order for callback function.
261

            
262
        #[allow(clippy::unwrap_used)]
263
        (*print_vars_guard).as_mut().unwrap().push(var_name.clone());
264

            
265
        ffi::vec_var_push_back(search_vars.ptr, var);
266
    }
267

            
268
    let search_order = Scoped::new(
269
        ffi::searchOrder_new(search_vars.ptr, ffi::VarOrderEnum_ORDER_STATIC, false),
270
        |x| ffi::searchOrder_free(x as _),
271
    );
272

            
273
    ffi::instance_addSearchOrder(instance, search_order.ptr);
274

            
275
    /*********************************/
276
    /*        Add constraints        */
277
    /*********************************/
278

            
279
    for constraint in &model.constraints {
280
        // 1. get constraint type and create C++ constraint object
281
        // 2. run through arguments and add them to the constraint
282
        // 3. add constraint to instance
283

            
284
        let constraint_type = get_constraint_type(constraint)?;
285
        let raw_constraint = Scoped::new(ffi::constraint_new(constraint_type), |x| {
286
            ffi::constraint_free(x as _)
287
        });
288

            
289
        constraint_add_args(instance, raw_constraint.ptr, constraint)?;
290
        ffi::instance_addConstraint(instance, raw_constraint.ptr);
291
    }
292

            
293
    Ok(())
294
}
295

            
296
unsafe fn get_constraint_type(constraint: &Constraint) -> Result<u32, MinionError> {
297
    match constraint {
298
        Constraint::SumGeq(_, _) => Ok(ffi::ConstraintType_CT_GEQSUM),
299
        Constraint::SumLeq(_, _) => Ok(ffi::ConstraintType_CT_LEQSUM),
300
        Constraint::Ineq(_, _, _) => Ok(ffi::ConstraintType_CT_INEQ),
301
        Constraint::Eq(_, _) => Ok(ffi::ConstraintType_CT_EQ),
302
        Constraint::Difference(_, _) => Ok(ffi::ConstraintType_CT_DIFFERENCE),
303
        Constraint::Div(_, _) => Ok(ffi::ConstraintType_CT_DIV),
304
        Constraint::DivUndefZero(_, _) => Ok(ffi::ConstraintType_CT_DIV_UNDEFZERO),
305
        Constraint::Modulo(_, _) => Ok(ffi::ConstraintType_CT_MODULO),
306
        Constraint::ModuloUndefZero(_, _) => Ok(ffi::ConstraintType_CT_MODULO_UNDEFZERO),
307
        Constraint::Pow(_, _) => Ok(ffi::ConstraintType_CT_POW),
308
        Constraint::Product(_, _) => Ok(ffi::ConstraintType_CT_PRODUCT2),
309
        Constraint::WeightedSumGeq(_, _, _) => Ok(ffi::ConstraintType_CT_WEIGHTGEQSUM),
310
        Constraint::WeightedSumLeq(_, _, _) => Ok(ffi::ConstraintType_CT_WEIGHTLEQSUM),
311
        Constraint::CheckAssign(_) => Ok(ffi::ConstraintType_CT_CHECK_ASSIGN),
312
        Constraint::CheckGsa(_) => Ok(ffi::ConstraintType_CT_CHECK_GSA),
313
        Constraint::ForwardChecking(_) => Ok(ffi::ConstraintType_CT_FORWARD_CHECKING),
314
        Constraint::Reify(_, _) => Ok(ffi::ConstraintType_CT_REIFY),
315
        Constraint::ReifyImply(_, _) => Ok(ffi::ConstraintType_CT_REIFYIMPLY),
316
        Constraint::ReifyImplyQuick(_, _) => Ok(ffi::ConstraintType_CT_REIFYIMPLY_QUICK),
317
        Constraint::WatchedAnd(_) => Ok(ffi::ConstraintType_CT_WATCHED_NEW_AND),
318
        Constraint::WatchedOr(_) => Ok(ffi::ConstraintType_CT_WATCHED_NEW_OR),
319
        Constraint::GacAllDiff(_) => Ok(ffi::ConstraintType_CT_GACALLDIFF),
320
        Constraint::AllDiff(_) => Ok(ffi::ConstraintType_CT_ALLDIFF),
321
        Constraint::AllDiffMatrix(_, _) => Ok(ffi::ConstraintType_CT_ALLDIFFMATRIX),
322
        Constraint::WatchSumGeq(_, _) => Ok(ffi::ConstraintType_CT_WATCHED_GEQSUM),
323
        Constraint::WatchSumLeq(_, _) => Ok(ffi::ConstraintType_CT_WATCHED_LEQSUM),
324
        Constraint::OccurrenceGeq(_, _, _) => Ok(ffi::ConstraintType_CT_GEQ_OCCURRENCE),
325
        Constraint::OccurrenceLeq(_, _, _) => Ok(ffi::ConstraintType_CT_LEQ_OCCURRENCE),
326
        Constraint::Occurrence(_, _, _) => Ok(ffi::ConstraintType_CT_OCCURRENCE),
327
        Constraint::LitSumGeq(_, _, _) => Ok(ffi::ConstraintType_CT_WATCHED_LITSUM),
328
        Constraint::Gcc(_, _, _) => Ok(ffi::ConstraintType_CT_GCC),
329
        Constraint::GccWeak(_, _, _) => Ok(ffi::ConstraintType_CT_GCCWEAK),
330
        Constraint::LexLeqRv(_, _) => Ok(ffi::ConstraintType_CT_GACLEXLEQ),
331
        Constraint::LexLeq(_, _) => Ok(ffi::ConstraintType_CT_LEXLEQ),
332
        Constraint::LexLess(_, _) => Ok(ffi::ConstraintType_CT_LEXLESS),
333
        Constraint::LexLeqQuick(_, _) => Ok(ffi::ConstraintType_CT_QUICK_LEXLEQ),
334
        Constraint::LexLessQuick(_, _) => Ok(ffi::ConstraintType_CT_QUICK_LEXLEQ),
335
        Constraint::WatchVecNeq(_, _) => Ok(ffi::ConstraintType_CT_WATCHED_VECNEQ),
336
        Constraint::WatchVecExistsLess(_, _) => Ok(ffi::ConstraintType_CT_WATCHED_VEC_OR_LESS),
337
        Constraint::Hamming(_, _, _) => Ok(ffi::ConstraintType_CT_WATCHED_HAMMING),
338
        Constraint::NotHamming(_, _, _) => Ok(ffi::ConstraintType_CT_WATCHED_NOT_HAMMING),
339
        Constraint::FrameUpdate(_, _, _, _, _) => Ok(ffi::ConstraintType_CT_FRAMEUPDATE),
340
        Constraint::NegativeTable(_, _) => Ok(ffi::ConstraintType_CT_WATCHED_NEGATIVE_TABLE),
341
        Constraint::Table(_, _) => Ok(ffi::ConstraintType_CT_WATCHED_TABLE),
342
        Constraint::GacSchema(_, _) => Ok(ffi::ConstraintType_CT_GACSCHEMA),
343
        Constraint::LightTable(_, _) => Ok(ffi::ConstraintType_CT_LIGHTTABLE),
344
        Constraint::Mddc(_, _) => Ok(ffi::ConstraintType_CT_MDDC),
345
        Constraint::NegativeMddc(_, _) => Ok(ffi::ConstraintType_CT_NEGATIVEMDDC),
346
        Constraint::Str2Plus(_, _) => Ok(ffi::ConstraintType_CT_STR),
347
        Constraint::Max(_, _) => Ok(ffi::ConstraintType_CT_MAX),
348
        Constraint::Min(_, _) => Ok(ffi::ConstraintType_CT_MIN),
349
        Constraint::NvalueGeq(_, _) => Ok(ffi::ConstraintType_CT_GEQNVALUE),
350
        Constraint::NvalueLeq(_, _) => Ok(ffi::ConstraintType_CT_LEQNVALUE),
351
        Constraint::Element(_, _, _) => Ok(ffi::ConstraintType_CT_ELEMENT),
352
        Constraint::ElementOne(_, _, _) => Ok(ffi::ConstraintType_CT_ELEMENT_ONE),
353
        Constraint::ElementUndefZero(_, _, _) => Ok(ffi::ConstraintType_CT_ELEMENT_UNDEFZERO),
354
        Constraint::WatchElement(_, _, _) => Ok(ffi::ConstraintType_CT_WATCHED_ELEMENT),
355
        Constraint::WatchElementOne(_, _, _) => Ok(ffi::ConstraintType_CT_WATCHED_ELEMENT_ONE),
356
        Constraint::WatchElementOneUndefZero(_, _, _) => {
357
            Ok(ffi::ConstraintType_CT_WATCHED_ELEMENT_ONE_UNDEFZERO)
358
        }
359
        Constraint::WatchElementUndefZero(_, _, _) => {
360
            Ok(ffi::ConstraintType_CT_WATCHED_ELEMENT_UNDEFZERO)
361
        }
362
        Constraint::WLiteral(_, _) => Ok(ffi::ConstraintType_CT_WATCHED_LIT),
363
        Constraint::WNotLiteral(_, _) => Ok(ffi::ConstraintType_CT_WATCHED_NOTLIT),
364
        Constraint::WInIntervalSet(_, _) => Ok(ffi::ConstraintType_CT_WATCHED_ININTERVALSET),
365
        Constraint::WInRange(_, _) => Ok(ffi::ConstraintType_CT_WATCHED_INRANGE),
366
        Constraint::WInset(_, _) => Ok(ffi::ConstraintType_CT_WATCHED_INSET),
367
        Constraint::WNotInRange(_, _) => Ok(ffi::ConstraintType_CT_WATCHED_NOT_INRANGE),
368
        Constraint::WNotInset(_, _) => Ok(ffi::ConstraintType_CT_WATCHED_NOT_INSET),
369
        Constraint::Abs(_, _) => Ok(ffi::ConstraintType_CT_ABS),
370
        Constraint::DisEq(_, _) => Ok(ffi::ConstraintType_CT_DISEQ),
371
        Constraint::MinusEq(_, _) => Ok(ffi::ConstraintType_CT_MINUSEQ),
372
        Constraint::GacEq(_, _) => Ok(ffi::ConstraintType_CT_GACEQ),
373
        Constraint::WatchLess(_, _) => Ok(ffi::ConstraintType_CT_WATCHED_LESS),
374
        Constraint::WatchNeq(_, _) => Ok(ffi::ConstraintType_CT_WATCHED_NEQ),
375

            
376
        #[allow(unreachable_patterns)]
377
        x => Err(MinionError::NotImplemented(format!(
378
            "Constraint not implemented {:?}",
379
            x,
380
        ))),
381
    }
382
}
383

            
384
unsafe fn constraint_add_args(
385
    i: *mut ffi::ProbSpec_CSPInstance,
386
    r_constr: *mut ffi::ProbSpec_ConstraintBlob,
387
    constr: &Constraint,
388
) -> Result<(), MinionError> {
389
    match constr {
390
        Constraint::SumGeq(lhs_vars, rhs_var) => {
391
            read_list(i, r_constr, lhs_vars)?;
392
            read_var(i, r_constr, rhs_var)?;
393
            Ok(())
394
        }
395
        Constraint::SumLeq(lhs_vars, rhs_var) => {
396
            read_list(i, r_constr, lhs_vars)?;
397
            read_var(i, r_constr, rhs_var)?;
398
            Ok(())
399
        }
400
        Constraint::Ineq(var1, var2, c) => {
401
            read_var(i, r_constr, var1)?;
402
            read_var(i, r_constr, var2)?;
403
            read_constant(r_constr, c)?;
404
            Ok(())
405
        }
406
        Constraint::Eq(var1, var2) => {
407
            read_var(i, r_constr, var1)?;
408
            read_var(i, r_constr, var2)?;
409
            Ok(())
410
        }
411
        Constraint::Difference((a, b), c) => {
412
            read_2_vars(i, r_constr, a, b)?;
413
            read_var(i, r_constr, c)?;
414
            Ok(())
415
        }
416
        Constraint::Div((a, b), c) => {
417
            read_2_vars(i, r_constr, a, b)?;
418
            read_var(i, r_constr, c)?;
419
            Ok(())
420
        }
421
        Constraint::DivUndefZero((a, b), c) => {
422
            read_2_vars(i, r_constr, a, b)?;
423
            read_var(i, r_constr, c)?;
424
            Ok(())
425
        }
426
        Constraint::Modulo((a, b), c) => {
427
            read_2_vars(i, r_constr, a, b)?;
428
            read_var(i, r_constr, c)?;
429
            Ok(())
430
        }
431
        Constraint::ModuloUndefZero((a, b), c) => {
432
            read_2_vars(i, r_constr, a, b)?;
433
            read_var(i, r_constr, c)?;
434
            Ok(())
435
        }
436
        Constraint::Pow((a, b), c) => {
437
            read_2_vars(i, r_constr, a, b)?;
438
            read_var(i, r_constr, c)?;
439
            Ok(())
440
        }
441
        Constraint::Product((a, b), c) => {
442
            read_2_vars(i, r_constr, a, b)?;
443
            read_var(i, r_constr, c)?;
444
            Ok(())
445
        }
446
        Constraint::WeightedSumGeq(a, b, c) => {
447
            read_constant_list(r_constr, a)?;
448
            read_list(i, r_constr, b)?;
449
            read_var(i, r_constr, c)?;
450
            Ok(())
451
        }
452
        Constraint::WeightedSumLeq(a, b, c) => {
453
            read_constant_list(r_constr, a)?;
454
            read_list(i, r_constr, b)?;
455
            read_var(i, r_constr, c)?;
456
            Ok(())
457
        }
458
        Constraint::CheckAssign(a) => {
459
            read_constraint(i, r_constr, (**a).clone())?;
460
            Ok(())
461
        }
462
        Constraint::CheckGsa(a) => {
463
            read_constraint(i, r_constr, (**a).clone())?;
464
            Ok(())
465
        }
466
        Constraint::ForwardChecking(a) => {
467
            read_constraint(i, r_constr, (**a).clone())?;
468
            Ok(())
469
        }
470
        Constraint::Reify(a, b) => {
471
            read_constraint(i, r_constr, (**a).clone())?;
472
            read_var(i, r_constr, b)?;
473
            Ok(())
474
        }
475
        Constraint::ReifyImply(a, b) => {
476
            read_constraint(i, r_constr, (**a).clone())?;
477
            read_var(i, r_constr, b)?;
478
            Ok(())
479
        }
480
        Constraint::ReifyImplyQuick(a, b) => {
481
            read_constraint(i, r_constr, (**a).clone())?;
482
            read_var(i, r_constr, b)?;
483
            Ok(())
484
        }
485
        Constraint::WatchedAnd(a) => {
486
            read_constraint_list(i, r_constr, a)?;
487
            Ok(())
488
        }
489
        Constraint::WatchedOr(a) => {
490
            read_constraint_list(i, r_constr, a)?;
491
            Ok(())
492
        }
493
        Constraint::GacAllDiff(a) => {
494
            read_list(i, r_constr, a)?;
495
            Ok(())
496
        }
497
        Constraint::AllDiff(a) => {
498
            read_list(i, r_constr, a)?;
499
            Ok(())
500
        }
501
        Constraint::AllDiffMatrix(a, b) => {
502
            read_list(i, r_constr, a)?;
503
            read_constant(r_constr, b)?;
504
            Ok(())
505
        }
506
        Constraint::WatchSumGeq(a, b) => {
507
            read_list(i, r_constr, a)?;
508
            read_constant(r_constr, b)?;
509
            Ok(())
510
        }
511
        Constraint::WatchSumLeq(a, b) => {
512
            read_list(i, r_constr, a)?;
513
            read_constant(r_constr, b)?;
514
            Ok(())
515
        }
516
        Constraint::OccurrenceGeq(a, b, c) => {
517
            read_list(i, r_constr, a)?;
518
            read_constant(r_constr, b)?;
519
            read_constant(r_constr, c)?;
520
            Ok(())
521
        }
522
        Constraint::OccurrenceLeq(a, b, c) => {
523
            read_list(i, r_constr, a)?;
524
            read_constant(r_constr, b)?;
525
            read_constant(r_constr, c)?;
526
            Ok(())
527
        }
528
        Constraint::Occurrence(a, b, c) => {
529
            read_list(i, r_constr, a)?;
530
            read_constant(r_constr, b)?;
531
            read_var(i, r_constr, c)?;
532
            Ok(())
533
        }
534
        //Constraint::LitSumGeq(_, _, _) => todo!(),
535
        //Constraint::Gcc(_, _, _) => todo!(),
536
        //Constraint::GccWeak(_, _, _) => todo!(),
537
        //Constraint::LexLeqRv(_, _) => todo!(),
538
        //Constraint::LexLeq(_, _) => todo!(),
539
        //Constraint::LexLess(_, _) => todo!(),
540
        //Constraint::LexLeqQuick(_, _) => todo!(),
541
        //Constraint::LexLessQuick(_, _) => todo!(),
542
        //Constraint::WatchVecNeq(_, _) => todo!(),
543
        //Constraint::WatchVecExistsLess(_, _) => todo!(),
544
        //Constraint::Hamming(_, _, _) => todo!(),
545
        //Constraint::NotHamming(_, _, _) => todo!(),
546
        //Constraint::FrameUpdate(_, _, _, _, _) => todo!(),
547
        //Constraint::NegativeTable(_, _) => todo!(),
548
        //Constraint::Table(_, _) => todo!(),
549
        //Constraint::GacSchema(_, _) => todo!(),
550
        //Constraint::LightTable(_, _) => todo!(),
551
        //Constraint::Mddc(_, _) => todo!(),
552
        //Constraint::NegativeMddc(_, _) => todo!(),
553
        //Constraint::Str2Plus(_, _) => todo!(),
554
        //Constraint::Max(_, _) => todo!(),
555
        //Constraint::Min(_, _) => todo!(),
556
        //Constraint::NvalueGeq(_, _) => todo!(),
557
        //Constraint::NvalueLeq(_, _) => todo!(),
558
        //Constraint::Element(_, _, _) => todo!(),
559
        //Constraint::ElementOne(_, _, _) => todo!(),
560
        //Constraint::ElementUndefZero(_, _, _) => todo!(),
561
        //Constraint::WatchElement(_, _, _) => todo!(),
562
        //Constraint::WatchElementOne(_, _, _) => todo!(),
563
        //Constraint::WatchElementOneUndefZero(_, _, _) => todo!(),
564
        //Constraint::WatchElementUndefZero(_, _, _) => todo!(),
565
        //Constraint::WLiteral(_, _) => todo!(),
566
        //Constraint::WNotLiteral(_, _) => todo!(),
567
        //Constraint::WInIntervalSet(_, _) => todo!(),
568
        //Constraint::WInRange(_, _) => todo!(),
569
        Constraint::WInset(a, b) => {
570
            read_var(i, r_constr, a)?;
571
            read_constant_list(r_constr, b)?;
572
            Ok(())
573
        }
574
        //Constraint::WNotInRange(_, _) => todo!(),
575
        //Constraint::WNotInset(_, _) => todo!(),
576
        //Constraint::Abs(_, _) => todo!(),
577
        Constraint::DisEq(a, b) => {
578
            read_var(i, r_constr, a)?;
579
            read_var(i, r_constr, b)?;
580
            Ok(())
581
        }
582
        //Constraint::MinusEq(_, _) => todo!(),
583
        //Constraint::GacEq(_, _) => todo!(),
584
        //Constraint::WatchLess(_, _) => todo!(),
585
        // TODO: ensure that this is a bool?
586
        Constraint::WatchNeq(a, b) => {
587
            read_var(i, r_constr, a)?;
588
            read_var(i, r_constr, b)?;
589
            Ok(())
590
        }
591
        #[allow(unreachable_patterns)]
592
        x => Err(MinionError::NotImplemented(format!("{:?}", x))),
593
    }
594
}
595

            
596
// DO NOT call manually - this assumes that all needed vars are already in the symbol table.
597
// TODO not happy with this just assuming the name is in the symbol table
598
unsafe fn read_list(
599
    instance: *mut ffi::ProbSpec_CSPInstance,
600
    raw_constraint: *mut ffi::ProbSpec_ConstraintBlob,
601
    vars: &Vec<Var>,
602
) -> Result<(), MinionError> {
603
    let raw_vars = Scoped::new(ffi::vec_var_new(), |x| ffi::vec_var_free(x as _));
604
    for var in vars {
605
        let raw_var = match var {
606
            Var::NameRef(name) => {
607
                let c_str = CString::new(name.clone()).map_err(|_| {
608
                    anyhow!(
609
                        "Variable name {:?} contains a null character.",
610
                        name.clone()
611
                    )
612
                })?;
613
                ffi::getVarByName(instance, c_str.as_ptr() as _)
614
            }
615
            Var::ConstantAsVar(n) => ffi::constantAsVar(*n),
616
        };
617

            
618
        ffi::vec_var_push_back(raw_vars.ptr, raw_var);
619
    }
620

            
621
    ffi::constraint_addList(raw_constraint, raw_vars.ptr);
622

            
623
    Ok(())
624
}
625

            
626
unsafe fn read_var(
627
    instance: *mut ffi::ProbSpec_CSPInstance,
628
    raw_constraint: *mut ffi::ProbSpec_ConstraintBlob,
629
    var: &Var,
630
) -> Result<(), MinionError> {
631
    let raw_vars = Scoped::new(ffi::vec_var_new(), |x| ffi::vec_var_free(x as _));
632
    let raw_var = match var {
633
        Var::NameRef(name) => {
634
            let c_str = CString::new(name.clone()).map_err(|_| {
635
                anyhow!(
636
                    "Variable name {:?} contains a null character.",
637
                    name.clone()
638
                )
639
            })?;
640
            ffi::getVarByName(instance, c_str.as_ptr() as _)
641
        }
642
        Var::ConstantAsVar(n) => ffi::constantAsVar(*n),
643
    };
644

            
645
    ffi::vec_var_push_back(raw_vars.ptr, raw_var);
646
    ffi::constraint_addList(raw_constraint, raw_vars.ptr);
647

            
648
    Ok(())
649
}
650

            
651
unsafe fn read_2_vars(
652
    instance: *mut ffi::ProbSpec_CSPInstance,
653
    raw_constraint: *mut ffi::ProbSpec_ConstraintBlob,
654
    var1: &Var,
655
    var2: &Var,
656
) -> Result<(), MinionError> {
657
    let mut raw_var = match var1 {
658
        Var::NameRef(name) => {
659
            let c_str = CString::new(name.clone()).map_err(|_| {
660
                anyhow!(
661
                    "Variable name {:?} contains a null character.",
662
                    name.clone()
663
                )
664
            })?;
665
            ffi::getVarByName(instance, c_str.as_ptr() as _)
666
        }
667
        Var::ConstantAsVar(n) => ffi::constantAsVar(*n),
668
    };
669
    let mut raw_var2 = match var2 {
670
        Var::NameRef(name) => {
671
            let c_str = CString::new(name.clone()).map_err(|_| {
672
                anyhow!(
673
                    "Variable name {:?} contains a null character.",
674
                    name.clone()
675
                )
676
            })?;
677
            ffi::getVarByName(instance, c_str.as_ptr() as _)
678
        }
679
        Var::ConstantAsVar(n) => ffi::constantAsVar(*n),
680
    };
681
    // todo: does this move or copy? I am confus!
682
    // TODO need to mkae the semantics of move vs copy / ownership clear in libminion!!
683
    // This shouldve leaked everywhere by now but i think libminion copies stuff??
684
    ffi::constraint_addTwoVars(raw_constraint, &mut raw_var, &mut raw_var2);
685
    Ok(())
686
}
687

            
688
unsafe fn read_constant(
689
    raw_constraint: *mut ffi::ProbSpec_ConstraintBlob,
690
    constant: &Constant,
691
) -> Result<(), MinionError> {
692
    let val: i32 = match constant {
693
        Constant::Integer(n) => Ok(*n),
694
        Constant::Bool(true) => Ok(1),
695
        Constant::Bool(false) => Ok(0),
696
        x => Err(MinionError::NotImplemented(format!("{:?}", x))),
697
    }?;
698

            
699
    ffi::constraint_addConstant(raw_constraint, val);
700

            
701
    Ok(())
702
}
703

            
704
unsafe fn read_constant_list(
705
    raw_constraint: *mut ffi::ProbSpec_ConstraintBlob,
706
    constants: &[Constant],
707
) -> Result<(), MinionError> {
708
    let raw_consts = Scoped::new(ffi::vec_int_new(), |x| ffi::vec_var_free(x as _));
709

            
710
    for constant in constants.iter() {
711
        let val = match constant {
712
            Constant::Integer(n) => Ok(*n),
713
            Constant::Bool(true) => Ok(1),
714
            Constant::Bool(false) => Ok(0),
715
            #[allow(unreachable_patterns)] // TODO: can there be other types?
716
            x => Err(MinionError::NotImplemented(format!("{:?}", x))),
717
        }?;
718

            
719
        ffi::vec_int_push_back(raw_consts.ptr, val);
720
    }
721

            
722
    ffi::constraint_addConstantList(raw_constraint, raw_consts.ptr);
723
    Ok(())
724
}
725

            
726
//TODO: check if the inner constraint is listed in the model or not?
727
//Does this matter?
728
// TODO: type-check inner constraints vars and tuples and so on?
729
unsafe fn read_constraint(
730
    instance: *mut ffi::ProbSpec_CSPInstance,
731
    raw_constraint: *mut ffi::ProbSpec_ConstraintBlob,
732
    inner_constraint: Constraint,
733
) -> Result<(), MinionError> {
734
    let constraint_type = get_constraint_type(&inner_constraint)?;
735
    let raw_inner_constraint = Scoped::new(ffi::constraint_new(constraint_type), |x| {
736
        ffi::constraint_free(x as _)
737
    });
738

            
739
    constraint_add_args(instance, raw_inner_constraint.ptr, &inner_constraint)?;
740

            
741
    ffi::constraint_addConstraint(raw_constraint, raw_inner_constraint.ptr);
742
    Ok(())
743
}
744

            
745
unsafe fn read_constraint_list(
746
    instance: *mut ffi::ProbSpec_CSPInstance,
747
    raw_constraint: *mut ffi::ProbSpec_ConstraintBlob,
748
    inner_constraints: &[Constraint],
749
) -> Result<(), MinionError> {
750
    let raw_inners = Scoped::new(ffi::vec_constraints_new(), |x| {
751
        ffi::vec_constraints_free(x as _)
752
    });
753
    for inner_constraint in inner_constraints.iter() {
754
        let constraint_type = get_constraint_type(inner_constraint)?;
755
        let raw_inner_constraint = Scoped::new(ffi::constraint_new(constraint_type), |x| {
756
            ffi::constraint_free(x as _)
757
        });
758

            
759
        constraint_add_args(instance, raw_inner_constraint.ptr, inner_constraint)?;
760
        ffi::vec_constraints_push_back(raw_inners.ptr, raw_inner_constraint.ptr);
761
    }
762

            
763
    ffi::constraint_addConstraintList(raw_constraint, raw_inners.ptr);
764
    Ok(())
765
}