1#![allow(unreachable_patterns)]
2
3use std::{
4 collections::HashMap,
5 ffi::CString,
6 sync::Condvar,
7 sync::{Mutex, MutexGuard},
8};
9
10use anyhow::anyhow;
11
12use crate::ffi::{self};
13use crate::{
14 ast::{Constant, Constraint, Model, Var, VarDomain, VarName},
15 error::{MinionError, RuntimeError},
16 scoped_ptr::Scoped,
17};
18
19pub type Callback = fn(solution_set: HashMap<VarName, Constant>) -> bool;
110
111static CALLBACK: Mutex<Option<Callback>> = Mutex::new(None);
116
117static PRINT_VARS: Mutex<Option<Vec<VarName>>> = Mutex::new(None);
119
120static LOCK: (Mutex<bool>, Condvar) = (Mutex::new(false), Condvar::new());
121
122#[no_mangle]
123unsafe extern "C" fn run_callback() -> bool {
124 #[allow(clippy::unwrap_used)]
129 let mut guard: MutexGuard<'_, Option<Vec<VarName>>> = PRINT_VARS.lock().unwrap();
130
131 if guard.is_none() {
132 return true;
133 }
134
135 let print_vars = match &mut *guard {
136 Some(x) => x,
137 None => unreachable!(),
138 };
139
140 if print_vars.is_empty() {
141 return true;
142 }
143
144 let mut solutions: HashMap<VarName, Constant> = HashMap::new();
146
147 for (i, var) in print_vars.iter().enumerate() {
148 let solution_int: i32 = ffi::printMatrix_getValue(i as _);
149 let solution: Constant = Constant::Integer(solution_int);
150 solutions.insert(var.to_string(), solution);
151 }
152
153 #[allow(clippy::unwrap_used)]
154 match *CALLBACK.lock().unwrap() {
155 None => true,
156 Some(func) => func(solutions),
157 }
158}
159
160#[allow(clippy::unwrap_used)]
165pub fn run_minion(model: Model, callback: Callback) -> Result<(), MinionError> {
166 *CALLBACK.lock().unwrap() = Some(callback);
168
169 let (lock, condvar) = &LOCK;
170 let mut _lock_guard = condvar
171 .wait_while(lock.lock().unwrap(), |locked| *locked)
172 .unwrap();
173
174 *_lock_guard = true;
175
176 unsafe {
177 let search_opts = ffi::searchOptions_new();
179 let search_method = ffi::searchMethod_new();
180 let search_instance = ffi::instance_new();
181
182 convert_model_to_raw(search_instance, &model)?;
183
184 let res = ffi::runMinion(
185 search_opts,
186 search_method,
187 search_instance,
188 Some(run_callback),
189 );
190
191 ffi::searchMethod_free(search_method);
192 ffi::searchOptions_free(search_opts);
193 ffi::instance_free(search_instance);
194
195 *_lock_guard = false;
196 std::mem::drop(_lock_guard);
197
198 condvar.notify_one();
199
200 match res {
201 0 => Ok(()),
202 x => Err(MinionError::from(RuntimeError::from(x))),
203 }
204 }
205}
206
207unsafe fn convert_model_to_raw(
208 instance: *mut ffi::ProbSpec_CSPInstance,
209 model: &Model,
210) -> Result<(), MinionError> {
211 let search_vars = Scoped::new(ffi::vec_var_new(), |x| ffi::vec_var_free(x as _));
225
226 #[allow(clippy::unwrap_used)]
228 let mut print_vars_guard = PRINT_VARS.lock().unwrap();
229 *print_vars_guard = Some(vec![]);
230
231 for var_name in model.named_variables.get_variable_order() {
233 let c_str = CString::new(var_name.clone()).map_err(|_| {
234 anyhow!(
235 "Variable name {:?} contains a null character.",
236 var_name.clone()
237 )
238 })?;
239
240 let vartype = model
241 .named_variables
242 .get_vartype(var_name.clone())
243 .ok_or(anyhow!("Could not get var type for {:?}", var_name.clone()))?;
244
245 let (vartype_raw, domain_low, domain_high) = match vartype {
246 VarDomain::Bound(a, b) => Ok((ffi::VariableType_VAR_BOUND, a, b)),
247 VarDomain::Bool => Ok((ffi::VariableType_VAR_BOOL, 0, 1)), x => Err(MinionError::NotImplemented(format!("{:?}", x))),
249 }?;
250
251 ffi::newVar_ffi(
252 instance,
253 c_str.as_ptr() as _,
254 vartype_raw,
255 domain_low,
256 domain_high,
257 );
258
259 let var = ffi::getVarByName(instance, c_str.as_ptr() as _);
260
261 ffi::printMatrix_addVar(instance, var);
262
263 #[allow(clippy::unwrap_used)]
267 (*print_vars_guard).as_mut().unwrap().push(var_name.clone());
268 }
269
270 for search_var_name in model.named_variables.get_search_variable_order() {
272 let c_str = CString::new(search_var_name.clone()).map_err(|_| {
273 anyhow!(
274 "Variable name {:?} contains a null character.",
275 search_var_name.clone()
276 )
277 })?;
278 let var = ffi::getVarByName(instance, c_str.as_ptr() as _);
279 ffi::vec_var_push_back(search_vars.ptr, var);
280 }
281
282 let search_order = Scoped::new(
283 ffi::searchOrder_new(search_vars.ptr, ffi::VarOrderEnum_ORDER_STATIC, false),
284 |x| ffi::searchOrder_free(x as _),
285 );
286
287 ffi::instance_addSearchOrder(instance, search_order.ptr);
288
289 for constraint in &model.constraints {
294 let constraint_type = get_constraint_type(constraint)?;
299 let raw_constraint = Scoped::new(ffi::constraint_new(constraint_type), |x| {
300 ffi::constraint_free(x as _)
301 });
302
303 constraint_add_args(instance, raw_constraint.ptr, constraint)?;
304 ffi::instance_addConstraint(instance, raw_constraint.ptr);
305 }
306
307 Ok(())
308}
309
310unsafe fn get_constraint_type(constraint: &Constraint) -> Result<u32, MinionError> {
311 match constraint {
312 Constraint::SumGeq(_, _) => Ok(ffi::ConstraintType_CT_GEQSUM),
313 Constraint::SumLeq(_, _) => Ok(ffi::ConstraintType_CT_LEQSUM),
314 Constraint::Ineq(_, _, _) => Ok(ffi::ConstraintType_CT_INEQ),
315 Constraint::Eq(_, _) => Ok(ffi::ConstraintType_CT_EQ),
316 Constraint::Difference(_, _) => Ok(ffi::ConstraintType_CT_DIFFERENCE),
317 Constraint::Div(_, _) => Ok(ffi::ConstraintType_CT_DIV),
318 Constraint::DivUndefZero(_, _) => Ok(ffi::ConstraintType_CT_DIV_UNDEFZERO),
319 Constraint::Modulo(_, _) => Ok(ffi::ConstraintType_CT_MODULO),
320 Constraint::ModuloUndefZero(_, _) => Ok(ffi::ConstraintType_CT_MODULO_UNDEFZERO),
321 Constraint::Pow(_, _) => Ok(ffi::ConstraintType_CT_POW),
322 Constraint::Product(_, _) => Ok(ffi::ConstraintType_CT_PRODUCT2),
323 Constraint::WeightedSumGeq(_, _, _) => Ok(ffi::ConstraintType_CT_WEIGHTGEQSUM),
324 Constraint::WeightedSumLeq(_, _, _) => Ok(ffi::ConstraintType_CT_WEIGHTLEQSUM),
325 Constraint::CheckAssign(_) => Ok(ffi::ConstraintType_CT_CHECK_ASSIGN),
326 Constraint::CheckGsa(_) => Ok(ffi::ConstraintType_CT_CHECK_GSA),
327 Constraint::ForwardChecking(_) => Ok(ffi::ConstraintType_CT_FORWARD_CHECKING),
328 Constraint::Reify(_, _) => Ok(ffi::ConstraintType_CT_REIFY),
329 Constraint::ReifyImply(_, _) => Ok(ffi::ConstraintType_CT_REIFYIMPLY),
330 Constraint::ReifyImplyQuick(_, _) => Ok(ffi::ConstraintType_CT_REIFYIMPLY_QUICK),
331 Constraint::WatchedAnd(_) => Ok(ffi::ConstraintType_CT_WATCHED_NEW_AND),
332 Constraint::WatchedOr(_) => Ok(ffi::ConstraintType_CT_WATCHED_NEW_OR),
333 Constraint::GacAllDiff(_) => Ok(ffi::ConstraintType_CT_GACALLDIFF),
334 Constraint::AllDiff(_) => Ok(ffi::ConstraintType_CT_ALLDIFF),
335 Constraint::AllDiffMatrix(_, _) => Ok(ffi::ConstraintType_CT_ALLDIFFMATRIX),
336 Constraint::WatchSumGeq(_, _) => Ok(ffi::ConstraintType_CT_WATCHED_GEQSUM),
337 Constraint::WatchSumLeq(_, _) => Ok(ffi::ConstraintType_CT_WATCHED_LEQSUM),
338 Constraint::OccurrenceGeq(_, _, _) => Ok(ffi::ConstraintType_CT_GEQ_OCCURRENCE),
339 Constraint::OccurrenceLeq(_, _, _) => Ok(ffi::ConstraintType_CT_LEQ_OCCURRENCE),
340 Constraint::Occurrence(_, _, _) => Ok(ffi::ConstraintType_CT_OCCURRENCE),
341 Constraint::LitSumGeq(_, _, _) => Ok(ffi::ConstraintType_CT_WATCHED_LITSUM),
342 Constraint::Gcc(_, _, _) => Ok(ffi::ConstraintType_CT_GCC),
343 Constraint::GccWeak(_, _, _) => Ok(ffi::ConstraintType_CT_GCCWEAK),
344 Constraint::LexLeqRv(_, _) => Ok(ffi::ConstraintType_CT_GACLEXLEQ),
345 Constraint::LexLeq(_, _) => Ok(ffi::ConstraintType_CT_LEXLEQ),
346 Constraint::LexLess(_, _) => Ok(ffi::ConstraintType_CT_LEXLESS),
347 Constraint::LexLeqQuick(_, _) => Ok(ffi::ConstraintType_CT_QUICK_LEXLEQ),
348 Constraint::LexLessQuick(_, _) => Ok(ffi::ConstraintType_CT_QUICK_LEXLEQ),
349 Constraint::WatchVecNeq(_, _) => Ok(ffi::ConstraintType_CT_WATCHED_VECNEQ),
350 Constraint::WatchVecExistsLess(_, _) => Ok(ffi::ConstraintType_CT_WATCHED_VEC_OR_LESS),
351 Constraint::Hamming(_, _, _) => Ok(ffi::ConstraintType_CT_WATCHED_HAMMING),
352 Constraint::NotHamming(_, _, _) => Ok(ffi::ConstraintType_CT_WATCHED_NOT_HAMMING),
353 Constraint::FrameUpdate(_, _, _, _, _) => Ok(ffi::ConstraintType_CT_FRAMEUPDATE),
354 Constraint::NegativeTable(_, _) => Ok(ffi::ConstraintType_CT_WATCHED_NEGATIVE_TABLE),
355 Constraint::Table(_, _) => Ok(ffi::ConstraintType_CT_WATCHED_TABLE),
356 Constraint::GacSchema(_, _) => Ok(ffi::ConstraintType_CT_GACSCHEMA),
357 Constraint::LightTable(_, _) => Ok(ffi::ConstraintType_CT_LIGHTTABLE),
358 Constraint::Mddc(_, _) => Ok(ffi::ConstraintType_CT_MDDC),
359 Constraint::NegativeMddc(_, _) => Ok(ffi::ConstraintType_CT_NEGATIVEMDDC),
360 Constraint::Str2Plus(_, _) => Ok(ffi::ConstraintType_CT_STR),
361 Constraint::Max(_, _) => Ok(ffi::ConstraintType_CT_MAX),
362 Constraint::Min(_, _) => Ok(ffi::ConstraintType_CT_MIN),
363 Constraint::NvalueGeq(_, _) => Ok(ffi::ConstraintType_CT_GEQNVALUE),
364 Constraint::NvalueLeq(_, _) => Ok(ffi::ConstraintType_CT_LEQNVALUE),
365 Constraint::Element(_, _, _) => Ok(ffi::ConstraintType_CT_ELEMENT),
366 Constraint::ElementOne(_, _, _) => Ok(ffi::ConstraintType_CT_ELEMENT_ONE),
367 Constraint::ElementUndefZero(_, _, _) => Ok(ffi::ConstraintType_CT_ELEMENT_UNDEFZERO),
368 Constraint::WatchElement(_, _, _) => Ok(ffi::ConstraintType_CT_WATCHED_ELEMENT),
369 Constraint::WatchElementOne(_, _, _) => Ok(ffi::ConstraintType_CT_WATCHED_ELEMENT_ONE),
370 Constraint::WatchElementOneUndefZero(_, _, _) => {
371 Ok(ffi::ConstraintType_CT_WATCHED_ELEMENT_ONE_UNDEFZERO)
372 }
373 Constraint::WatchElementUndefZero(_, _, _) => {
374 Ok(ffi::ConstraintType_CT_WATCHED_ELEMENT_UNDEFZERO)
375 }
376 Constraint::WLiteral(_, _) => Ok(ffi::ConstraintType_CT_WATCHED_LIT),
377 Constraint::WNotLiteral(_, _) => Ok(ffi::ConstraintType_CT_WATCHED_NOTLIT),
378 Constraint::WInIntervalSet(_, _) => Ok(ffi::ConstraintType_CT_WATCHED_ININTERVALSET),
379 Constraint::WInRange(_, _) => Ok(ffi::ConstraintType_CT_WATCHED_INRANGE),
380 Constraint::WInset(_, _) => Ok(ffi::ConstraintType_CT_WATCHED_INSET),
381 Constraint::WNotInRange(_, _) => Ok(ffi::ConstraintType_CT_WATCHED_NOT_INRANGE),
382 Constraint::WNotInset(_, _) => Ok(ffi::ConstraintType_CT_WATCHED_NOT_INSET),
383 Constraint::Abs(_, _) => Ok(ffi::ConstraintType_CT_ABS),
384 Constraint::DisEq(_, _) => Ok(ffi::ConstraintType_CT_DISEQ),
385 Constraint::MinusEq(_, _) => Ok(ffi::ConstraintType_CT_MINUSEQ),
386 Constraint::GacEq(_, _) => Ok(ffi::ConstraintType_CT_GACEQ),
387 Constraint::WatchLess(_, _) => Ok(ffi::ConstraintType_CT_WATCHED_LESS),
388 Constraint::WatchNeq(_, _) => Ok(ffi::ConstraintType_CT_WATCHED_NEQ),
389 Constraint::True => Ok(ffi::ConstraintType_CT_TRUE),
390 Constraint::False => Ok(ffi::ConstraintType_CT_FALSE),
391
392 #[allow(unreachable_patterns)]
393 x => Err(MinionError::NotImplemented(format!(
394 "Constraint not implemented {:?}",
395 x,
396 ))),
397 }
398}
399
400unsafe fn constraint_add_args(
401 i: *mut ffi::ProbSpec_CSPInstance,
402 r_constr: *mut ffi::ProbSpec_ConstraintBlob,
403 constr: &Constraint,
404) -> Result<(), MinionError> {
405 match constr {
406 Constraint::SumGeq(lhs_vars, rhs_var) => {
407 read_list(i, r_constr, lhs_vars)?;
408 read_var(i, r_constr, rhs_var)?;
409 Ok(())
410 }
411 Constraint::SumLeq(lhs_vars, rhs_var) => {
412 read_list(i, r_constr, lhs_vars)?;
413 read_var(i, r_constr, rhs_var)?;
414 Ok(())
415 }
416 Constraint::Ineq(var1, var2, c) => {
417 read_var(i, r_constr, var1)?;
418 read_var(i, r_constr, var2)?;
419 read_constant(r_constr, c)?;
420 Ok(())
421 }
422 Constraint::Eq(var1, var2) => {
423 read_var(i, r_constr, var1)?;
424 read_var(i, r_constr, var2)?;
425 Ok(())
426 }
427 Constraint::Difference((a, b), c) => {
428 read_2_vars(i, r_constr, a, b)?;
429 read_var(i, r_constr, c)?;
430 Ok(())
431 }
432 Constraint::Div((a, b), c) => {
433 read_2_vars(i, r_constr, a, b)?;
434 read_var(i, r_constr, c)?;
435 Ok(())
436 }
437 Constraint::DivUndefZero((a, b), c) => {
438 read_2_vars(i, r_constr, a, b)?;
439 read_var(i, r_constr, c)?;
440 Ok(())
441 }
442 Constraint::Modulo((a, b), c) => {
443 read_2_vars(i, r_constr, a, b)?;
444 read_var(i, r_constr, c)?;
445 Ok(())
446 }
447 Constraint::ModuloUndefZero((a, b), c) => {
448 read_2_vars(i, r_constr, a, b)?;
449 read_var(i, r_constr, c)?;
450 Ok(())
451 }
452 Constraint::Pow((a, b), c) => {
453 read_2_vars(i, r_constr, a, b)?;
454 read_var(i, r_constr, c)?;
455 Ok(())
456 }
457 Constraint::Product((a, b), c) => {
458 read_2_vars(i, r_constr, a, b)?;
459 read_var(i, r_constr, c)?;
460 Ok(())
461 }
462 Constraint::WeightedSumGeq(a, b, c) => {
463 read_constant_list(r_constr, a)?;
464 read_list(i, r_constr, b)?;
465 read_var(i, r_constr, c)?;
466 Ok(())
467 }
468 Constraint::WeightedSumLeq(a, b, c) => {
469 read_constant_list(r_constr, a)?;
470 read_list(i, r_constr, b)?;
471 read_var(i, r_constr, c)?;
472 Ok(())
473 }
474 Constraint::CheckAssign(a) => {
475 read_constraint(i, r_constr, (**a).clone())?;
476 Ok(())
477 }
478 Constraint::CheckGsa(a) => {
479 read_constraint(i, r_constr, (**a).clone())?;
480 Ok(())
481 }
482 Constraint::ForwardChecking(a) => {
483 read_constraint(i, r_constr, (**a).clone())?;
484 Ok(())
485 }
486 Constraint::Reify(a, b) => {
487 read_constraint(i, r_constr, (**a).clone())?;
488 read_var(i, r_constr, b)?;
489 Ok(())
490 }
491 Constraint::ReifyImply(a, b) => {
492 read_constraint(i, r_constr, (**a).clone())?;
493 read_var(i, r_constr, b)?;
494 Ok(())
495 }
496 Constraint::ReifyImplyQuick(a, b) => {
497 read_constraint(i, r_constr, (**a).clone())?;
498 read_var(i, r_constr, b)?;
499 Ok(())
500 }
501 Constraint::WatchedAnd(a) => {
502 read_constraint_list(i, r_constr, a)?;
503 Ok(())
504 }
505 Constraint::WatchedOr(a) => {
506 read_constraint_list(i, r_constr, a)?;
507 Ok(())
508 }
509 Constraint::GacAllDiff(a) => {
510 read_list(i, r_constr, a)?;
511 Ok(())
512 }
513 Constraint::AllDiff(a) => {
514 read_list(i, r_constr, a)?;
515 Ok(())
516 }
517 Constraint::AllDiffMatrix(a, b) => {
518 read_list(i, r_constr, a)?;
519 read_constant(r_constr, b)?;
520 Ok(())
521 }
522 Constraint::WatchSumGeq(a, b) => {
523 read_list(i, r_constr, a)?;
524 read_constant(r_constr, b)?;
525 Ok(())
526 }
527 Constraint::WatchSumLeq(a, b) => {
528 read_list(i, r_constr, a)?;
529 read_constant(r_constr, b)?;
530 Ok(())
531 }
532 Constraint::OccurrenceGeq(a, b, c) => {
533 read_list(i, r_constr, a)?;
534 read_constant(r_constr, b)?;
535 read_constant(r_constr, c)?;
536 Ok(())
537 }
538 Constraint::OccurrenceLeq(a, b, c) => {
539 read_list(i, r_constr, a)?;
540 read_constant(r_constr, b)?;
541 read_constant(r_constr, c)?;
542 Ok(())
543 }
544 Constraint::Occurrence(a, b, c) => {
545 read_list(i, r_constr, a)?;
546 read_constant(r_constr, b)?;
547 read_var(i, r_constr, c)?;
548 Ok(())
549 }
550 Constraint::ElementOne(vec, j, e) => {
579 read_list(i, r_constr, vec)?;
580 read_var(i, r_constr, j)?;
581 read_var(i, r_constr, e)?;
582 Ok(())
583 }
584 Constraint::WLiteral(a, b) => {
587 read_var(i, r_constr, a)?;
588 read_constant(r_constr, b)?;
589 Ok(())
590 }
591 Constraint::WInIntervalSet(var, consts) => {
593 read_var(i, r_constr, var)?;
594 read_constant_list(r_constr, consts)?;
595 Ok(())
596 }
597 Constraint::WInset(a, b) => {
599 read_var(i, r_constr, a)?;
600 read_constant_list(r_constr, b)?;
601 Ok(())
602 }
603 Constraint::Abs(a, b) => {
606 read_var(i, r_constr, a)?;
607 read_var(i, r_constr, b)?;
608 Ok(())
609 }
610 Constraint::DisEq(a, b) => {
611 read_var(i, r_constr, a)?;
612 read_var(i, r_constr, b)?;
613 Ok(())
614 }
615 Constraint::MinusEq(a, b) => {
616 read_var(i, r_constr, a)?;
617 read_var(i, r_constr, b)?;
618 Ok(())
619 }
620 Constraint::WatchNeq(a, b) => {
624 read_var(i, r_constr, a)?;
625 read_var(i, r_constr, b)?;
626 Ok(())
627 }
628
629 Constraint::True => Ok(()),
630 Constraint::False => Ok(()),
631 #[allow(unreachable_patterns)]
632 x => Err(MinionError::NotImplemented(format!("{:?}", x))),
633 }
634}
635
636unsafe fn read_list(
639 instance: *mut ffi::ProbSpec_CSPInstance,
640 raw_constraint: *mut ffi::ProbSpec_ConstraintBlob,
641 vars: &Vec<Var>,
642) -> Result<(), MinionError> {
643 let raw_vars = Scoped::new(ffi::vec_var_new(), |x| ffi::vec_var_free(x as _));
644 for var in vars {
645 let raw_var = match var {
646 Var::NameRef(name) => {
647 let c_str = CString::new(name.clone()).map_err(|_| {
648 anyhow!(
649 "Variable name {:?} contains a null character.",
650 name.clone()
651 )
652 })?;
653 ffi::getVarByName(instance, c_str.as_ptr() as _)
654 }
655 Var::ConstantAsVar(n) => ffi::constantAsVar(*n),
656 };
657
658 ffi::vec_var_push_back(raw_vars.ptr, raw_var);
659 }
660
661 ffi::constraint_addList(raw_constraint, raw_vars.ptr);
662
663 Ok(())
664}
665
666unsafe fn read_var(
667 instance: *mut ffi::ProbSpec_CSPInstance,
668 raw_constraint: *mut ffi::ProbSpec_ConstraintBlob,
669 var: &Var,
670) -> Result<(), MinionError> {
671 let raw_vars = Scoped::new(ffi::vec_var_new(), |x| ffi::vec_var_free(x as _));
672 let raw_var = match var {
673 Var::NameRef(name) => {
674 let c_str = CString::new(name.clone()).map_err(|_| {
675 anyhow!(
676 "Variable name {:?} contains a null character.",
677 name.clone()
678 )
679 })?;
680 ffi::getVarByName(instance, c_str.as_ptr() as _)
681 }
682 Var::ConstantAsVar(n) => ffi::constantAsVar(*n),
683 };
684
685 ffi::vec_var_push_back(raw_vars.ptr, raw_var);
686 ffi::constraint_addList(raw_constraint, raw_vars.ptr);
687
688 Ok(())
689}
690
691unsafe fn read_2_vars(
692 instance: *mut ffi::ProbSpec_CSPInstance,
693 raw_constraint: *mut ffi::ProbSpec_ConstraintBlob,
694 var1: &Var,
695 var2: &Var,
696) -> Result<(), MinionError> {
697 let mut raw_var = match var1 {
698 Var::NameRef(name) => {
699 let c_str = CString::new(name.clone()).map_err(|_| {
700 anyhow!(
701 "Variable name {:?} contains a null character.",
702 name.clone()
703 )
704 })?;
705 ffi::getVarByName(instance, c_str.as_ptr() as _)
706 }
707 Var::ConstantAsVar(n) => ffi::constantAsVar(*n),
708 };
709 let mut raw_var2 = match var2 {
710 Var::NameRef(name) => {
711 let c_str = CString::new(name.clone()).map_err(|_| {
712 anyhow!(
713 "Variable name {:?} contains a null character.",
714 name.clone()
715 )
716 })?;
717 ffi::getVarByName(instance, c_str.as_ptr() as _)
718 }
719 Var::ConstantAsVar(n) => ffi::constantAsVar(*n),
720 };
721 ffi::constraint_addTwoVars(raw_constraint, &mut raw_var, &mut raw_var2);
725 Ok(())
726}
727
728unsafe fn read_constant(
729 raw_constraint: *mut ffi::ProbSpec_ConstraintBlob,
730 constant: &Constant,
731) -> Result<(), MinionError> {
732 let val: i32 = match constant {
733 Constant::Integer(n) => Ok(*n),
734 Constant::Bool(true) => Ok(1),
735 Constant::Bool(false) => Ok(0),
736 x => Err(MinionError::NotImplemented(format!("{:?}", x))),
737 }?;
738
739 ffi::constraint_addConstant(raw_constraint, val);
740
741 Ok(())
742}
743
744unsafe fn read_constant_list(
745 raw_constraint: *mut ffi::ProbSpec_ConstraintBlob,
746 constants: &[Constant],
747) -> Result<(), MinionError> {
748 let raw_consts = Scoped::new(ffi::vec_int_new(), |x| ffi::vec_var_free(x as _));
749
750 for constant in constants.iter() {
751 let val = match constant {
752 Constant::Integer(n) => Ok(*n),
753 Constant::Bool(true) => Ok(1),
754 Constant::Bool(false) => Ok(0),
755 #[allow(unreachable_patterns)] x => Err(MinionError::NotImplemented(format!("{:?}", x))),
757 }?;
758
759 ffi::vec_int_push_back(raw_consts.ptr, val);
760 }
761
762 ffi::constraint_addConstantList(raw_constraint, raw_consts.ptr);
763 Ok(())
764}
765
766unsafe fn read_constraint(
770 instance: *mut ffi::ProbSpec_CSPInstance,
771 raw_constraint: *mut ffi::ProbSpec_ConstraintBlob,
772 inner_constraint: Constraint,
773) -> Result<(), MinionError> {
774 let constraint_type = get_constraint_type(&inner_constraint)?;
775 let raw_inner_constraint = Scoped::new(ffi::constraint_new(constraint_type), |x| {
776 ffi::constraint_free(x as _)
777 });
778
779 constraint_add_args(instance, raw_inner_constraint.ptr, &inner_constraint)?;
780
781 ffi::constraint_addConstraint(raw_constraint, raw_inner_constraint.ptr);
782 Ok(())
783}
784
785unsafe fn read_constraint_list(
786 instance: *mut ffi::ProbSpec_CSPInstance,
787 raw_constraint: *mut ffi::ProbSpec_ConstraintBlob,
788 inner_constraints: &[Constraint],
789) -> Result<(), MinionError> {
790 let raw_inners = Scoped::new(ffi::vec_constraints_new(), |x| {
791 ffi::vec_constraints_free(x as _)
792 });
793 for inner_constraint in inner_constraints.iter() {
794 let constraint_type = get_constraint_type(inner_constraint)?;
795 let raw_inner_constraint = Scoped::new(ffi::constraint_new(constraint_type), |x| {
796 ffi::constraint_free(x as _)
797 });
798
799 constraint_add_args(instance, raw_inner_constraint.ptr, inner_constraint)?;
800 ffi::vec_constraints_push_back(raw_inners.ptr, raw_inner_constraint.ptr);
801 }
802
803 ffi::constraint_addConstraintList(raw_constraint, raw_inners.ptr);
804 Ok(())
805}