1use std::{
2 ffi::{c_int, CString},
3 ops::Not,
4};
5
6pub type Result<T> = std::result::Result<T, Error>;
7
8use ffi::{cpxenv, cpxlp, CPXgeterrorstring, CPXgetijdiv, CPXsolninfo, CPXMESSAGEBUFSIZE};
9use log::error;
10use thiserror::Error;
11
12#[derive(Error, Debug)]
13pub enum Error {
14 #[error("Cplex error: {0}")]
15 Cplex(#[from] Cplex),
16 #[error("Input error: {0}")]
17 Input(#[from] Input),
18}
19
20#[derive(Error, Debug)]
21#[error("Cplex error status {code}: {message}")]
22pub enum Cplex {
23 Unbounded { code: c_int, message: String },
24 Unfeasible { code: c_int, message: String },
25 Other { code: c_int, message: String },
26}
27
28impl Cplex {
29 pub(crate) fn from_code(env: *const cpxenv, lp: *const cpxlp, code: c_int) -> Cplex {
30 let mut buf = vec![0u8; CPXMESSAGEBUFSIZE as usize];
31 let ptr = unsafe { CPXgeterrorstring(env, code, buf.as_mut_ptr() as *mut i8) };
32 let message = ptr
33 .is_null()
34 .not()
35 .then_some(())
36 .and_then(|_| CString::from_vec_with_nul(buf).ok())
37 .and_then(|cs| cs.into_string().ok())
38 .unwrap_or_else(|| "Unable to extract error message".to_string());
39
40 if lp.is_null() {
41 return Self::Other { code, message };
42 }
43
44 if !Self::is_feasible(env, lp) {
45 Self::Unfeasible { code, message }
46 } else if !Self::is_bounded(env, lp) {
47 Self::Unbounded { code, message }
48 } else {
49 Self::Other { code, message }
50 }
51 }
52
53 fn is_bounded(env: *const cpxenv, lp: *const cpxlp) -> bool {
54 let mut i = 0;
55 let mut j = 0;
56 match unsafe { CPXgetijdiv(env, lp, &mut i, &mut j) } {
57 0 => j == -1,
58 _ => {
59 error!("Unable to determine if problem is bounded, assuming it is");
60 true
61 }
62 }
63 }
64
65 fn is_feasible(env: *const cpxenv, lp: *const cpxlp) -> bool {
66 let mut lpstat = 0;
67 let mut stype = 0;
68 let mut pfeas = 0;
69 let mut dfeas = 0;
70 match unsafe { CPXsolninfo(env, lp, &mut lpstat, &mut stype, &mut pfeas, &mut dfeas) } {
71 0 => pfeas != 0,
72 _ => {
73 error!("Unable to determine if problem is feasible, assuming it is");
74 true
75 }
76 }
77 }
78
79 pub(crate) fn env_error(code: c_int) -> Cplex {
80 let message = "Error encountered when constructing CPLEX env".to_owned();
81 Self::Other { code, message }
82 }
83}
84
85#[derive(Error, Debug)]
86#[error("Input error: {message}")]
87pub struct Input {
88 pub message: String,
89}
90
91impl Input {
92 pub(crate) fn from_message(message: String) -> Input {
93 Self { message }
94 }
95}