// Copyright (c) 2016, 2017 Frank Fischer <frank-fischer@shadow-soft.de>
//
// This program is free software: you can redistribute it and/or
// modify it under the terms of the GNU General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>
//
use {DVector, Real};
use cplex_sys as cpx;
use std::ptr;
use std::ffi::CString;
use std::result::Result;
use std::os::raw::{c_char, c_double, c_int};
use failure::{err_msg, Error};
pub struct Solver {
net: *mut cpx::Net,
logfile: *mut cpx::File,
}
impl Drop for Solver {
fn drop(&mut self) {
unsafe {
cpx::NETfreeprob(cpx::env(), &mut self.net);
cpx::fclose(self.logfile);
}
}
}
impl Solver {
pub fn new(nnodes: usize) -> Result<Solver, Error> {
let mut status: c_int;
let mut net = ptr::null_mut();
let logfile;
unsafe {
#[cfg_attr(feature = "cargo-clippy", allow(never_loop))]
loop {
logfile = cpx::fopen(
const_cstr!("mcf.cpxlog").as_ptr(),
const_cstr!("w").as_ptr(),
);
if logfile.is_null() {
return Err(err_msg("Can't open log-file"));
}
status = cpx::setlogfile(cpx::env(), logfile);
if status != 0 {
break;
}
net = cpx::NETcreateprob(cpx::env(), &mut status, const_cstr!("mcf").as_ptr());
if status != 0 {
break;
}
status = cpx::NETaddnodes(cpx::env(), net, nnodes as c_int, ptr::null(), ptr::null());
if status != 0 {
break;
}
status = cpx::NETchgobjsen(cpx::env(), net, cpx::ObjectiveSense::Minimize.to_c());
if status != 0 {
break;
}
break;
}
if status != 0 {
let msg = CString::new(vec![0; cpx::MESSAGE_BUF_SIZE])
.unwrap()
.into_raw();
cpx::geterrorstring(cpx::env(), status, msg);
cpx::NETfreeprob(cpx::env(), &mut net);
cpx::fclose(logfile);
return Err(
cpx::CplexError {
code: status,
msg: CString::from_raw(msg).to_string_lossy().into_owned(),
}.into(),
);
}
}
Ok(Solver {
net: net,
logfile: logfile,
})
}
pub fn num_nodes(&self) -> usize {
unsafe { cpx::NETgetnumnodes(cpx::env(), self.net) as usize }
}
pub fn num_arcs(&self) -> usize {
unsafe { cpx::NETgetnumarcs(cpx::env(), self.net) as usize }
}
pub fn set_balance(&mut self, node: usize, supply: Real) -> Result<(), Error> {
let n = node as c_int;
let s = supply as c_double;
Ok(trycpx!(cpx::NETchgsupply(
cpx::env(),
self.net,
1,
&n,
&s as *const c_double
)))
}
pub fn set_objective(&mut self, obj: &DVector) -> Result<(), Error> {
let inds = (0..obj.len() as c_int).collect::<Vec<_>>();
Ok(trycpx!(cpx::NETchgobj(
cpx::env(),
self.net,
obj.len() as c_int,
inds.as_ptr(),
obj.as_ptr()
)))
}
pub fn add_arc(&mut self, src: usize, snk: usize, cost: Real, cap: Real) -> Result<(), Error> {
let f = src as c_int;
let t = snk as c_int;
let c = cost as c_double;
let u = cap as c_double;
let name = CString::new(format!("x{}#{}_{}", self.num_arcs() + 1, f + 1, t + 1)).unwrap();
let cname = name.as_ptr();
Ok(trycpx!(cpx::NETaddarcs(
cpx::env(),
self.net,
1,
&f,
&t,
ptr::null(),
&u,
&c,
&cname as *const *const c_char
)))
}
pub fn solve(&mut self) -> Result<(), Error> {
Ok(trycpx!(cpx::NETprimopt(cpx::env(), self.net)))
}
pub fn objective(&self) -> Result<Real, Error> {
let mut objval: c_double = 0.0;
trycpx!(cpx::NETgetobjval(
cpx::env(),
self.net,
&mut objval as *mut c_double
));
Ok(objval)
}
pub fn get_solution(&self) -> Result<DVector, Error> {
let mut sol = dvec![0.0; self.num_arcs()];
let mut stat: c_int = 0;
let mut objval: c_double = 0.0;
trycpx!(cpx::NETsolution(
cpx::env(),
self.net,
&mut stat as *mut c_int,
&mut objval as *mut c_double,
sol.as_mut_ptr(),
ptr::null_mut(),
ptr::null_mut(),
ptr::null_mut()
));
Ok(sol)
}
pub fn writelp(&self, filename: &str) -> Result<(), Error> {
let fname = CString::new(filename).unwrap();
Ok(trycpx!(cpx::NETwriteprob(
cpx::env(),
self.net,
fname.as_ptr(),
ptr::null_mut()
)))
}
}