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::{Constant, Constraint, Model, Var, VarDomain, VarName},
13 error::{MinionError, check_minion_result},
14 scoped_ptr::Scoped,
15};
16use crate::{
17 ast::{ShortTuple, Tuple},
18 ffi::{self},
19};
20
21pub type Callback<'a> = Box<dyn FnMut(HashMap<VarName, Constant>) -> bool + 'a>;
100
101pub type MidSearchCallback<'a> =
105 Box<dyn FnMut(&mut MidSearchContext<'_>, HashMap<VarName, Constant>) -> bool + 'a>;
106
107pub struct MidSearchContext<'a> {
118 ctx: *mut ffi::MinionContext,
119 instance: *mut ffi::ProbSpec_CSPInstance,
120 midsearch_vars: &'a mut Vec<VarName>,
121 _not_send: std::marker::PhantomData<*mut ()>,
122}
123
124impl MidSearchContext<'_> {
125 pub fn add_var(&mut self, name: &str, domain: VarDomain) -> Result<(), MinionError> {
132 let c_name = CString::new(name)
133 .map_err(|_| anyhow!("Variable name {:?} contains a null character.", name))?;
134 unsafe {
135 match domain {
136 VarDomain::Bool => {
137 check_minion_result(ffi::minion_newVarMidsearch(
138 self.ctx,
139 self.instance,
140 c_name.as_ptr() as *mut c_char,
141 ffi::VariableType_VAR_BOOL,
142 0,
143 1,
144 ))?;
145 }
146 VarDomain::Bound(a, b) => {
147 check_minion_result(ffi::minion_newVarMidsearch(
148 self.ctx,
149 self.instance,
150 c_name.as_ptr() as *mut c_char,
151 ffi::VariableType_VAR_BOUND,
152 a,
153 b,
154 ))?;
155 }
156 VarDomain::Discrete(a, b) => {
157 check_minion_result(ffi::minion_newVarMidsearch(
158 self.ctx,
159 self.instance,
160 c_name.as_ptr() as *mut c_char,
161 ffi::VariableType_VAR_DISCRETE,
162 a,
163 b,
164 ))?;
165 }
166 VarDomain::SparseBound(ref vals) => {
167 let raw = Scoped::new(ffi::vec_int_new(), |x| ffi::vec_int_free(x as _));
168 for v in vals {
169 ffi::vec_int_push_back(raw.ptr, *v);
170 }
171 check_minion_result(ffi::minion_newSparseBoundVarMidsearch(
172 self.ctx,
173 self.instance,
174 c_name.as_ptr() as *mut c_char,
175 raw.ptr,
176 ))?;
177 }
178 x => return Err(MinionError::NotImplemented(format!("{x:?}"))),
179 }
180 }
181 self.midsearch_vars.push(name.to_owned());
182 Ok(())
183 }
184
185 pub fn add_constraint(&mut self, constraint: Constraint) -> Result<(), MinionError> {
190 unsafe {
191 let ct = get_constraint_type(&constraint)?;
192 let raw = Scoped::new(ffi::constraint_new(ct), |x| ffi::constraint_free(x as _));
193 constraint_add_args(self.instance, raw.ptr, &constraint)?;
194 check_minion_result(ffi::minion_addConstraintMidsearch(
195 self.ctx,
196 self.instance,
197 raw.ptr,
198 ))?;
199 }
200 Ok(())
201 }
202}
203
204struct CallbackState<'a> {
209 callback: MidSearchCallback<'a>,
210 instance: *mut ffi::ProbSpec_CSPInstance,
211 print_vars: Vec<VarName>,
214 midsearch_vars: Vec<VarName>,
218}
219
220pub struct SolverContext {
225 ctx: *mut ffi::MinionContext,
226}
227
228unsafe impl Send for SolverContext {}
232
233impl SolverContext {
234 pub fn get_from_table(&self, key: String) -> Option<String> {
236 unsafe {
237 #[allow(clippy::expect_used)]
238 let c_string = CString::new(key).expect("");
239 let key_ptr = c_string.into_raw();
240 let val_ptr: *mut c_char = ffi::TableOut_get(self.ctx, key_ptr);
241
242 drop(CString::from_raw(key_ptr));
243
244 if val_ptr.is_null() {
245 None
246 } else {
247 #[allow(clippy::unwrap_used)]
248 let res = CStr::from_ptr(val_ptr).to_str().unwrap().to_owned();
249 libc::free(val_ptr as _);
250 Some(res)
251 }
252 }
253 }
254}
255
256impl Drop for SolverContext {
257 fn drop(&mut self) {
258 unsafe {
259 ffi::minion_freeContext(self.ctx);
260 }
261 }
262}
263
264unsafe extern "C" fn run_callback(ctx: *mut ffi::MinionContext, userdata: *mut c_void) -> bool {
267 let state = unsafe { &mut *(userdata as *mut CallbackState<'_>) };
270
271 let mut solutions: HashMap<VarName, Constant> = HashMap::new();
273
274 for (i, var) in state.print_vars.iter().enumerate() {
276 let v: i32 = unsafe { ffi::printMatrix_getValue(ctx, i as _) };
277 solutions.insert(var.clone(), Constant::Integer(v));
278 }
279
280 for var in state.midsearch_vars.iter() {
282 #[allow(clippy::unwrap_used)]
283 let c_name = CString::new(var.clone()).unwrap();
284 let v: i32 = unsafe { ffi::minion_getVarValue(ctx, state.instance, c_name.as_ptr()) };
285 solutions.insert(var.clone(), Constant::Integer(v));
286 }
287
288 let mut midctx = MidSearchContext {
289 ctx,
290 instance: state.instance,
291 midsearch_vars: &mut state.midsearch_vars,
292 _not_send: std::marker::PhantomData,
293 };
294 (state.callback)(&mut midctx, solutions)
295}
296
297#[derive(Default, Clone, Copy, Debug, PartialEq, Eq)]
306pub enum PropLevel {
307 #[default]
308 None,
309 Gac,
310 SacBounds,
311 Sac,
312 SsacBounds,
313 Ssac,
314}
315
316impl PropLevel {
317 fn to_ffi(self) -> ffi::PropagationType {
318 match self {
319 PropLevel::None => ffi::PropagationType_PropLevel_None,
320 PropLevel::Gac => ffi::PropagationType_PropLevel_GAC,
321 PropLevel::SacBounds => ffi::PropagationType_PropLevel_SACBounds,
322 PropLevel::Sac => ffi::PropagationType_PropLevel_SAC,
323 PropLevel::SsacBounds => ffi::PropagationType_PropLevel_SSACBounds,
324 PropLevel::Ssac => ffi::PropagationType_PropLevel_SSAC,
325 }
326 }
327}
328
329#[derive(Default, Clone, Copy, Debug, PartialEq, Eq)]
336pub struct Propagation {
337 pub level: PropLevel,
338 pub limit: bool,
339}
340
341impl Propagation {
342 fn to_ffi(self) -> ffi::PropagationLevel {
343 ffi::PropagationLevel {
344 type_: self.level.to_ffi(),
345 limit: self.limit,
346 }
347 }
348
349 pub fn is_default_preprocess(self) -> bool {
350 self.level == PropLevel::None && !self.limit
351 }
352
353 pub fn is_default_prop_node(self) -> bool {
354 self.level == PropLevel::Gac && !self.limit
355 }
356}
357
358#[derive(Default, Clone, Copy, Debug, PartialEq, Eq)]
366pub enum VarOrder {
367 #[default]
368 Static,
369 Sdf,
370 Srf,
371 Ldf,
372 Original,
373 Wdeg,
374 DomOverWdeg,
375 Conflict,
376}
377
378#[derive(Default, Clone, Copy, Debug, PartialEq, Eq)]
385pub enum ValOrder {
386 #[default]
387 Ascend,
388 Descend,
389 Random,
390}
391
392#[derive(Clone, Copy, Debug)]
395pub struct RunOptions {
396 pub seed: Option<u32>,
403
404 pub var_order: VarOrder,
406
407 pub val_order: ValOrder,
409
410 pub randomise_order: bool,
418
419 pub preprocess: Propagation,
423
424 pub prop_node: Propagation,
430
431 pub node_limit: u64,
437
438 pub time_limit: Option<TimeLimit>,
446}
447
448#[derive(Clone, Copy, Debug, PartialEq, Eq)]
461pub struct TimeLimit {
462 pub seconds: u32,
463 pub is_cpu_time: bool,
464}
465
466impl Default for RunOptions {
467 fn default() -> Self {
468 Self {
469 seed: None,
470 var_order: VarOrder::default(),
471 val_order: ValOrder::default(),
472 randomise_order: false,
473 preprocess: Propagation {
474 level: PropLevel::None,
475 limit: false,
476 },
477 prop_node: Propagation {
478 level: PropLevel::Gac,
479 limit: false,
480 },
481 node_limit: 0,
482 time_limit: None,
483 }
484 }
485}
486
487impl VarOrder {
488 fn to_ffi(self) -> ffi::VarOrderEnum {
489 match self {
490 VarOrder::Static => ffi::VarOrderEnum_ORDER_STATIC,
491 VarOrder::Sdf => ffi::VarOrderEnum_ORDER_SDF,
492 VarOrder::Srf => ffi::VarOrderEnum_ORDER_SRF,
493 VarOrder::Ldf => ffi::VarOrderEnum_ORDER_LDF,
494 VarOrder::Original => ffi::VarOrderEnum_ORDER_ORIGINAL,
495 VarOrder::Wdeg => ffi::VarOrderEnum_ORDER_WDEG,
496 VarOrder::DomOverWdeg => ffi::VarOrderEnum_ORDER_DOMOVERWDEG,
497 VarOrder::Conflict => ffi::VarOrderEnum_ORDER_CONFLICT,
498 }
499 }
500}
501
502impl ValOrder {
503 fn to_ffi(self) -> ffi::ValOrderEnum {
504 match self {
505 ValOrder::Ascend => ffi::ValOrderEnum_VALORDER_ASCEND,
506 ValOrder::Descend => ffi::ValOrderEnum_VALORDER_DESCEND,
507 ValOrder::Random => ffi::ValOrderEnum_VALORDER_RANDOM,
508 }
509 }
510}
511
512pub fn run_minion(model: Model, callback: Callback<'_>) -> Result<SolverContext, MinionError> {
522 run_minion_with_options(model, RunOptions::default(), callback)
523}
524
525pub fn run_minion_with_options(
528 model: Model,
529 options: RunOptions,
530 mut callback: Callback<'_>,
531) -> Result<SolverContext, MinionError> {
532 run_minion_midsearch_with_options(model, options, Box::new(move |_ctx, sol| callback(sol)))
533}
534
535pub fn run_minion_midsearch(
538 model: Model,
539 callback: MidSearchCallback<'_>,
540) -> Result<SolverContext, MinionError> {
541 run_minion_midsearch_with_options(model, RunOptions::default(), callback)
542}
543
544pub type ParallelCallback<'a> = Box<dyn FnMut(HashMap<VarName, Constant>) -> bool + Send + 'a>;
552
553struct ParallelCallbackState<'a> {
557 callback: std::sync::Mutex<ParallelCallback<'a>>,
558 print_vars: Vec<VarName>,
562}
563
564unsafe extern "C" fn parallel_callback_thunk(
565 ctx: *mut ffi::MinionContext,
566 userdata: *mut c_void,
567) -> bool {
568 let state = unsafe { &*(userdata as *mut ParallelCallbackState<'_>) };
569
570 let mut solutions: HashMap<VarName, Constant> = HashMap::new();
571 for (i, var) in state.print_vars.iter().enumerate() {
572 let v: i32 = unsafe { ffi::printMatrix_getValue(ctx, i as _) };
573 solutions.insert(var.clone(), Constant::Integer(v));
574 }
575
576 #[allow(clippy::unwrap_used)]
580 let mut cb = state.callback.lock().unwrap();
581 cb(solutions)
582}
583
584pub fn run_minion_parallel(
612 num_threads: usize,
613 model: Model,
614 callback: ParallelCallback<'_>,
615) -> Result<(), MinionError> {
616 run_minion_parallel_with_options(num_threads, model, RunOptions::default(), callback)
617}
618
619pub fn run_minion_work_steal(
639 num_threads: usize,
640 model: Model,
641 callback: ParallelCallback<'_>,
642) -> Result<WorkStealStats, MinionError> {
643 run_minion_work_steal_with_options(num_threads, model, RunOptions::default(), callback)
644}
645
646#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
668pub struct WorkStealStats {
669 pub donations: i64,
670 pub items_taken: i64,
671 pub replay_failures: i64,
672 pub total_nodes: i64,
673 pub queue_lock_wait_nanos: i64,
674 pub idle_wait_nanos: i64,
675 pub callback_lock_wait_nanos: i64,
676}
677
678#[allow(clippy::unwrap_used)]
680pub fn run_minion_work_steal_with_options(
681 num_threads: usize,
682 model: Model,
683 options: RunOptions,
684 callback: ParallelCallback<'_>,
685) -> Result<WorkStealStats, MinionError> {
686 if num_threads == 0 {
687 return Err(MinionError::Other(anyhow!(
688 "num_threads must be at least 1"
689 )));
690 }
691 if num_threads > i32::MAX as usize {
692 return Err(MinionError::Other(anyhow!("num_threads is too large")));
693 }
694
695 unsafe {
696 let search_opts = ffi::searchOptions_new();
697 let search_method = ffi::searchMethod_new();
698 let search_instance = ffi::instance_new();
699
700 (*search_opts).randomiseValvarorder = options.randomise_order;
701 (*search_opts).silent = true;
702 (*search_opts).print_solution = false;
703 (*search_opts).sollimit = -1;
704
705 if let Some(seed) = options.seed {
706 #[allow(clippy::useless_conversion)]
709 {
710 (*search_method).randomSeed = seed.into();
711 }
712 }
713 (*search_method).preprocess = options.preprocess.to_ffi();
714 (*search_method).propMethod = options.prop_node.to_ffi();
715 if options.node_limit > 0 {
716 (*search_opts).nodelimit = options.node_limit as _;
717 }
718 if let Some(tl) = options.time_limit {
719 (*search_opts).timeoutActive = true;
720 (*search_opts).time_limit = tl.seconds as _;
721 (*search_opts).time_limit_is_CPUTime = tl.is_cpu_time;
722 }
723
724 let mut print_vars: Vec<VarName> = vec![];
725 let convert_result =
726 convert_model_to_raw(search_instance, &model, &options, &mut print_vars);
727 if let Err(e) = convert_result {
728 ffi::searchMethod_free(search_method);
729 ffi::searchOptions_free(search_opts);
730 ffi::instance_free(search_instance);
731 return Err(e);
732 }
733
734 let state = ParallelCallbackState {
735 callback: std::sync::Mutex::new(callback),
736 print_vars,
737 };
738 let userdata = &state as *const ParallelCallbackState<'_> as *mut c_void;
739
740 let cfg = ffi::MinionThreadConfig {
741 numThreads: num_threads as i32,
742 baseSeed: options.seed.unwrap_or(0),
743 };
744
745 let mut raw_stats = ffi::MinionWorkStealStats {
746 donations: 0,
747 itemsTaken: 0,
748 replayFailures: 0,
749 totalNodes: 0,
750 queueLockWaitNanos: 0,
751 idleWaitNanos: 0,
752 callbackLockWaitNanos: 0,
753 };
754
755 let res = ffi::runMinionWorkSteal(
756 cfg,
757 search_opts,
758 search_method,
759 search_instance,
760 Some(parallel_callback_thunk),
761 userdata,
762 &mut raw_stats,
763 );
764
765 ffi::searchMethod_free(search_method);
766 ffi::searchOptions_free(search_opts);
767 ffi::instance_free(search_instance);
768
769 check_minion_result(res)?;
770 Ok(WorkStealStats {
771 donations: raw_stats.donations,
772 items_taken: raw_stats.itemsTaken,
773 replay_failures: raw_stats.replayFailures,
774 total_nodes: raw_stats.totalNodes,
775 queue_lock_wait_nanos: raw_stats.queueLockWaitNanos,
776 idle_wait_nanos: raw_stats.idleWaitNanos,
777 callback_lock_wait_nanos: raw_stats.callbackLockWaitNanos,
778 })
779 }
780}
781
782#[allow(clippy::unwrap_used)]
786pub fn run_minion_parallel_with_options(
787 num_threads: usize,
788 model: Model,
789 options: RunOptions,
790 callback: ParallelCallback<'_>,
791) -> Result<(), MinionError> {
792 if num_threads == 0 {
793 return Err(MinionError::Other(anyhow!(
794 "num_threads must be at least 1"
795 )));
796 }
797 if num_threads > i32::MAX as usize {
798 return Err(MinionError::Other(anyhow!("num_threads is too large")));
799 }
800
801 unsafe {
802 let search_opts = ffi::searchOptions_new();
803 let search_method = ffi::searchMethod_new();
804 let search_instance = ffi::instance_new();
805
806 (*search_opts).randomiseValvarorder = options.randomise_order;
810 (*search_opts).silent = true;
811 (*search_opts).print_solution = false;
812 (*search_opts).sollimit = -1;
816
817 if let Some(seed) = options.seed {
818 #[allow(clippy::useless_conversion)]
821 {
822 (*search_method).randomSeed = seed.into();
823 }
824 }
825 (*search_method).preprocess = options.preprocess.to_ffi();
826 (*search_method).propMethod = options.prop_node.to_ffi();
827 if options.node_limit > 0 {
828 (*search_opts).nodelimit = options.node_limit as _;
829 }
830 if let Some(tl) = options.time_limit {
831 (*search_opts).timeoutActive = true;
832 (*search_opts).time_limit = tl.seconds as _;
833 (*search_opts).time_limit_is_CPUTime = tl.is_cpu_time;
834 }
835
836 let mut print_vars: Vec<VarName> = vec![];
837 let convert_result =
838 convert_model_to_raw(search_instance, &model, &options, &mut print_vars);
839 if let Err(e) = convert_result {
840 ffi::searchMethod_free(search_method);
841 ffi::searchOptions_free(search_opts);
842 ffi::instance_free(search_instance);
843 return Err(e);
844 }
845
846 let state = ParallelCallbackState {
847 callback: std::sync::Mutex::new(callback),
848 print_vars,
849 };
850 let userdata = &state as *const ParallelCallbackState<'_> as *mut c_void;
851
852 let cfg = ffi::MinionThreadConfig {
853 numThreads: num_threads as i32,
854 baseSeed: options.seed.unwrap_or(0),
855 };
856
857 let res = ffi::runMinionParallel(
858 cfg,
859 search_opts,
860 search_method,
861 search_instance,
862 Some(parallel_callback_thunk),
863 userdata,
864 );
865
866 ffi::searchMethod_free(search_method);
867 ffi::searchOptions_free(search_opts);
868 ffi::instance_free(search_instance);
869
870 check_minion_result(res)?;
871 Ok(())
872 }
873}
874
875#[allow(clippy::unwrap_used)]
877pub fn run_minion_midsearch_with_options(
878 model: Model,
879 options: RunOptions,
880 callback: MidSearchCallback<'_>,
881) -> Result<SolverContext, MinionError> {
882 unsafe {
883 let ctx = ffi::minion_newContext();
884 let search_opts = ffi::searchOptions_new();
885 let search_method = ffi::searchMethod_new();
886 let search_instance = ffi::instance_new();
887
888 (*search_opts).randomiseValvarorder = options.randomise_order;
892 (*search_opts).silent = true;
893 (*search_opts).print_solution = false;
894
895 if let Some(seed) = options.seed {
896 #[allow(clippy::useless_conversion)]
899 {
900 (*search_method).randomSeed = seed.into();
901 }
902 }
903 (*search_method).preprocess = options.preprocess.to_ffi();
904 (*search_method).propMethod = options.prop_node.to_ffi();
905 if options.node_limit > 0 {
906 (*search_opts).nodelimit = options.node_limit as _;
907 }
908 if let Some(tl) = options.time_limit {
909 (*search_opts).timeoutActive = true;
910 (*search_opts).time_limit = tl.seconds as _;
911 (*search_opts).time_limit_is_CPUTime = tl.is_cpu_time;
912 }
913
914 let mut state = CallbackState {
915 callback,
916 instance: search_instance,
917 print_vars: vec![],
918 midsearch_vars: vec![],
919 };
920
921 convert_model_to_raw(search_instance, &model, &options, &mut state.print_vars)?;
922
923 let userdata = &mut state as *mut CallbackState<'_> as *mut c_void;
924 let res = ffi::runMinion(
925 ctx,
926 search_opts,
927 search_method,
928 search_instance,
929 Some(run_callback),
930 userdata,
931 );
932
933 ffi::searchMethod_free(search_method);
934 ffi::searchOptions_free(search_opts);
935 ffi::instance_free(search_instance);
936
937 match check_minion_result(res) {
938 Ok(()) => Ok(SolverContext { ctx }),
939 Err(e) => {
940 ffi::minion_freeContext(ctx);
941 Err(MinionError::from(e))
942 }
943 }
944 }
945}
946
947unsafe fn convert_model_to_raw(
948 instance: *mut ffi::ProbSpec_CSPInstance,
949 model: &Model,
950 options: &RunOptions,
951 print_vars: &mut Vec<VarName>,
952) -> Result<(), MinionError> {
953 let search_vars = Scoped::new(ffi::vec_var_new(), |x| ffi::vec_var_free(x as _));
967
968 for var_name in model.named_variables.get_variable_order() {
970 let c_str = CString::new(var_name.clone()).map_err(|_| {
971 anyhow!(
972 "Variable name {:?} contains a null character.",
973 var_name.clone()
974 )
975 })?;
976
977 let vartype = model
978 .named_variables
979 .get_vartype(var_name.clone())
980 .ok_or(anyhow!("Could not get var type for {:?}", var_name.clone()))?;
981
982 match vartype {
983 VarDomain::Bound(a, b) => {
984 check_minion_result(ffi::minion_newVar(
985 instance,
986 c_str.as_ptr() as *mut c_char,
987 ffi::VariableType_VAR_BOUND,
988 a,
989 b,
990 ))?;
991 }
992 VarDomain::Discrete(a, b) => {
993 check_minion_result(ffi::minion_newVar(
994 instance,
995 c_str.as_ptr() as *mut c_char,
996 ffi::VariableType_VAR_DISCRETE,
997 a,
998 b,
999 ))?;
1000 }
1001 VarDomain::Bool => {
1002 check_minion_result(ffi::minion_newVar(
1003 instance,
1004 c_str.as_ptr() as *mut c_char,
1005 ffi::VariableType_VAR_BOOL,
1006 0,
1007 1,
1008 ))?;
1009 }
1010 VarDomain::SparseBound(ref vals) => {
1011 let raw = Scoped::new(ffi::vec_int_new(), |x| ffi::vec_int_free(x as _));
1012 for v in vals {
1013 ffi::vec_int_push_back(raw.ptr, *v);
1014 }
1015 check_minion_result(ffi::minion_newSparseBoundVar(
1016 instance,
1017 c_str.as_ptr() as *mut c_char,
1018 raw.ptr,
1019 ))?;
1020 }
1021 x => return Err(MinionError::NotImplemented(format!("{x:?}"))),
1022 };
1023
1024 let var_result = ffi::minion_getVarByName(instance, c_str.as_ptr() as _);
1025 check_minion_result(var_result.result)?;
1026 let var = var_result.var;
1027
1028 ffi::printMatrix_addVar(instance, var);
1029
1030 print_vars.push(var_name.clone());
1032 }
1033
1034 let primary_search_var_names: std::collections::HashSet<VarName> = model
1036 .named_variables
1037 .get_search_variable_order()
1038 .into_iter()
1039 .collect();
1040 for search_var_name in model.named_variables.get_search_variable_order() {
1041 let c_str = CString::new(search_var_name.clone()).map_err(|_| {
1042 anyhow!(
1043 "Variable name {:?} contains a null character.",
1044 search_var_name.clone()
1045 )
1046 })?;
1047 let var_result = ffi::minion_getVarByName(instance, c_str.as_ptr() as _);
1048 check_minion_result(var_result.result)?;
1049 ffi::vec_var_push_back(search_vars.ptr, var_result.var);
1050 }
1051
1052 let search_order = Scoped::new(
1053 ffi::searchOrder_new(search_vars.ptr, options.var_order.to_ffi(), false),
1054 |x| ffi::searchOrder_free(x as _),
1055 );
1056 ffi::searchOrder_setValOrder(search_order.ptr, options.val_order.to_ffi());
1061
1062 ffi::instance_addSearchOrder(instance, search_order.ptr);
1063
1064 let auxiliary_search_vars = Scoped::new(ffi::vec_var_new(), |x| ffi::vec_var_free(x as _));
1072 let mut has_auxiliary_search_vars = false;
1073 for auxiliary_name in model
1074 .named_variables
1075 .get_variable_order()
1076 .into_iter()
1077 .filter(|name| !primary_search_var_names.contains(name))
1078 {
1079 let c_str = CString::new(auxiliary_name.clone())
1080 .map_err(|_| anyhow!("Variable name {auxiliary_name:?} contains a null character"))?;
1081 let var_result = ffi::minion_getVarByName(instance, c_str.as_ptr() as _);
1082 check_minion_result(var_result.result)?;
1083 ffi::vec_var_push_back(auxiliary_search_vars.ptr, var_result.var);
1084 has_auxiliary_search_vars = true;
1085 }
1086 if has_auxiliary_search_vars {
1087 let auxiliary_search_order = Scoped::new(
1088 ffi::searchOrder_new(
1089 auxiliary_search_vars.ptr,
1090 ffi::VarOrderEnum_ORDER_STATIC,
1091 true,
1092 ),
1093 |x| ffi::searchOrder_free(x as _),
1094 );
1095 ffi::instance_addSearchOrder(instance, auxiliary_search_order.ptr);
1096 }
1097
1098 for (name, tuples) in &model.tuple_tables {
1107 let c_name = CString::new(name.clone()).map_err(|_| {
1108 anyhow!(
1109 "Tuple-table name {:?} contains a null character.",
1110 name.clone()
1111 )
1112 })?;
1113
1114 let raw_tuples = Scoped::new(ffi::vec_vec_int_new(), |x| ffi::vec_vec_int_free(x as _));
1119 for tuple in tuples {
1120 let raw_tuple = Scoped::new(ffi::vec_int_new(), |x| ffi::vec_int_free(x as _));
1121 for constant in tuple {
1122 let val = match constant {
1123 Constant::Integer(n) => *n,
1124 Constant::Bool(true) => 1,
1125 Constant::Bool(false) => 0,
1126 #[allow(unreachable_patterns)]
1127 x => return Err(MinionError::NotImplemented(format!("{x:?}"))),
1128 };
1129 ffi::vec_int_push_back(raw_tuple.ptr, val);
1130 }
1131 ffi::vec_vec_int_push_back_ptr(raw_tuples.ptr, raw_tuple.ptr);
1132 }
1133
1134 let raw_tuple_list = ffi::tupleList_new(raw_tuples.ptr);
1138 ffi::instance_addTupleTableSymbol(instance, c_name.as_ptr() as *mut c_char, raw_tuple_list);
1139 }
1140
1141 for constraint in &model.constraints {
1146 let constraint_type = get_constraint_type(constraint)?;
1151 let raw_constraint = Scoped::new(ffi::constraint_new(constraint_type), |x| {
1152 ffi::constraint_free(x as _)
1153 });
1154
1155 constraint_add_args(instance, raw_constraint.ptr, constraint)?;
1156 ffi::instance_addConstraint(instance, raw_constraint.ptr);
1157 }
1158
1159 if let Some(opt) = &model.optimise {
1163 let mut raw_var = resolve_var(instance, &opt.var)?;
1164 ffi::instance_setOptimise(instance, opt.minimise, &mut raw_var);
1165 }
1166
1167 Ok(())
1168}
1169
1170unsafe fn get_constraint_type(constraint: &Constraint) -> Result<u32, MinionError> {
1171 match constraint {
1172 Constraint::SumGeq(_, _) => Ok(ffi::ConstraintType_CT_GEQSUM),
1173 Constraint::SumLeq(_, _) => Ok(ffi::ConstraintType_CT_LEQSUM),
1174 Constraint::Ineq(_, _, _) => Ok(ffi::ConstraintType_CT_INEQ),
1175 Constraint::Eq(_, _) => Ok(ffi::ConstraintType_CT_EQ),
1176 Constraint::Difference(_, _) => Ok(ffi::ConstraintType_CT_DIFFERENCE),
1177 Constraint::Div(_, _) => Ok(ffi::ConstraintType_CT_DIV),
1178 Constraint::DivUndefZero(_, _) => Ok(ffi::ConstraintType_CT_DIV_UNDEFZERO),
1179 Constraint::Modulo(_, _) => Ok(ffi::ConstraintType_CT_MODULO),
1180 Constraint::ModuloUndefZero(_, _) => Ok(ffi::ConstraintType_CT_MODULO_UNDEFZERO),
1181 Constraint::Pow(_, _) => Ok(ffi::ConstraintType_CT_POW),
1182 Constraint::Product(_, _) => Ok(ffi::ConstraintType_CT_PRODUCT2),
1183 Constraint::WeightedSumGeq(_, _, _) => Ok(ffi::ConstraintType_CT_WEIGHTGEQSUM),
1184 Constraint::WeightedSumLeq(_, _, _) => Ok(ffi::ConstraintType_CT_WEIGHTLEQSUM),
1185 Constraint::CheckAssign(_) => Ok(ffi::ConstraintType_CT_CHECK_ASSIGN),
1186 Constraint::CheckGsa(_) => Ok(ffi::ConstraintType_CT_CHECK_GSA),
1187 Constraint::ForwardChecking(_) => Ok(ffi::ConstraintType_CT_FORWARD_CHECKING),
1188 Constraint::Reify(_, _) => Ok(ffi::ConstraintType_CT_REIFY),
1189 Constraint::ReifyImply(_, _) => Ok(ffi::ConstraintType_CT_REIFYIMPLY),
1190 Constraint::ReifyImplyQuick(_, _) => Ok(ffi::ConstraintType_CT_REIFYIMPLY_QUICK),
1191 Constraint::WatchedAnd(_) => Ok(ffi::ConstraintType_CT_WATCHED_NEW_AND),
1192 Constraint::WatchedOr(_) => Ok(ffi::ConstraintType_CT_WATCHED_NEW_OR),
1193 Constraint::GacAllDiff(_) => Ok(ffi::ConstraintType_CT_GACALLDIFF),
1194 Constraint::AllDiff(_) => Ok(ffi::ConstraintType_CT_ALLDIFF),
1195 Constraint::AllDiffMatrix(_, _) => Ok(ffi::ConstraintType_CT_ALLDIFFMATRIX),
1196 Constraint::WatchSumGeq(_, _) => Ok(ffi::ConstraintType_CT_WATCHED_GEQSUM),
1197 Constraint::WatchSumLeq(_, _) => Ok(ffi::ConstraintType_CT_WATCHED_LEQSUM),
1198 Constraint::OccurrenceGeq(_, _, _) => Ok(ffi::ConstraintType_CT_GEQ_OCCURRENCE),
1199 Constraint::OccurrenceLeq(_, _, _) => Ok(ffi::ConstraintType_CT_LEQ_OCCURRENCE),
1200 Constraint::Occurrence(_, _, _) => Ok(ffi::ConstraintType_CT_OCCURRENCE),
1201 Constraint::LitSumGeq(_, _, _) => Ok(ffi::ConstraintType_CT_WATCHED_LITSUM),
1202 Constraint::Gcc(_, _, _) => Ok(ffi::ConstraintType_CT_GCC),
1203 Constraint::GccWeak(_, _, _) => Ok(ffi::ConstraintType_CT_GCCWEAK),
1204 Constraint::LexLeqRv(_, _) => Ok(ffi::ConstraintType_CT_GACLEXLEQ),
1205 Constraint::LexLeq(_, _) => Ok(ffi::ConstraintType_CT_LEXLEQ),
1206 Constraint::LexLess(_, _) => Ok(ffi::ConstraintType_CT_LEXLESS),
1207 Constraint::LexLeqQuick(_, _) => Ok(ffi::ConstraintType_CT_QUICK_LEXLEQ),
1208 Constraint::LexLessQuick(_, _) => Ok(ffi::ConstraintType_CT_QUICK_LEXLESS),
1209 Constraint::WatchVecNeq(_, _) => Ok(ffi::ConstraintType_CT_WATCHED_VECNEQ),
1210 Constraint::WatchVecExistsLess(_, _) => Ok(ffi::ConstraintType_CT_WATCHED_VEC_OR_LESS),
1211 Constraint::Hamming(_, _, _) => Ok(ffi::ConstraintType_CT_WATCHED_HAMMING),
1212 Constraint::NotHamming(_, _, _) => Ok(ffi::ConstraintType_CT_WATCHED_NOT_HAMMING),
1213 Constraint::FrameUpdate(_, _, _, _, _) => Ok(ffi::ConstraintType_CT_FRAMEUPDATE),
1214 Constraint::NegativeTable(_, _) => Ok(ffi::ConstraintType_CT_WATCHED_NEGATIVE_TABLE),
1215 Constraint::Table(_, _) => Ok(ffi::ConstraintType_CT_WATCHED_TABLE),
1216 Constraint::GacSchema(_, _) => Ok(ffi::ConstraintType_CT_GACSCHEMA),
1217 Constraint::LightTable(_, _) => Ok(ffi::ConstraintType_CT_LIGHTTABLE),
1218 Constraint::Mddc(_, _) => Ok(ffi::ConstraintType_CT_MDDC),
1219 Constraint::NegativeMddc(_, _) => Ok(ffi::ConstraintType_CT_NEGATIVEMDDC),
1220 Constraint::Str2Plus(_, _) => Ok(ffi::ConstraintType_CT_STR),
1221 Constraint::ShortStr2(_, _) => Ok(ffi::ConstraintType_CT_SHORTSTR),
1222 Constraint::HaggisGac(_, _) => Ok(ffi::ConstraintType_CT_HAGGISGAC),
1223 Constraint::HaggisGacStable(_, _) => Ok(ffi::ConstraintType_CT_HAGGISGAC_STABLE),
1224 Constraint::ShortCTupleStr2(_, _) => Ok(ffi::ConstraintType_CT_SHORTSTR_CTUPLE),
1225 Constraint::Max(_, _) => Ok(ffi::ConstraintType_CT_MAX),
1226 Constraint::Min(_, _) => Ok(ffi::ConstraintType_CT_MIN),
1227 Constraint::NvalueGeq(_, _) => Ok(ffi::ConstraintType_CT_GEQNVALUE),
1228 Constraint::NvalueLeq(_, _) => Ok(ffi::ConstraintType_CT_LEQNVALUE),
1229 Constraint::Element(_, _, _) => Ok(ffi::ConstraintType_CT_ELEMENT),
1230 Constraint::ElementOne(_, _, _) => Ok(ffi::ConstraintType_CT_ELEMENT_ONE),
1231 Constraint::ElementUndefZero(_, _, _) => Ok(ffi::ConstraintType_CT_ELEMENT_UNDEFZERO),
1232 Constraint::WatchElement(_, _, _) => Ok(ffi::ConstraintType_CT_WATCHED_ELEMENT),
1233 Constraint::WatchElementOne(_, _, _) => Ok(ffi::ConstraintType_CT_WATCHED_ELEMENT_ONE),
1234 Constraint::WatchElementOneUndefZero(_, _, _) => {
1235 Ok(ffi::ConstraintType_CT_WATCHED_ELEMENT_ONE_UNDEFZERO)
1236 }
1237 Constraint::WatchElementUndefZero(_, _, _) => {
1238 Ok(ffi::ConstraintType_CT_WATCHED_ELEMENT_UNDEFZERO)
1239 }
1240 Constraint::WLiteral(_, _) => Ok(ffi::ConstraintType_CT_WATCHED_LIT),
1241 Constraint::WNotLiteral(_, _) => Ok(ffi::ConstraintType_CT_WATCHED_NOTLIT),
1242 Constraint::WInIntervalSet(_, _) => Ok(ffi::ConstraintType_CT_WATCHED_ININTERVALSET),
1243 Constraint::WInRange(_, _) => Ok(ffi::ConstraintType_CT_WATCHED_INRANGE),
1244 Constraint::WInset(_, _) => Ok(ffi::ConstraintType_CT_WATCHED_INSET),
1245 Constraint::WNotInRange(_, _) => Ok(ffi::ConstraintType_CT_WATCHED_NOT_INRANGE),
1246 Constraint::WNotInset(_, _) => Ok(ffi::ConstraintType_CT_WATCHED_NOT_INSET),
1247 Constraint::Abs(_, _) => Ok(ffi::ConstraintType_CT_ABS),
1248 Constraint::DisEq(_, _) => Ok(ffi::ConstraintType_CT_DISEQ),
1249 Constraint::MinusEq(_, _) => Ok(ffi::ConstraintType_CT_MINUSEQ),
1250 Constraint::GacEq(_, _) => Ok(ffi::ConstraintType_CT_GACEQ),
1251 Constraint::WatchLess(_, _) => Ok(ffi::ConstraintType_CT_WATCHED_LESS),
1252 Constraint::WatchNeq(_, _) => Ok(ffi::ConstraintType_CT_WATCHED_NEQ),
1253 Constraint::True => Ok(ffi::ConstraintType_CT_TRUE),
1254 Constraint::False => Ok(ffi::ConstraintType_CT_FALSE),
1255
1256 #[allow(unreachable_patterns)]
1257 x => Err(MinionError::NotImplemented(format!(
1258 "Constraint not implemented {x:?}",
1259 ))),
1260 }
1261}
1262
1263unsafe fn constraint_add_args(
1264 i: *mut ffi::ProbSpec_CSPInstance,
1265 r_constr: *mut ffi::ProbSpec_ConstraintBlob,
1266 constr: &Constraint,
1267) -> Result<(), MinionError> {
1268 match constr {
1269 Constraint::SumGeq(lhs_vars, rhs_var) => {
1270 read_list(i, r_constr, lhs_vars)?;
1271 read_var(i, r_constr, rhs_var)?;
1272 Ok(())
1273 }
1274 Constraint::SumLeq(lhs_vars, rhs_var) => {
1275 read_list(i, r_constr, lhs_vars)?;
1276 read_var(i, r_constr, rhs_var)?;
1277 Ok(())
1278 }
1279 Constraint::Ineq(var1, var2, c) => {
1280 read_var(i, r_constr, var1)?;
1281 read_var(i, r_constr, var2)?;
1282 read_constant(r_constr, c)?;
1283 Ok(())
1284 }
1285 Constraint::Eq(var1, var2) => {
1286 read_var(i, r_constr, var1)?;
1287 read_var(i, r_constr, var2)?;
1288 Ok(())
1289 }
1290 Constraint::Difference((a, b), c) => {
1291 read_2_vars(i, r_constr, a, b)?;
1292 read_var(i, r_constr, c)?;
1293 Ok(())
1294 }
1295 Constraint::Div((a, b), c) => {
1296 read_2_vars(i, r_constr, a, b)?;
1297 read_var(i, r_constr, c)?;
1298 Ok(())
1299 }
1300 Constraint::DivUndefZero((a, b), c) => {
1301 read_2_vars(i, r_constr, a, b)?;
1302 read_var(i, r_constr, c)?;
1303 Ok(())
1304 }
1305 Constraint::Modulo((a, b), c) => {
1306 read_2_vars(i, r_constr, a, b)?;
1307 read_var(i, r_constr, c)?;
1308 Ok(())
1309 }
1310 Constraint::ModuloUndefZero((a, b), c) => {
1311 read_2_vars(i, r_constr, a, b)?;
1312 read_var(i, r_constr, c)?;
1313 Ok(())
1314 }
1315 Constraint::Pow((a, b), c) => {
1316 read_2_vars(i, r_constr, a, b)?;
1317 read_var(i, r_constr, c)?;
1318 Ok(())
1319 }
1320 Constraint::Product((a, b), c) => {
1321 read_2_vars(i, r_constr, a, b)?;
1322 read_var(i, r_constr, c)?;
1323 Ok(())
1324 }
1325 Constraint::WeightedSumGeq(a, b, c) => {
1326 read_constant_list(r_constr, a)?;
1327 read_list(i, r_constr, b)?;
1328 read_var(i, r_constr, c)?;
1329 Ok(())
1330 }
1331 Constraint::WeightedSumLeq(a, b, c) => {
1332 read_constant_list(r_constr, a)?;
1333 read_list(i, r_constr, b)?;
1334 read_var(i, r_constr, c)?;
1335 Ok(())
1336 }
1337 Constraint::CheckAssign(a) => {
1338 read_constraint(i, r_constr, (**a).clone())?;
1339 Ok(())
1340 }
1341 Constraint::CheckGsa(a) => {
1342 read_constraint(i, r_constr, (**a).clone())?;
1343 Ok(())
1344 }
1345 Constraint::ForwardChecking(a) => {
1346 read_constraint(i, r_constr, (**a).clone())?;
1347 Ok(())
1348 }
1349 Constraint::Reify(a, b) => {
1350 read_constraint(i, r_constr, (**a).clone())?;
1351 read_var(i, r_constr, b)?;
1352 Ok(())
1353 }
1354 Constraint::ReifyImply(a, b) => {
1355 read_constraint(i, r_constr, (**a).clone())?;
1356 read_var(i, r_constr, b)?;
1357 Ok(())
1358 }
1359 Constraint::ReifyImplyQuick(a, b) => {
1360 read_constraint(i, r_constr, (**a).clone())?;
1361 read_var(i, r_constr, b)?;
1362 Ok(())
1363 }
1364 Constraint::WatchedAnd(a) => {
1365 read_constraint_list(i, r_constr, a)?;
1366 Ok(())
1367 }
1368 Constraint::WatchedOr(a) => {
1369 read_constraint_list(i, r_constr, a)?;
1370 Ok(())
1371 }
1372 Constraint::GacAllDiff(a) => {
1373 read_list(i, r_constr, a)?;
1374 Ok(())
1375 }
1376 Constraint::AllDiff(a) => {
1377 read_list(i, r_constr, a)?;
1378 Ok(())
1379 }
1380 Constraint::AllDiffMatrix(a, b) => {
1381 read_list(i, r_constr, a)?;
1382 read_constant(r_constr, b)?;
1383 Ok(())
1384 }
1385 Constraint::WatchSumGeq(a, b) => {
1386 read_list(i, r_constr, a)?;
1387 read_constant(r_constr, b)?;
1388 Ok(())
1389 }
1390 Constraint::WatchSumLeq(a, b) => {
1391 read_list(i, r_constr, a)?;
1392 read_constant(r_constr, b)?;
1393 Ok(())
1394 }
1395 Constraint::OccurrenceGeq(a, b, c) => {
1396 read_list(i, r_constr, a)?;
1397 read_constant(r_constr, b)?;
1398 read_constant(r_constr, c)?;
1399 Ok(())
1400 }
1401 Constraint::OccurrenceLeq(a, b, c) => {
1402 read_list(i, r_constr, a)?;
1403 read_constant(r_constr, b)?;
1404 read_constant(r_constr, c)?;
1405 Ok(())
1406 }
1407 Constraint::Occurrence(a, b, c) => {
1408 read_list(i, r_constr, a)?;
1409 read_constant(r_constr, b)?;
1410 read_var(i, r_constr, c)?;
1411 Ok(())
1412 }
1413 Constraint::LexLess(a, b)
1414 | Constraint::LexLeq(a, b)
1415 | Constraint::LexLeqRv(a, b)
1416 | Constraint::LexLeqQuick(a, b)
1417 | Constraint::LexLessQuick(a, b)
1418 | Constraint::WatchVecNeq(a, b)
1419 | Constraint::WatchVecExistsLess(a, b) => {
1420 read_list(i, r_constr, a)?;
1421 read_list(i, r_constr, b)?;
1422 Ok(())
1423 }
1424 Constraint::LitSumGeq(a, b, c) => {
1425 read_list(i, r_constr, a)?;
1426 read_constant_list(r_constr, b)?;
1427 read_constant(r_constr, c)?;
1428 Ok(())
1429 }
1430 Constraint::Gcc(a, b, c) | Constraint::GccWeak(a, b, c) => {
1431 read_list(i, r_constr, a)?;
1432 read_constant_list(r_constr, b)?;
1433 read_list(i, r_constr, c)?;
1434 Ok(())
1435 }
1436 Constraint::Hamming(a, b, c) | Constraint::NotHamming(a, b, c) => {
1437 read_list(i, r_constr, a)?;
1438 read_list(i, r_constr, b)?;
1439 read_constant(r_constr, c)?;
1440 Ok(())
1441 }
1442 Constraint::FrameUpdate(a, b, c, d, e) => {
1443 read_list(i, r_constr, a)?;
1444 read_list(i, r_constr, b)?;
1445 read_list(i, r_constr, c)?;
1446 read_list(i, r_constr, d)?;
1447 read_constant(r_constr, e)?;
1448 Ok(())
1449 }
1450 Constraint::NegativeTable(vars, tuple_list)
1451 | Constraint::Table(vars, tuple_list)
1452 | Constraint::GacSchema(vars, tuple_list)
1453 | Constraint::LightTable(vars, tuple_list)
1454 | Constraint::Mddc(vars, tuple_list)
1455 | Constraint::NegativeMddc(vars, tuple_list) => {
1456 read_list(i, r_constr, vars)?;
1457 read_tuple_list(r_constr, tuple_list)?;
1458 Ok(())
1459 }
1460 Constraint::Str2Plus(vars, table_var) => {
1461 read_list(i, r_constr, vars)?;
1468 let name = match table_var {
1469 Var::NameRef(n) => n.clone(),
1470 other => {
1471 return Err(MinionError::NotImplemented(format!(
1472 "Str2Plus second argument must be Var::NameRef(tuple_table_name), got {other:?}"
1473 )));
1474 }
1475 };
1476 let c_name = CString::new(name.clone())
1477 .map_err(|_| anyhow!("Tuple-table name {name:?} contains a null character."))?;
1478 ffi::constraint_setTuplesByName(r_constr, i, c_name.as_ptr());
1479 Ok(())
1480 }
1481 Constraint::ShortStr2(vars, short_tuples)
1482 | Constraint::HaggisGac(vars, short_tuples)
1483 | Constraint::HaggisGacStable(vars, short_tuples)
1484 | Constraint::ShortCTupleStr2(vars, short_tuples) => {
1485 read_list(i, r_constr, vars)?;
1486 read_short_tuple_list(r_constr, short_tuples)?;
1487 Ok(())
1488 }
1489 Constraint::Max(a, b)
1490 | Constraint::Min(a, b)
1491 | Constraint::NvalueGeq(a, b)
1492 | Constraint::NvalueLeq(a, b) => {
1493 read_list(i, r_constr, a)?;
1494 read_var(i, r_constr, b)?;
1495 Ok(())
1496 }
1497 Constraint::Element(vec, j, e)
1498 | Constraint::ElementOne(vec, j, e)
1499 | Constraint::ElementUndefZero(vec, j, e)
1500 | Constraint::WatchElement(vec, j, e)
1501 | Constraint::WatchElementOne(vec, j, e)
1502 | Constraint::WatchElementOneUndefZero(vec, j, e)
1503 | Constraint::WatchElementUndefZero(vec, j, e) => {
1504 read_list(i, r_constr, vec)?;
1505 read_var(i, r_constr, j)?;
1506 read_var(i, r_constr, e)?;
1507 Ok(())
1508 }
1509 Constraint::WLiteral(a, b) | Constraint::WNotLiteral(a, b) => {
1510 read_var(i, r_constr, a)?;
1511 read_constant(r_constr, b)?;
1512 Ok(())
1513 }
1514 Constraint::WInIntervalSet(var, consts)
1515 | Constraint::WInRange(var, consts)
1516 | Constraint::WNotInRange(var, consts)
1517 | Constraint::WInset(var, consts)
1518 | Constraint::WNotInset(var, consts) => {
1519 read_var(i, r_constr, var)?;
1520 read_constant_list(r_constr, consts)?;
1521 Ok(())
1522 }
1523 Constraint::Abs(a, b)
1524 | Constraint::DisEq(a, b)
1525 | Constraint::MinusEq(a, b)
1526 | Constraint::GacEq(a, b)
1527 | Constraint::WatchLess(a, b)
1528 | Constraint::WatchNeq(a, b) => {
1529 read_var(i, r_constr, a)?;
1530 read_var(i, r_constr, b)?;
1531 Ok(())
1532 }
1533 Constraint::AllDiffMatrix(vars, c) => {
1534 read_list(i, r_constr, vars)?;
1535 read_constant(r_constr, c)?;
1536 Ok(())
1537 }
1538
1539 Constraint::True => Ok(()),
1540 Constraint::False => Ok(()),
1541 #[allow(unreachable_patterns)]
1542 x => Err(MinionError::NotImplemented(format!("{x:?}"))),
1543 }
1544}
1545
1546unsafe fn resolve_var(
1550 instance: *mut ffi::ProbSpec_CSPInstance,
1551 var: &Var,
1552) -> Result<ffi::ProbSpec_Var, MinionError> {
1553 match var {
1554 Var::NameRef(name) => {
1555 let c_str = CString::new(name.clone()).map_err(|_| {
1556 anyhow!(
1557 "Variable name {:?} contains a null character.",
1558 name.clone()
1559 )
1560 })?;
1561 let var_result = ffi::minion_getVarByName(instance, c_str.as_ptr() as _);
1562 check_minion_result(var_result.result)?;
1563 Ok(var_result.var)
1564 }
1565 Var::ConstantAsVar(n) => Ok(ffi::constantAsVar(*n)),
1566 }
1567}
1568
1569unsafe fn read_list(
1570 instance: *mut ffi::ProbSpec_CSPInstance,
1571 raw_constraint: *mut ffi::ProbSpec_ConstraintBlob,
1572 vars: &Vec<Var>,
1573) -> Result<(), MinionError> {
1574 let raw_vars = Scoped::new(ffi::vec_var_new(), |x| ffi::vec_var_free(x as _));
1575 for var in vars {
1576 let raw_var = resolve_var(instance, var)?;
1577 ffi::vec_var_push_back(raw_vars.ptr, raw_var);
1578 }
1579
1580 ffi::constraint_addList(raw_constraint, raw_vars.ptr);
1581
1582 Ok(())
1583}
1584
1585unsafe fn read_var(
1586 instance: *mut ffi::ProbSpec_CSPInstance,
1587 raw_constraint: *mut ffi::ProbSpec_ConstraintBlob,
1588 var: &Var,
1589) -> Result<(), MinionError> {
1590 let raw_vars = Scoped::new(ffi::vec_var_new(), |x| ffi::vec_var_free(x as _));
1591 let raw_var = resolve_var(instance, var)?;
1592 ffi::vec_var_push_back(raw_vars.ptr, raw_var);
1593 ffi::constraint_addList(raw_constraint, raw_vars.ptr);
1594
1595 Ok(())
1596}
1597
1598unsafe fn read_2_vars(
1599 instance: *mut ffi::ProbSpec_CSPInstance,
1600 raw_constraint: *mut ffi::ProbSpec_ConstraintBlob,
1601 var1: &Var,
1602 var2: &Var,
1603) -> Result<(), MinionError> {
1604 let mut raw_var = resolve_var(instance, var1)?;
1605 let mut raw_var2 = resolve_var(instance, var2)?;
1606 ffi::constraint_addTwoVars(raw_constraint, &mut raw_var, &mut raw_var2);
1610 Ok(())
1611}
1612
1613unsafe fn read_constant(
1614 raw_constraint: *mut ffi::ProbSpec_ConstraintBlob,
1615 constant: &Constant,
1616) -> Result<(), MinionError> {
1617 let val: i32 = match constant {
1618 Constant::Integer(n) => Ok(*n),
1619 Constant::Bool(true) => Ok(1),
1620 Constant::Bool(false) => Ok(0),
1621 x => Err(MinionError::NotImplemented(format!("{x:?}"))),
1622 }?;
1623
1624 ffi::constraint_addConstant(raw_constraint, val);
1625
1626 Ok(())
1627}
1628
1629unsafe fn read_constant_list(
1630 raw_constraint: *mut ffi::ProbSpec_ConstraintBlob,
1631 constants: &[Constant],
1632) -> Result<(), MinionError> {
1633 let raw_consts = Scoped::new(ffi::vec_int_new(), |x| ffi::vec_int_free(x as _));
1634
1635 for constant in constants.iter() {
1636 let val = match constant {
1637 Constant::Integer(n) => Ok(*n),
1638 Constant::Bool(true) => Ok(1),
1639 Constant::Bool(false) => Ok(0),
1640 #[allow(unreachable_patterns)] x => Err(MinionError::NotImplemented(format!("{x:?}"))),
1642 }?;
1643
1644 ffi::vec_int_push_back(raw_consts.ptr, val);
1645 }
1646
1647 ffi::constraint_addConstantList(raw_constraint, raw_consts.ptr);
1648 Ok(())
1649}
1650
1651unsafe fn read_constraint(
1655 instance: *mut ffi::ProbSpec_CSPInstance,
1656 raw_constraint: *mut ffi::ProbSpec_ConstraintBlob,
1657 inner_constraint: Constraint,
1658) -> Result<(), MinionError> {
1659 let constraint_type = get_constraint_type(&inner_constraint)?;
1660 let raw_inner_constraint = Scoped::new(ffi::constraint_new(constraint_type), |x| {
1661 ffi::constraint_free(x as _)
1662 });
1663
1664 constraint_add_args(instance, raw_inner_constraint.ptr, &inner_constraint)?;
1665
1666 ffi::constraint_addConstraint(raw_constraint, raw_inner_constraint.ptr);
1667 Ok(())
1668}
1669
1670unsafe fn read_constraint_list(
1671 instance: *mut ffi::ProbSpec_CSPInstance,
1672 raw_constraint: *mut ffi::ProbSpec_ConstraintBlob,
1673 inner_constraints: &[Constraint],
1674) -> Result<(), MinionError> {
1675 let raw_inners = Scoped::new(ffi::vec_constraints_new(), |x| {
1676 ffi::vec_constraints_free(x as _)
1677 });
1678 for inner_constraint in inner_constraints.iter() {
1679 let constraint_type = get_constraint_type(inner_constraint)?;
1680 let raw_inner_constraint = Scoped::new(ffi::constraint_new(constraint_type), |x| {
1681 ffi::constraint_free(x as _)
1682 });
1683
1684 constraint_add_args(instance, raw_inner_constraint.ptr, inner_constraint)?;
1685 ffi::vec_constraints_push_back(raw_inners.ptr, raw_inner_constraint.ptr);
1686 }
1687
1688 ffi::constraint_addConstraintList(raw_constraint, raw_inners.ptr);
1689 Ok(())
1690}
1691
1692unsafe fn read_tuple_list(
1693 raw_constraint: *mut ffi::ProbSpec_ConstraintBlob,
1694 tuples: &Vec<Tuple>,
1695) -> Result<(), MinionError> {
1696 let raw_tuples = Scoped::new(ffi::vec_vec_int_new(), |x| ffi::vec_vec_int_free(x as _));
1698 for tuple in tuples {
1699 let raw_tuple = Scoped::new(ffi::vec_int_new(), |x| ffi::vec_int_free(x as _));
1700 for constant in tuple.iter() {
1701 let val = match constant {
1702 Constant::Integer(n) => Ok(*n),
1703 Constant::Bool(true) => Ok(1),
1704 Constant::Bool(false) => Ok(0),
1705 #[allow(unreachable_patterns)] x => Err(MinionError::NotImplemented(format!("{:?}", x))),
1707 }?;
1708
1709 ffi::vec_int_push_back(raw_tuple.ptr, val);
1710 }
1711
1712 ffi::vec_vec_int_push_back_ptr(raw_tuples.ptr, raw_tuple.ptr);
1713 }
1714
1715 let raw_tuple_list = ffi::tupleList_new(raw_tuples.ptr);
1718 ffi::constraint_setTuples(raw_constraint, raw_tuple_list);
1719
1720 Ok(())
1721}
1722
1723unsafe fn read_short_tuple_list(
1729 raw_constraint: *mut ffi::ProbSpec_ConstraintBlob,
1730 short_tuples: &Vec<ShortTuple>,
1731) -> Result<(), MinionError> {
1732 let raw_outer = Scoped::new(ffi::vec_vec_int_new(), |x| ffi::vec_vec_int_free(x as _));
1733 for short in short_tuples {
1734 let raw_inner = Scoped::new(ffi::vec_int_new(), |x| ffi::vec_int_free(x as _));
1735 for &(idx, ref constant) in short {
1736 let val = match constant {
1737 Constant::Integer(n) => Ok(*n),
1738 Constant::Bool(true) => Ok(1),
1739 Constant::Bool(false) => Ok(0),
1740 #[allow(unreachable_patterns)]
1741 x => Err(MinionError::NotImplemented(format!("{:?}", x))),
1742 }?;
1743 ffi::vec_int_push_back(raw_inner.ptr, idx as i32);
1744 ffi::vec_int_push_back(raw_inner.ptr, val);
1745 }
1746 ffi::vec_vec_int_push_back_ptr(raw_outer.ptr, raw_inner.ptr);
1747 }
1748
1749 let raw_short_list = ffi::shortTupleList_new(raw_outer.ptr);
1752 ffi::constraint_setShortTuples(raw_constraint, raw_short_list);
1753
1754 Ok(())
1755}