1#![allow(unreachable_patterns)]
2#![allow(unsafe_op_in_unsafe_fn)]
3
4use std::{
5 collections::HashMap,
6 ffi::{CStr, CString, c_char, c_void},
7};
8
9use anyhow::anyhow;
10
11use crate::{
12 ast::Tuple,
13 ffi::{self},
14};
15use crate::{
16 ast::{Constant, Constraint, Model, Var, VarDomain, VarName},
17 error::{MinionError, RuntimeError},
18 scoped_ptr::Scoped,
19};
20
21unsafe fn take_minion_ffi_error() -> String {
22 let error_ptr = ffi::minion_get_last_error();
23 if error_ptr.is_null() {
24 return "unknown Minion FFI error".to_string();
25 }
26
27 let message = CStr::from_ptr(error_ptr).to_string_lossy().into_owned();
28 ffi::minion_clear_last_error();
29 if message.is_empty() {
30 "unknown Minion FFI error".to_string()
31 } else {
32 message
33 }
34}
35
36unsafe fn new_var_ffi_checked(
37 instance: *mut ffi::ProbSpec_CSPInstance,
38 name: *const c_char,
39 vartype_raw: ffi::VariableType,
40 domain_low: i32,
41 domain_high: i32,
42) -> Result<(), MinionError> {
43 if ffi::newVar_ffi_safe(
44 instance,
45 name as *mut c_char,
46 vartype_raw,
47 domain_low,
48 domain_high,
49 ) {
50 Ok(())
51 } else {
52 Err(MinionError::Other(anyhow!(take_minion_ffi_error())))
53 }
54}
55
56unsafe fn get_var_by_name_checked(
57 instance: *mut ffi::ProbSpec_CSPInstance,
58 name: *const c_char,
59) -> Result<ffi::ProbSpec_Var, MinionError> {
60 let mut var = ffi::ProbSpec_Var {
61 type_m: ffi::VariableType_VAR_CONSTANT,
62 pos_m: 0,
63 };
64
65 if ffi::getVarByName_safe(instance, name as *mut c_char, &mut var) {
66 Ok(var)
67 } else {
68 Err(MinionError::Other(anyhow!(take_minion_ffi_error())))
69 }
70}
71
72pub type Callback<'a> = Box<dyn FnMut(HashMap<VarName, Constant>) -> bool + 'a>;
151
152struct CallbackState<'a> {
157 callback: Callback<'a>,
158 print_vars: Vec<VarName>,
159}
160
161pub struct SolverContext {
166 ctx: *mut ffi::MinionContext,
167}
168
169unsafe impl Send for SolverContext {}
173
174impl SolverContext {
175 pub fn get_from_table(&self, key: String) -> Option<String> {
177 unsafe {
178 #[allow(clippy::expect_used)]
179 let c_string = CString::new(key).expect("");
180 let key_ptr = c_string.into_raw();
181 let val_ptr: *mut c_char = ffi::TableOut_get(self.ctx, key_ptr);
182
183 drop(CString::from_raw(key_ptr));
184
185 if val_ptr.is_null() {
186 None
187 } else {
188 #[allow(clippy::unwrap_used)]
189 let res = CStr::from_ptr(val_ptr).to_str().unwrap().to_owned();
190 libc::free(val_ptr as _);
191 Some(res)
192 }
193 }
194 }
195}
196
197impl Drop for SolverContext {
198 fn drop(&mut self) {
199 unsafe {
200 ffi::minion_freeContext(self.ctx);
201 }
202 }
203}
204
205unsafe extern "C" fn run_callback(ctx: *mut ffi::MinionContext, userdata: *mut c_void) -> bool {
208 let state = unsafe { &mut *(userdata as *mut CallbackState<'_>) };
211
212 if state.print_vars.is_empty() {
213 return true;
214 }
215
216 let mut solutions: HashMap<VarName, Constant> = HashMap::new();
218 for (i, var) in state.print_vars.iter().enumerate() {
219 let solution_int: i32 = unsafe { ffi::printMatrix_getValue(ctx, i as _) };
220 let solution: Constant = Constant::Integer(solution_int);
221 solutions.insert(var.to_string(), solution);
222 }
223
224 (state.callback)(solutions)
225}
226
227#[allow(clippy::unwrap_used)]
234pub fn run_minion(model: Model, callback: Callback<'_>) -> Result<SolverContext, MinionError> {
235 let mut state = CallbackState {
236 callback,
237 print_vars: vec![],
238 };
239
240 unsafe {
241 let ctx = ffi::minion_newContext();
242 let search_opts = ffi::searchOptions_new();
243 let search_method = ffi::searchMethod_new();
244 let search_instance = ffi::instance_new();
245
246 (*search_opts).silent = true;
250 (*search_opts).print_solution = false;
251
252 convert_model_to_raw(search_instance, &model, &mut state.print_vars)?;
253
254 let userdata = &mut state as *mut CallbackState<'_> as *mut c_void;
255 let res = ffi::runMinion(
256 ctx,
257 search_opts,
258 search_method,
259 search_instance,
260 Some(run_callback),
261 userdata,
262 );
263
264 ffi::searchMethod_free(search_method);
265 ffi::searchOptions_free(search_opts);
266 ffi::instance_free(search_instance);
267
268 match res {
269 0 => Ok(SolverContext { ctx }),
270 x => {
271 ffi::minion_freeContext(ctx);
272 Err(MinionError::from(RuntimeError::from(x)))
273 }
274 }
275 }
276}
277
278unsafe fn convert_model_to_raw(
279 instance: *mut ffi::ProbSpec_CSPInstance,
280 model: &Model,
281 print_vars: &mut Vec<VarName>,
282) -> Result<(), MinionError> {
283 let search_vars = Scoped::new(ffi::vec_var_new(), |x| ffi::vec_var_free(x as _));
297
298 for var_name in model.named_variables.get_variable_order() {
300 let c_str = CString::new(var_name.clone()).map_err(|_| {
301 anyhow!(
302 "Variable name {:?} contains a null character.",
303 var_name.clone()
304 )
305 })?;
306
307 let vartype = model
308 .named_variables
309 .get_vartype(var_name.clone())
310 .ok_or(anyhow!("Could not get var type for {:?}", var_name.clone()))?;
311
312 let (vartype_raw, domain_low, domain_high) = match vartype {
313 VarDomain::Bound(a, b) => Ok((ffi::VariableType_VAR_BOUND, a, b)),
314 VarDomain::Discrete(a, b) => Ok((ffi::VariableType_VAR_DISCRETE, a, b)),
315 VarDomain::Bool => Ok((ffi::VariableType_VAR_BOOL, 0, 1)), x => Err(MinionError::NotImplemented(format!("{x:?}"))),
317 }?;
318
319 new_var_ffi_checked(
320 instance,
321 c_str.as_ptr(),
322 vartype_raw,
323 domain_low,
324 domain_high,
325 )?;
326
327 let var = get_var_by_name_checked(instance, c_str.as_ptr())?;
328
329 ffi::printMatrix_addVar(instance, var);
330
331 print_vars.push(var_name.clone());
333 }
334
335 for search_var_name in model.named_variables.get_search_variable_order() {
337 let c_str = CString::new(search_var_name.clone()).map_err(|_| {
338 anyhow!(
339 "Variable name {:?} contains a null character.",
340 search_var_name.clone()
341 )
342 })?;
343 let var = get_var_by_name_checked(instance, c_str.as_ptr())?;
344 ffi::vec_var_push_back(search_vars.ptr, var);
345 }
346
347 let search_order = Scoped::new(
348 ffi::searchOrder_new(search_vars.ptr, ffi::VarOrderEnum_ORDER_STATIC, false),
349 |x| ffi::searchOrder_free(x as _),
350 );
351
352 ffi::instance_addSearchOrder(instance, search_order.ptr);
353
354 for constraint in &model.constraints {
359 let constraint_type = get_constraint_type(constraint)?;
364 let raw_constraint = Scoped::new(ffi::constraint_new(constraint_type), |x| {
365 ffi::constraint_free(x as _)
366 });
367
368 constraint_add_args(instance, raw_constraint.ptr, constraint)?;
369 ffi::instance_addConstraint(instance, raw_constraint.ptr);
370 }
371
372 Ok(())
373}
374
375unsafe fn get_constraint_type(constraint: &Constraint) -> Result<u32, MinionError> {
376 match constraint {
377 Constraint::SumGeq(_, _) => Ok(ffi::ConstraintType_CT_GEQSUM),
378 Constraint::SumLeq(_, _) => Ok(ffi::ConstraintType_CT_LEQSUM),
379 Constraint::Ineq(_, _, _) => Ok(ffi::ConstraintType_CT_INEQ),
380 Constraint::Eq(_, _) => Ok(ffi::ConstraintType_CT_EQ),
381 Constraint::Difference(_, _) => Ok(ffi::ConstraintType_CT_DIFFERENCE),
382 Constraint::Div(_, _) => Ok(ffi::ConstraintType_CT_DIV),
383 Constraint::DivUndefZero(_, _) => Ok(ffi::ConstraintType_CT_DIV_UNDEFZERO),
384 Constraint::Modulo(_, _) => Ok(ffi::ConstraintType_CT_MODULO),
385 Constraint::ModuloUndefZero(_, _) => Ok(ffi::ConstraintType_CT_MODULO_UNDEFZERO),
386 Constraint::Pow(_, _) => Ok(ffi::ConstraintType_CT_POW),
387 Constraint::Product(_, _) => Ok(ffi::ConstraintType_CT_PRODUCT2),
388 Constraint::WeightedSumGeq(_, _, _) => Ok(ffi::ConstraintType_CT_WEIGHTGEQSUM),
389 Constraint::WeightedSumLeq(_, _, _) => Ok(ffi::ConstraintType_CT_WEIGHTLEQSUM),
390 Constraint::CheckAssign(_) => Ok(ffi::ConstraintType_CT_CHECK_ASSIGN),
391 Constraint::CheckGsa(_) => Ok(ffi::ConstraintType_CT_CHECK_GSA),
392 Constraint::ForwardChecking(_) => Ok(ffi::ConstraintType_CT_FORWARD_CHECKING),
393 Constraint::Reify(_, _) => Ok(ffi::ConstraintType_CT_REIFY),
394 Constraint::ReifyImply(_, _) => Ok(ffi::ConstraintType_CT_REIFYIMPLY),
395 Constraint::ReifyImplyQuick(_, _) => Ok(ffi::ConstraintType_CT_REIFYIMPLY_QUICK),
396 Constraint::WatchedAnd(_) => Ok(ffi::ConstraintType_CT_WATCHED_NEW_AND),
397 Constraint::WatchedOr(_) => Ok(ffi::ConstraintType_CT_WATCHED_NEW_OR),
398 Constraint::GacAllDiff(_) => Ok(ffi::ConstraintType_CT_GACALLDIFF),
399 Constraint::AllDiff(_) => Ok(ffi::ConstraintType_CT_ALLDIFF),
400 Constraint::AllDiffMatrix(_, _) => Ok(ffi::ConstraintType_CT_ALLDIFFMATRIX),
401 Constraint::WatchSumGeq(_, _) => Ok(ffi::ConstraintType_CT_WATCHED_GEQSUM),
402 Constraint::WatchSumLeq(_, _) => Ok(ffi::ConstraintType_CT_WATCHED_LEQSUM),
403 Constraint::OccurrenceGeq(_, _, _) => Ok(ffi::ConstraintType_CT_GEQ_OCCURRENCE),
404 Constraint::OccurrenceLeq(_, _, _) => Ok(ffi::ConstraintType_CT_LEQ_OCCURRENCE),
405 Constraint::Occurrence(_, _, _) => Ok(ffi::ConstraintType_CT_OCCURRENCE),
406 Constraint::LitSumGeq(_, _, _) => Ok(ffi::ConstraintType_CT_WATCHED_LITSUM),
407 Constraint::Gcc(_, _, _) => Ok(ffi::ConstraintType_CT_GCC),
408 Constraint::GccWeak(_, _, _) => Ok(ffi::ConstraintType_CT_GCCWEAK),
409 Constraint::LexLeqRv(_, _) => Ok(ffi::ConstraintType_CT_GACLEXLEQ),
410 Constraint::LexLeq(_, _) => Ok(ffi::ConstraintType_CT_LEXLEQ),
411 Constraint::LexLess(_, _) => Ok(ffi::ConstraintType_CT_LEXLESS),
412 Constraint::LexLeqQuick(_, _) => Ok(ffi::ConstraintType_CT_QUICK_LEXLEQ),
413 Constraint::LexLessQuick(_, _) => Ok(ffi::ConstraintType_CT_QUICK_LEXLEQ),
414 Constraint::WatchVecNeq(_, _) => Ok(ffi::ConstraintType_CT_WATCHED_VECNEQ),
415 Constraint::WatchVecExistsLess(_, _) => Ok(ffi::ConstraintType_CT_WATCHED_VEC_OR_LESS),
416 Constraint::Hamming(_, _, _) => Ok(ffi::ConstraintType_CT_WATCHED_HAMMING),
417 Constraint::NotHamming(_, _, _) => Ok(ffi::ConstraintType_CT_WATCHED_NOT_HAMMING),
418 Constraint::FrameUpdate(_, _, _, _, _) => Ok(ffi::ConstraintType_CT_FRAMEUPDATE),
419 Constraint::NegativeTable(_, _) => Ok(ffi::ConstraintType_CT_WATCHED_NEGATIVE_TABLE),
420 Constraint::Table(_, _) => Ok(ffi::ConstraintType_CT_WATCHED_TABLE),
421 Constraint::GacSchema(_, _) => Ok(ffi::ConstraintType_CT_GACSCHEMA),
422 Constraint::LightTable(_, _) => Ok(ffi::ConstraintType_CT_LIGHTTABLE),
423 Constraint::Mddc(_, _) => Ok(ffi::ConstraintType_CT_MDDC),
424 Constraint::NegativeMddc(_, _) => Ok(ffi::ConstraintType_CT_NEGATIVEMDDC),
425 Constraint::Str2Plus(_, _) => Ok(ffi::ConstraintType_CT_STR),
426 Constraint::Max(_, _) => Ok(ffi::ConstraintType_CT_MAX),
427 Constraint::Min(_, _) => Ok(ffi::ConstraintType_CT_MIN),
428 Constraint::NvalueGeq(_, _) => Ok(ffi::ConstraintType_CT_GEQNVALUE),
429 Constraint::NvalueLeq(_, _) => Ok(ffi::ConstraintType_CT_LEQNVALUE),
430 Constraint::Element(_, _, _) => Ok(ffi::ConstraintType_CT_ELEMENT),
431 Constraint::ElementOne(_, _, _) => Ok(ffi::ConstraintType_CT_ELEMENT_ONE),
432 Constraint::ElementUndefZero(_, _, _) => Ok(ffi::ConstraintType_CT_ELEMENT_UNDEFZERO),
433 Constraint::WatchElement(_, _, _) => Ok(ffi::ConstraintType_CT_WATCHED_ELEMENT),
434 Constraint::WatchElementOne(_, _, _) => Ok(ffi::ConstraintType_CT_WATCHED_ELEMENT_ONE),
435 Constraint::WatchElementOneUndefZero(_, _, _) => {
436 Ok(ffi::ConstraintType_CT_WATCHED_ELEMENT_ONE_UNDEFZERO)
437 }
438 Constraint::WatchElementUndefZero(_, _, _) => {
439 Ok(ffi::ConstraintType_CT_WATCHED_ELEMENT_UNDEFZERO)
440 }
441 Constraint::WLiteral(_, _) => Ok(ffi::ConstraintType_CT_WATCHED_LIT),
442 Constraint::WNotLiteral(_, _) => Ok(ffi::ConstraintType_CT_WATCHED_NOTLIT),
443 Constraint::WInIntervalSet(_, _) => Ok(ffi::ConstraintType_CT_WATCHED_ININTERVALSET),
444 Constraint::WInRange(_, _) => Ok(ffi::ConstraintType_CT_WATCHED_INRANGE),
445 Constraint::WInset(_, _) => Ok(ffi::ConstraintType_CT_WATCHED_INSET),
446 Constraint::WNotInRange(_, _) => Ok(ffi::ConstraintType_CT_WATCHED_NOT_INRANGE),
447 Constraint::WNotInset(_, _) => Ok(ffi::ConstraintType_CT_WATCHED_NOT_INSET),
448 Constraint::Abs(_, _) => Ok(ffi::ConstraintType_CT_ABS),
449 Constraint::DisEq(_, _) => Ok(ffi::ConstraintType_CT_DISEQ),
450 Constraint::MinusEq(_, _) => Ok(ffi::ConstraintType_CT_MINUSEQ),
451 Constraint::GacEq(_, _) => Ok(ffi::ConstraintType_CT_GACEQ),
452 Constraint::WatchLess(_, _) => Ok(ffi::ConstraintType_CT_WATCHED_LESS),
453 Constraint::WatchNeq(_, _) => Ok(ffi::ConstraintType_CT_WATCHED_NEQ),
454 Constraint::True => Ok(ffi::ConstraintType_CT_TRUE),
455 Constraint::False => Ok(ffi::ConstraintType_CT_FALSE),
456
457 #[allow(unreachable_patterns)]
458 x => Err(MinionError::NotImplemented(format!(
459 "Constraint not implemented {x:?}",
460 ))),
461 }
462}
463
464unsafe fn constraint_add_args(
465 i: *mut ffi::ProbSpec_CSPInstance,
466 r_constr: *mut ffi::ProbSpec_ConstraintBlob,
467 constr: &Constraint,
468) -> Result<(), MinionError> {
469 match constr {
470 Constraint::SumGeq(lhs_vars, rhs_var) => {
471 read_list(i, r_constr, lhs_vars)?;
472 read_var(i, r_constr, rhs_var)?;
473 Ok(())
474 }
475 Constraint::SumLeq(lhs_vars, rhs_var) => {
476 read_list(i, r_constr, lhs_vars)?;
477 read_var(i, r_constr, rhs_var)?;
478 Ok(())
479 }
480 Constraint::Ineq(var1, var2, c) => {
481 read_var(i, r_constr, var1)?;
482 read_var(i, r_constr, var2)?;
483 read_constant(r_constr, c)?;
484 Ok(())
485 }
486 Constraint::Eq(var1, var2) => {
487 read_var(i, r_constr, var1)?;
488 read_var(i, r_constr, var2)?;
489 Ok(())
490 }
491 Constraint::Difference((a, b), c) => {
492 read_2_vars(i, r_constr, a, b)?;
493 read_var(i, r_constr, c)?;
494 Ok(())
495 }
496 Constraint::Div((a, b), c) => {
497 read_2_vars(i, r_constr, a, b)?;
498 read_var(i, r_constr, c)?;
499 Ok(())
500 }
501 Constraint::DivUndefZero((a, b), c) => {
502 read_2_vars(i, r_constr, a, b)?;
503 read_var(i, r_constr, c)?;
504 Ok(())
505 }
506 Constraint::Modulo((a, b), c) => {
507 read_2_vars(i, r_constr, a, b)?;
508 read_var(i, r_constr, c)?;
509 Ok(())
510 }
511 Constraint::ModuloUndefZero((a, b), c) => {
512 read_2_vars(i, r_constr, a, b)?;
513 read_var(i, r_constr, c)?;
514 Ok(())
515 }
516 Constraint::Pow((a, b), c) => {
517 read_2_vars(i, r_constr, a, b)?;
518 read_var(i, r_constr, c)?;
519 Ok(())
520 }
521 Constraint::Product((a, b), c) => {
522 read_2_vars(i, r_constr, a, b)?;
523 read_var(i, r_constr, c)?;
524 Ok(())
525 }
526 Constraint::WeightedSumGeq(a, b, c) => {
527 read_constant_list(r_constr, a)?;
528 read_list(i, r_constr, b)?;
529 read_var(i, r_constr, c)?;
530 Ok(())
531 }
532 Constraint::WeightedSumLeq(a, b, c) => {
533 read_constant_list(r_constr, a)?;
534 read_list(i, r_constr, b)?;
535 read_var(i, r_constr, c)?;
536 Ok(())
537 }
538 Constraint::CheckAssign(a) => {
539 read_constraint(i, r_constr, (**a).clone())?;
540 Ok(())
541 }
542 Constraint::CheckGsa(a) => {
543 read_constraint(i, r_constr, (**a).clone())?;
544 Ok(())
545 }
546 Constraint::ForwardChecking(a) => {
547 read_constraint(i, r_constr, (**a).clone())?;
548 Ok(())
549 }
550 Constraint::Reify(a, b) => {
551 read_constraint(i, r_constr, (**a).clone())?;
552 read_var(i, r_constr, b)?;
553 Ok(())
554 }
555 Constraint::ReifyImply(a, b) => {
556 read_constraint(i, r_constr, (**a).clone())?;
557 read_var(i, r_constr, b)?;
558 Ok(())
559 }
560 Constraint::ReifyImplyQuick(a, b) => {
561 read_constraint(i, r_constr, (**a).clone())?;
562 read_var(i, r_constr, b)?;
563 Ok(())
564 }
565 Constraint::WatchedAnd(a) => {
566 read_constraint_list(i, r_constr, a)?;
567 Ok(())
568 }
569 Constraint::WatchedOr(a) => {
570 read_constraint_list(i, r_constr, a)?;
571 Ok(())
572 }
573 Constraint::GacAllDiff(a) => {
574 read_list(i, r_constr, a)?;
575 Ok(())
576 }
577 Constraint::AllDiff(a) => {
578 read_list(i, r_constr, a)?;
579 Ok(())
580 }
581 Constraint::AllDiffMatrix(a, b) => {
582 read_list(i, r_constr, a)?;
583 read_constant(r_constr, b)?;
584 Ok(())
585 }
586 Constraint::WatchSumGeq(a, b) => {
587 read_list(i, r_constr, a)?;
588 read_constant(r_constr, b)?;
589 Ok(())
590 }
591 Constraint::WatchSumLeq(a, b) => {
592 read_list(i, r_constr, a)?;
593 read_constant(r_constr, b)?;
594 Ok(())
595 }
596 Constraint::OccurrenceGeq(a, b, c) => {
597 read_list(i, r_constr, a)?;
598 read_constant(r_constr, b)?;
599 read_constant(r_constr, c)?;
600 Ok(())
601 }
602 Constraint::OccurrenceLeq(a, b, c) => {
603 read_list(i, r_constr, a)?;
604 read_constant(r_constr, b)?;
605 read_constant(r_constr, c)?;
606 Ok(())
607 }
608 Constraint::Occurrence(a, b, c) => {
609 read_list(i, r_constr, a)?;
610 read_constant(r_constr, b)?;
611 read_var(i, r_constr, c)?;
612 Ok(())
613 }
614 Constraint::LexLess(a, b) => {
615 read_list(i, r_constr, a)?;
616 read_list(i, r_constr, b)?;
617 Ok(())
618 }
619 Constraint::LexLeq(a, b) => {
620 read_list(i, r_constr, a)?;
621 read_list(i, r_constr, b)?;
622 Ok(())
623 }
624 Constraint::NegativeTable(vars, tuple_list) | Constraint::Table(vars, tuple_list) => {
636 read_list(i, r_constr, vars)?;
637 read_tuple_list(r_constr, tuple_list)?;
638 Ok(())
639 }
640 Constraint::ElementOne(vec, j, e) => {
654 read_list(i, r_constr, vec)?;
655 read_var(i, r_constr, j)?;
656 read_var(i, r_constr, e)?;
657 Ok(())
658 }
659 Constraint::WLiteral(a, b) => {
662 read_var(i, r_constr, a)?;
663 read_constant(r_constr, b)?;
664 Ok(())
665 }
666 Constraint::WInIntervalSet(var, consts) => {
668 read_var(i, r_constr, var)?;
669 read_constant_list(r_constr, consts)?;
670 Ok(())
671 }
672 Constraint::WInset(a, b) => {
674 read_var(i, r_constr, a)?;
675 read_constant_list(r_constr, b)?;
676 Ok(())
677 }
678 Constraint::Abs(a, b) => {
681 read_var(i, r_constr, a)?;
682 read_var(i, r_constr, b)?;
683 Ok(())
684 }
685 Constraint::DisEq(a, b) => {
686 read_var(i, r_constr, a)?;
687 read_var(i, r_constr, b)?;
688 Ok(())
689 }
690 Constraint::MinusEq(a, b) => {
691 read_var(i, r_constr, a)?;
692 read_var(i, r_constr, b)?;
693 Ok(())
694 }
695 Constraint::WatchNeq(a, b) => {
699 read_var(i, r_constr, a)?;
700 read_var(i, r_constr, b)?;
701 Ok(())
702 }
703
704 Constraint::True => Ok(()),
705 Constraint::False => Ok(()),
706 #[allow(unreachable_patterns)]
707 x => Err(MinionError::NotImplemented(format!("{x:?}"))),
708 }
709}
710
711unsafe fn read_list(
714 instance: *mut ffi::ProbSpec_CSPInstance,
715 raw_constraint: *mut ffi::ProbSpec_ConstraintBlob,
716 vars: &Vec<Var>,
717) -> Result<(), MinionError> {
718 let raw_vars = Scoped::new(ffi::vec_var_new(), |x| ffi::vec_var_free(x as _));
719 for var in vars {
720 let raw_var = match var {
721 Var::NameRef(name) => {
722 let c_str = CString::new(name.clone()).map_err(|_| {
723 anyhow!(
724 "Variable name {:?} contains a null character.",
725 name.clone()
726 )
727 })?;
728 get_var_by_name_checked(instance, c_str.as_ptr())?
729 }
730 Var::ConstantAsVar(n) => ffi::constantAsVar(*n),
731 };
732
733 ffi::vec_var_push_back(raw_vars.ptr, raw_var);
734 }
735
736 ffi::constraint_addList(raw_constraint, raw_vars.ptr);
737
738 Ok(())
739}
740
741unsafe fn read_var(
742 instance: *mut ffi::ProbSpec_CSPInstance,
743 raw_constraint: *mut ffi::ProbSpec_ConstraintBlob,
744 var: &Var,
745) -> Result<(), MinionError> {
746 let raw_vars = Scoped::new(ffi::vec_var_new(), |x| ffi::vec_var_free(x as _));
747 let raw_var = match var {
748 Var::NameRef(name) => {
749 let c_str = CString::new(name.clone()).map_err(|_| {
750 anyhow!(
751 "Variable name {:?} contains a null character.",
752 name.clone()
753 )
754 })?;
755 get_var_by_name_checked(instance, c_str.as_ptr())?
756 }
757 Var::ConstantAsVar(n) => ffi::constantAsVar(*n),
758 };
759
760 ffi::vec_var_push_back(raw_vars.ptr, raw_var);
761 ffi::constraint_addList(raw_constraint, raw_vars.ptr);
762
763 Ok(())
764}
765
766unsafe fn read_2_vars(
767 instance: *mut ffi::ProbSpec_CSPInstance,
768 raw_constraint: *mut ffi::ProbSpec_ConstraintBlob,
769 var1: &Var,
770 var2: &Var,
771) -> Result<(), MinionError> {
772 let mut raw_var = match var1 {
773 Var::NameRef(name) => {
774 let c_str = CString::new(name.clone()).map_err(|_| {
775 anyhow!(
776 "Variable name {:?} contains a null character.",
777 name.clone()
778 )
779 })?;
780 get_var_by_name_checked(instance, c_str.as_ptr())?
781 }
782 Var::ConstantAsVar(n) => ffi::constantAsVar(*n),
783 };
784 let mut raw_var2 = match var2 {
785 Var::NameRef(name) => {
786 let c_str = CString::new(name.clone()).map_err(|_| {
787 anyhow!(
788 "Variable name {:?} contains a null character.",
789 name.clone()
790 )
791 })?;
792 get_var_by_name_checked(instance, c_str.as_ptr())?
793 }
794 Var::ConstantAsVar(n) => ffi::constantAsVar(*n),
795 };
796 ffi::constraint_addTwoVars(raw_constraint, &mut raw_var, &mut raw_var2);
800 Ok(())
801}
802
803unsafe fn read_constant(
804 raw_constraint: *mut ffi::ProbSpec_ConstraintBlob,
805 constant: &Constant,
806) -> Result<(), MinionError> {
807 let val: i32 = match constant {
808 Constant::Integer(n) => Ok(*n),
809 Constant::Bool(true) => Ok(1),
810 Constant::Bool(false) => Ok(0),
811 x => Err(MinionError::NotImplemented(format!("{x:?}"))),
812 }?;
813
814 ffi::constraint_addConstant(raw_constraint, val);
815
816 Ok(())
817}
818
819unsafe fn read_constant_list(
820 raw_constraint: *mut ffi::ProbSpec_ConstraintBlob,
821 constants: &[Constant],
822) -> Result<(), MinionError> {
823 let raw_consts = Scoped::new(ffi::vec_int_new(), |x| ffi::vec_int_free(x as _));
824
825 for constant in constants.iter() {
826 let val = match constant {
827 Constant::Integer(n) => Ok(*n),
828 Constant::Bool(true) => Ok(1),
829 Constant::Bool(false) => Ok(0),
830 #[allow(unreachable_patterns)] x => Err(MinionError::NotImplemented(format!("{x:?}"))),
832 }?;
833
834 ffi::vec_int_push_back(raw_consts.ptr, val);
835 }
836
837 ffi::constraint_addConstantList(raw_constraint, raw_consts.ptr);
838 Ok(())
839}
840
841unsafe fn read_constraint(
845 instance: *mut ffi::ProbSpec_CSPInstance,
846 raw_constraint: *mut ffi::ProbSpec_ConstraintBlob,
847 inner_constraint: Constraint,
848) -> Result<(), MinionError> {
849 let constraint_type = get_constraint_type(&inner_constraint)?;
850 let raw_inner_constraint = Scoped::new(ffi::constraint_new(constraint_type), |x| {
851 ffi::constraint_free(x as _)
852 });
853
854 constraint_add_args(instance, raw_inner_constraint.ptr, &inner_constraint)?;
855
856 ffi::constraint_addConstraint(raw_constraint, raw_inner_constraint.ptr);
857 Ok(())
858}
859
860unsafe fn read_constraint_list(
861 instance: *mut ffi::ProbSpec_CSPInstance,
862 raw_constraint: *mut ffi::ProbSpec_ConstraintBlob,
863 inner_constraints: &[Constraint],
864) -> Result<(), MinionError> {
865 let raw_inners = Scoped::new(ffi::vec_constraints_new(), |x| {
866 ffi::vec_constraints_free(x as _)
867 });
868 for inner_constraint in inner_constraints.iter() {
869 let constraint_type = get_constraint_type(inner_constraint)?;
870 let raw_inner_constraint = Scoped::new(ffi::constraint_new(constraint_type), |x| {
871 ffi::constraint_free(x as _)
872 });
873
874 constraint_add_args(instance, raw_inner_constraint.ptr, inner_constraint)?;
875 ffi::vec_constraints_push_back(raw_inners.ptr, raw_inner_constraint.ptr);
876 }
877
878 ffi::constraint_addConstraintList(raw_constraint, raw_inners.ptr);
879 Ok(())
880}
881
882unsafe fn read_tuple_list(
883 raw_constraint: *mut ffi::ProbSpec_ConstraintBlob,
884 tuples: &Vec<Tuple>,
885) -> Result<(), MinionError> {
886 let raw_tuples = Scoped::new(ffi::vec_vec_int_new(), |x| ffi::vec_vec_int_free(x as _));
888 for tuple in tuples {
889 let raw_tuple = Scoped::new(ffi::vec_int_new(), |x| ffi::vec_int_free(x as _));
890 for constant in tuple.iter() {
891 let val = match constant {
892 Constant::Integer(n) => Ok(*n),
893 Constant::Bool(true) => Ok(1),
894 Constant::Bool(false) => Ok(0),
895 #[allow(unreachable_patterns)] x => Err(MinionError::NotImplemented(format!("{:?}", x))),
897 }?;
898
899 ffi::vec_int_push_back(raw_tuple.ptr, val);
900 }
901
902 ffi::vec_vec_int_push_back_ptr(raw_tuples.ptr, raw_tuple.ptr);
903 }
904
905 let raw_tuple_list = ffi::tupleList_new(raw_tuples.ptr);
908 ffi::constraint_setTuples(raw_constraint, raw_tuple_list);
909
910 Ok(())
911}