use std::fmt;
use std::error::Error;
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum MatrixOp {
Get,
Set,
GetColumn,
SetColumn,
GetRow,
SetRow,
}
impl MatrixOp {
pub fn as_str(&self) -> &'static str {
match self {
Self::Get => "get",
Self::Set => "set",
Self::GetColumn => "get_column",
Self::SetColumn => "set_column",
Self::GetRow => "get_row",
Self::SetRow => "set_row",
}
}
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum OpContext {
AddColumn,
AddConstraint,
SetObjective,
SetBounds,
SetInteger,
SetBinary,
SetUnbounded,
SetSemicont,
SetVariableType,
SetRowName,
SetColName,
SetProblemName,
SetUpbo,
SetLowbo,
MatrixGet,
MatrixSet,
Resize,
DeleteColumn,
DeleteRow,
GetColumn,
SetColumn,
GetRow,
SetRow,
GetSensitivityRhs,
GetSensitivityObj,
Validate,
GetSolution,
}
impl OpContext {
pub fn as_str(&self) -> &'static str {
match self {
Self::AddColumn => "add_column",
Self::AddConstraint => "add_constraint",
Self::SetObjective => "set_objective_function",
Self::SetBounds => "set_bounds",
Self::SetInteger => "set_integer",
Self::SetBinary => "set_binary",
Self::SetUnbounded => "set_unbounded",
Self::SetSemicont => "set_semicont",
Self::SetVariableType => "set_variable_type",
Self::SetRowName => "set_row_name",
Self::SetColName => "set_col_name",
Self::SetProblemName => "set_problem_name",
Self::SetUpbo => "set_upbo",
Self::SetLowbo => "set_lowbo",
Self::MatrixGet => "get_mat",
Self::MatrixSet => "set_mat",
Self::Resize => "resize",
Self::DeleteColumn => "del_column",
Self::DeleteRow => "del_constraint",
Self::GetColumn => "get_column",
Self::SetColumn => "set_column",
Self::GetRow => "get_row",
Self::SetRow => "set_row",
Self::GetSensitivityRhs => "get_sensitivity_rhs",
Self::GetSensitivityObj => "get_sensitivity_obj",
Self::Validate => "is_feasible",
Self::GetSolution => "get_solution_variables",
}
}
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum EntityType {
Row,
Column,
Problem,
}
impl EntityType {
pub fn as_str(&self) -> &'static str {
match self {
Self::Row => "row",
Self::Column => "column",
Self::Problem => "problem",
}
}
}
#[derive(Debug, Clone, PartialEq)]
pub enum LpSolveError {
CreationFailed,
FileReadError {
format: FileFormat,
},
WriteError,
DimensionMismatch {
expected: usize,
actual: usize,
context: OpContext,
},
IndexOutOfBounds {
index: i32,
min: i32,
max: i32,
context: OpContext,
},
ConstraintAdditionFailed {
row: Option<i32>,
},
ColumnOperationFailed {
column: Option<i32>,
context: OpContext,
},
ObjectiveFunctionError,
MatrixOperationFailed {
row: i32,
col: i32,
op: MatrixOp,
},
OutOfMemory,
InAddRowMode,
BasisOperationFailed,
NamingError {
entity: EntityType,
index: i32,
},
SOSConstraintError,
NoSolutionAvailable,
InvalidName {
context: &'static str,
},
BufferTooSmall {
required: usize,
provided: usize,
context: OpContext,
},
}
impl fmt::Display for LpSolveError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::CreationFailed =>
write!(f, "Failed to create lpsolve problem instance"),
Self::FileReadError { format } =>
write!(f, "Failed to read {:?} file", format),
Self::WriteError =>
write!(f, "Failed to write problem"),
Self::DimensionMismatch { expected, actual, context } =>
write!(f, "Dimension mismatch in {}: expected {} elements, got {}",
context.as_str(), expected, actual),
Self::IndexOutOfBounds { index, min, max, context } =>
write!(f, "Index {} out of bounds [{}, {}] in {}",
index, min, max, context.as_str()),
Self::ConstraintAdditionFailed { row } =>
if let Some(r) = row {
write!(f, "Failed to add constraint at row {}", r)
} else {
write!(f, "Failed to add constraint")
},
Self::ColumnOperationFailed { column, context } =>
if let Some(c) = column {
write!(f, "Column operation '{}' failed for column {}", context.as_str(), c)
} else {
write!(f, "Column operation '{}' failed", context.as_str())
},
Self::ObjectiveFunctionError =>
write!(f, "Failed to set objective function"),
Self::MatrixOperationFailed { row, col, op } =>
write!(f, "Matrix operation '{}' failed at ({}, {})",
op.as_str(), row, col),
Self::OutOfMemory =>
write!(f, "Out of memory"),
Self::InAddRowMode =>
write!(f, "Operation not allowed while in add_row mode"),
Self::BasisOperationFailed =>
write!(f, "Basis operation failed"),
Self::NamingError { entity, index } =>
write!(f, "Failed to set/get name for {} at index {}",
entity.as_str(), index),
Self::SOSConstraintError =>
write!(f, "SOS constraint error"),
Self::NoSolutionAvailable =>
write!(f, "No solution available (solve not called or failed)"),
Self::InvalidName { context } =>
write!(f, "Invalid name in {} (contains null bytes)", context),
Self::BufferTooSmall { required, provided, context } =>
write!(f, "Buffer too small for {}: required {} elements, provided {}",
context.as_str(), required, provided),
}
}
}
impl Error for LpSolveError {}
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum FileFormat {
LP,
FreeMPS,
FixedMPS,
}
pub type Result<T> = std::result::Result<T, LpSolveError>;
pub(crate) trait CReturnCode {
fn check_with<E: Into<LpSolveError>>(self, error: E) -> Result<()>;
}
impl CReturnCode for libc::c_int {
fn check_with<E: Into<LpSolveError>>(self, error: E) -> Result<()> {
if self == 1 {
Ok(())
} else {
Err(error.into())
}
}
}
impl CReturnCode for libc::c_uchar {
fn check_with<E: Into<LpSolveError>>(self, error: E) -> Result<()> {
if self == 1 {
Ok(())
} else {
Err(error.into())
}
}
}