1use std::ffi::{c_char, c_int, c_void, c_double, c_uint, CStr, CString};
2use std::panic::catch_unwind;
3use std::ptr::{null, copy_nonoverlapping};
4
5use crate::shunting::{ContextHashMap, new_context, ContextLike};
6use crate::solve_equation_with_context;
7use crate::system::{System, SystemBuilder, ConstrainResult};
8
9unsafe fn new_owned_string(s: *const c_char) -> String
11{
12 let c_str = CStr::from_ptr(s);
13 String::from_utf8_lossy(c_str.to_bytes()).to_string()
14}
15
16#[inline]
18fn leak_object<T>(obj: T) -> *mut T
19{
20 Box::into_raw(Box::new(obj))
21}
22
23#[inline]
25unsafe fn destroy_object<T>(p_obj: *mut T)
26{
27 let _dropper = Box::from_raw(p_obj);
28}
29
30#[no_mangle]
32pub unsafe extern "C" fn new_context_hash_map() -> *mut c_void
33{
34 leak_object(ContextHashMap::new()) as *mut c_void
35}
36
37#[no_mangle]
39pub unsafe extern "C" fn new_default_context_hash_map() -> *mut c_void
40{
41 leak_object(new_context()) as *mut c_void
42}
43
44#[no_mangle]
46pub unsafe extern "C" fn add_const_to_ctx(context: *mut c_void, name: *const c_char, val: c_double)
47{
48 let name_str = new_owned_string(name);
49 (*(context as *mut ContextHashMap)).add_const_to_ctx(&name_str, val)
50}
51
52#[no_mangle]
55pub extern "C" fn solve_equation(equation: *const c_char, context: *const c_void, guess: c_double, min: c_double, max: c_double, margin: c_double, limit: c_uint) -> *const c_char
56{
57 let res = catch_unwind(|| {
58 let equation_str = unsafe { new_owned_string(equation) };
59
60 let mut ctx = ContextHashMap::new();
61 unsafe { copy_nonoverlapping(context as *const ContextHashMap, &mut ctx, 1) };
62
63 let (var, val) = match solve_equation_with_context(&equation_str, &mut ctx, guess, min, max, margin, limit as usize)
64 {
65 Ok(s) => s,
66 Err(_) => return null() as *const c_char,
67 };
68
69 let soln_str: CString = CString::new(format!("{}={}", var, val))
71 .expect("failed to create C-compatible solution string!");
72
73 soln_str.into_raw()
74 });
75
76 match res
77 {
78 Ok(s) => s,
79 Err(_) => null() as *const c_char,
80 }
81}
82
83#[no_mangle]
85pub extern "C" fn new_system_builder(equation: *const c_char, context: *const c_void) -> *const c_void
86{
87 let res = catch_unwind(|| {
88 let equation_str = unsafe { new_owned_string(equation) };
89
90 let ctx = unsafe { (*(context as *const ContextHashMap)).clone() };
91
92 let builder = match SystemBuilder::new(&equation_str, ctx)
93 {
94 Ok(x) => x,
95 Err(_) => return null(),
96 };
97
98 leak_object(builder)
99 });
100
101 match res
102 {
103 Ok(p) => p as *const c_void,
104 Err(_) => null(),
105 }
106}
107
108#[no_mangle]
116pub extern "C" fn try_constrain_with(p_builder: *mut c_void, equation: *const c_char) -> c_int
117{
118 let res = catch_unwind(|| {
119 let builder = p_builder as *mut SystemBuilder;
120 let equation_str = unsafe { new_owned_string(equation) };
121 let constrain_res = unsafe { (*builder).try_constrain_with(&equation_str) };
122
123 match constrain_res
124 {
125 Ok(ConstrainResult::WillConstrain) => 1,
126 Ok(ConstrainResult::WillNotConstrain) => 0,
127 Ok(ConstrainResult::WillOverConstrain) => 2,
128 Err(_) => -1
129 }
130 });
131
132 res.unwrap_or(-1)
133}
134
135#[no_mangle]
141pub extern "C" fn is_fully_constrained(p_builder: *mut c_void) -> c_int
142{
143 let res = catch_unwind(|| {
144 unsafe{ (*(p_builder as *mut SystemBuilder)).is_fully_constrained() }
145 });
146
147 match res
148 {
149 Ok(x) => if x { 1 } else { 0 },
150 Err(_) => -1,
151 }
152}
153
154#[no_mangle]
157pub extern "C" fn build_system(p_builder: *mut c_void) -> *const c_void
158{
159 let res = catch_unwind(|| {
160 let builder = unsafe { Box::from_raw(p_builder as *mut SystemBuilder) };
161 let system = match builder.build_system()
162 {
163 Some(s) => s,
164 None => return null(),
165 };
166
167 leak_object(system)
168 });
169
170 match res
171 {
172 Ok(p) => p as *const c_void,
173 Err(_) => null(),
174 }
175}
176
177#[no_mangle]
179pub unsafe extern "C" fn debug_system_builder(p_builder: *const c_void)
180{
181 println!("{:#?}", *(p_builder as *const SystemBuilder));
182}
183
184#[no_mangle]
190pub extern "C" fn specify_variable(p_system: *mut c_void, var: *const c_char, guess: c_double, min: c_double, max: c_double) -> c_int
191{
192 let res = catch_unwind(|| {
193 unsafe
194 {
195 let var_str = new_owned_string(var);
196 (*(p_system as *mut System)).specify_variable(&var_str, guess, min, max);
197 }
198 });
199
200 match res
201 {
202 Ok(_) => 1,
203 Err(_) => -1,
204 }
205}
206
207#[no_mangle]
211pub extern "C" fn solve_system(p_system: *mut c_void, margin: c_double, limit: c_uint) -> *const c_char
212{
213 let res = catch_unwind(|| {
214 let system = unsafe { Box::from_raw(p_system as *mut System) };
215
216 let soln = match system.solve(margin, limit as usize)
217 {
218 Ok(s) => s,
219 Err(_) => return null() as *const c_char,
220 };
221
222 let soln_str: CString = CString::new(
224 soln.iter()
225 .map(|(var, val)| format!("{}={}", var, val))
226 .collect::<Vec<String>>()
227 .join("\n")
228 ).expect("failed to create C-compatible solution string!");
229
230 soln_str.into_raw()
231 });
232
233 match res
234 {
235 Ok(s) => s,
236 Err(_) => null() as *const c_char,
237 }
238}
239
240#[no_mangle]
242pub unsafe extern "C" fn free_context_hash_map(p_context: *mut c_void)
243{
244 destroy_object(p_context as *mut ContextHashMap);
245}
246
247#[no_mangle]
249pub unsafe extern "C" fn free_system_builder(p_builder: *mut c_void)
250{
251 destroy_object(p_builder as *mut SystemBuilder);
252}
253
254#[no_mangle]
256pub unsafe extern "C" fn free_system(p_system: *mut c_void)
257{
258 destroy_object(p_system as *mut System);
259}
260
261#[no_mangle]
263pub unsafe extern "C" fn free_solution_string(soln_str: *mut c_char)
264{
265 let _owned = CString::from_raw(soln_str);
266}