1use crate::utils::{Handle, HandleT};
2use crate::variables::{IntVar, NewIntVarT};
3use crate::{BoolVar, CHOCO_BACKEND, CHOCO_LIB};
4
5pub struct Model {
10 handle: Handle,
11}
12
13impl Model {
14 #[must_use]
19 pub fn new(name: Option<&str>) -> Self {
20 let model_handle = unsafe {
24 match name {
25 Some(n) => {
26 let c_name =
27 std::ffi::CString::new(n).expect("Failed to convert name to CString");
28 let model_handle = CHOCO_BACKEND.with(|backend| {
29 CHOCO_LIB.Java_org_chocosolver_capi_ModelApi_createModel_s(
30 backend.thread,
31 c_name.as_ptr().cast_mut(),
32 )
33 });
34
35 assert!(
36 !model_handle.is_null(),
37 "Failed to create model with name: received null handle"
38 );
39
40 model_handle
41 }
42 None => {
43 let model_handle = CHOCO_BACKEND.with(|backend| {
44 CHOCO_LIB.Java_org_chocosolver_capi_ModelApi_createModel(backend.thread)
45 });
46 assert!(
47 !model_handle.is_null(),
48 "Failed to create model: received null handle"
49 );
50 model_handle
51 }
52 }
53 };
54 Model {
55 handle: Handle::new(model_handle),
56 }
57 }
58
59 #[must_use]
61 pub fn name(&self) -> Option<String> {
62 unsafe {
65 let name_ptr = CHOCO_BACKEND.with(|backend| {
66 CHOCO_LIB.Java_org_chocosolver_capi_ModelApi_getName(
67 backend.thread,
68 self.handle.get_raw_handle(),
69 )
70 });
71 if name_ptr.is_null() {
72 None
73 } else {
74 let c_str = std::ffi::CStr::from_ptr(name_ptr);
75 Some(c_str.to_string_lossy().into_owned())
76 }
77 }
78 }
79
80 #[must_use]
85 #[allow(private_bounds)]
86 pub fn int_var<'b, X>(&'b self, x: X, name: Option<&str>) -> IntVar<'b>
87 where
88 for<'a> (X, Option<&'a str>): NewIntVarT,
89 {
90 (x, name).create_int_var(self)
91 }
92
93 #[must_use]
97 #[allow(private_bounds)]
98 pub fn int_var_bounded<'b, X, Y>(
99 &'b self,
100 x: X,
101 y: Y,
102 name: Option<&str>,
103 bounded: Option<bool>,
104 ) -> IntVar<'b>
105 where
106 for<'a> (X, Y, Option<&'a str>, bool): NewIntVarT,
107 for<'a> (X, Y, Option<&'a str>): NewIntVarT,
108 {
109 match bounded {
110 Some(v) => (x, y, name, v).create_int_var(self),
111 None => (x, y, name).create_int_var(self),
112 }
113 }
114 #[must_use]
115 pub fn bool_var<'model>(
116 &'model self,
117 value: Option<bool>,
118 name: Option<&str>,
119 ) -> BoolVar<'model> {
120 BoolVar::new(self, value, name)
121 }
122
123 #[must_use]
124 pub fn solver(&self) -> crate::solver::Solver<'_> {
125 crate::solver::Solver::new(self)
126 }
127}
128
129impl HandleT for Model {
130 fn get_raw_handle(&self) -> *mut std::os::raw::c_void {
131 self.handle.get_raw_handle()
132 }
133}
134
135#[cfg(test)]
136mod tests {
137 use crate::constraint::{ArithmeticOperator, EqualityOperator};
138
139 use super::Model;
140
141 #[test]
142 fn test_model_basic() {
143 let model = Model::new(Some("TestModel"));
144 assert_eq!(model.name().as_deref(), Some("TestModel"));
145
146 let unnamed_model = Model::new(None);
147
148 let a = unnamed_model.int_var_bounded(-100, 100, Some("var1"), None);
149 let b = unnamed_model.int_var_bounded(-100, 100, Some("var2"), None);
150 let c = unnamed_model.int_var_bounded(0, 50, Some("var3"), None);
151 assert!(a.arithm(EqualityOperator::Eq, 32i32).post().is_ok());
152 assert!(
159 a.arithm2(EqualityOperator::Eq, &b, ArithmeticOperator::Sum, &c)
160 .post()
161 .is_ok()
162 );
163 let solver = unnamed_model.solver();
164 let solution = solver
165 .find_solution(&Default::default())
166 .expect("Expected to find a solution");
167 println!(
168 "Solution: var1 = {}, var2 = {}, var3 = {}",
169 solution.get_int_var(&a).unwrap(),
170 solution.get_int_var(&b).unwrap(),
171 solution.get_int_var(&c).unwrap()
172 );
173 }
174}