1extern crate lpsolve_sys as lp;
52extern crate libc;
53#[macro_use] extern crate bitflags;
54
55use std::mem::transmute;
56use std::io::Write;
57use std::ffi::CStr;
58use std::ops::Deref;
59
60#[repr(C)]
61#[derive(Debug, PartialEq, Eq, Ord, PartialOrd, Hash)]
62pub enum Verbosity {
63    Critical = 1,
64    Severe = 2,
65    Important = 3,
66    Normal = 4,
67    Detailed = 5,
68    Full = 6,
69}
70
71#[repr(C)]
72#[derive(Debug, PartialEq, Eq, Ord, PartialOrd, Hash)]
73pub enum ConstraintType {
74    Le = 1,
75    Eq = 3,
76    Ge = 2,
77    Free = 0,
78}
79
80#[repr(C)]
81#[derive(Debug, PartialEq, Eq, Ord, PartialOrd, Hash)]
82pub enum SOSType {
83    Type1 = 1,
84    Type2 = 2,
85}
86
87#[repr(C)]
88#[derive(Debug, PartialEq, Eq, Ord, PartialOrd, Hash)]
89pub enum VarType {
90    Binary = 1,
91    Float = 0,
92}
93
94#[repr(u8)]
95#[derive(Debug, PartialEq, Eq, Ord, PartialOrd, Hash)]
96pub enum BoundsMode {
97    Restrictive = 1,
98    None = 0,
99}
100
101#[repr(C)]
102#[derive(Debug, PartialEq, Eq, Ord, PartialOrd, Hash)]
103pub enum SolveStatus {
104    OutOfMemory = -2,
105    NotRun = -1,
106    Optimal = 0,
107    Suboptimal = 1,
108    Infeasible = 2,
109    Unbounded = 3,
110    Degenerate = 4,
111    NumericalFailure = 5,
112    UserAbort = 6,
113    Timeout = 7,
114    Presolved = 8,
115    ProcFail = 9,
116    ProcBreak = 11,
117    FeasibleFound = 12,
118    NoFeasibleFound = 13,
119}
120
121bitflags! {
122    pub flags MPSOptions: ::libc::c_int {
123        const CRITICAL = 1,
124        const SEVERE = 2,
125        const IMPORTANT = 3,
126        const NORMAL = 4,
127        const DETAILED = 5,
128        const FULL = 6,
129        const FREE = 8,
130        const IBM = 16,
131        const NEGOBJCONST = 32,
132    }
133}
134
135pub struct Problem {
137    lprec: *mut lp::lprec,
138}
139
140macro_rules! cptr {
141    ($e:expr) => { if $e.is_null() { None } else { Some(Problem { lprec: $e }) } }
142}
143
144unsafe extern "C" fn write_modeldata(val: *mut libc::c_void, buf: *mut libc::c_char) -> libc::c_int {
145    let val = transmute::<_, &mut &mut Write>(val);
146    let buf = CStr::from_ptr(buf);
147    match val.write(buf.to_bytes()) {
148        Ok(_) => 0,
149        Err(_) => 1,
150    }
151}
152
153impl Problem {
154    pub fn new(rows: libc::c_int, cols: libc::c_int) -> Option<Problem> {
156        let ptr = unsafe { lp::make_lp(rows, cols) };
157        cptr!(ptr)
158    }
159
160    pub fn read_lp<P: Deref<Target=CStr>, C: Deref<Target=CStr>>(path: &P, verbosity: Verbosity, initial_name: &C) -> Option<Problem> {
162        let ptr = unsafe { lp::read_LP(path.as_ptr() as *mut _, verbosity as libc::c_int, initial_name.as_ptr() as *mut _) };
163        cptr!(ptr)
164    }
165
166    pub fn read_freemps<P: Deref<Target=CStr>>(path: &P, options: MPSOptions) -> Option<Problem> {
168        let ptr = unsafe { lp::read_freeMPS(path.as_ptr() as *mut _, options.bits) };
169        cptr!(ptr)
170    }
171
172    pub fn read_fixedmps<P: Deref<Target=CStr>>(path: &P, options: MPSOptions) -> Option<Problem> {
174        let ptr = unsafe { lp::read_MPS(path.as_ptr() as *mut _, options.bits) };
175        cptr!(ptr)
176    }
177
178    pub fn write_lp(&self, out: &mut Write) -> bool {
182        1 == unsafe { lp::write_lpex(self.lprec, transmute::<_, *mut libc::c_void>(&out), write_modeldata) }
183    }
184
185    pub fn write_fixedmps(&self, out: &mut Write) -> bool {
189        self.write_mps(out, 1)
190    }
191
192    pub fn write_freemps(&self, out: &mut Write) -> bool {
196        self.write_mps(out, 2)
197    }
198
199    pub fn write_mps(&self, out: &mut Write, formatting: libc::c_int) -> bool {
205        debug_assert!(formatting == 1 || formatting == 2);
206        1 == unsafe { lp::MPS_writefileex(self.lprec, formatting, transmute::<_, *mut libc::c_void>(&out), write_modeldata) }
207    }
208
209    pub fn resize(&mut self, rows: libc::c_int, cols: libc::c_int) -> bool {
216        1 == unsafe { lp::resize_lp(self.lprec, rows, cols) }
217    }
218
219    pub fn add_column(&mut self, values: &[f64]) -> bool {
225        assert!(values.len() == self.num_rows() as usize + 1);
226        1 == unsafe { lp::add_column(self.lprec, values.as_ptr() as *mut _) }
227    }
228
229    pub fn add_column_scatter(&mut self, values: &[f64], indices: &[libc::c_int]) -> bool {
235        debug_assert!(values.len() == indices.len());
236        let len = std::cmp::max(values.len(), indices.len());
237        1 == unsafe { lp::add_columnex(self.lprec, len as libc::c_int, values.as_ptr() as *mut _, indices.as_ptr() as *mut _) }
238    }
239
240    pub fn get_column(&self, values: &mut [f64], column: libc::c_int) -> bool {
242        assert!(values.len() >= self.num_rows() as usize + 1);
243        1 == unsafe { lp::get_column(self.lprec, column, values.as_mut_ptr()) }
244    }
245
246    pub fn get_row(&self, values: &mut [f64], row: libc::c_int) -> bool {
248        assert!(values.len() >= self.num_cols() as usize + 1);
249        1 == unsafe { lp::get_row(self.lprec, row, values.as_mut_ptr()) }
250    }
251
252
253    pub fn add_constraint(&mut self, coeffs: &[f64], target: f64, kind: ConstraintType) -> bool {
262        assert!(coeffs.len() >= self.num_cols() as usize + 1);
263        1 == unsafe { lp::add_constraint(self.lprec, coeffs.as_ptr() as *mut _, kind as libc::c_int, target) }
264    }
265
266    pub fn add_sos_constraint(&mut self, name: &CStr, sostype: SOSType, priority: libc::c_int,
274                              weights: &[f64], variables: &[libc::c_int]) -> bool {
275        let len = std::cmp::max(weights.len(), variables.len());
276        debug_assert!(weights.len() == variables.len());
277        0 != unsafe { lp::add_SOS(self.lprec, name.as_ptr() as *mut _, sostype as libc::c_int, priority, 
278                             len as libc::c_int, variables.as_ptr() as *mut _, weights.as_ptr() as *mut _) }
279    }
280
281    pub fn del_column(&mut self, col: libc::c_int) -> bool {
286        1 == unsafe { lp::del_column(self.lprec, col) }
287    }
288
289    pub fn del_constraint(&mut self, row: libc::c_int) -> bool {
294        1 == unsafe { lp::del_constraint(self.lprec, row) }
295    }
296
297    pub fn is_negative(&self, col: libc::c_int) -> Option<bool> {
301        if col > self.num_cols()+1 {
302            None
303        } else {
304            Some(unsafe { lp::is_negative(self.lprec, col) } == 1)
305        }
306    }
307
308    pub fn set_variable_type(&mut self, col: libc::c_int, vartype: VarType) -> bool {
310        1 == unsafe { lp::set_binary(self.lprec, col, vartype as libc::c_uchar) }
311    }
312
313    pub fn get_variable_type(&self, col: libc::c_int) -> Option<VarType> {
315        if col > self.num_cols()+1 {
316            None
317        } else {
318            let res = if unsafe { lp::is_binary(self.lprec, col) } == 1 {
319                VarType::Binary
320            } else {
321                VarType::Float
322            };
323            Some(res)
324        }
325    }
326
327    pub fn set_bounds(&mut self, col: libc::c_int, lower: f64, upper: f64) -> bool {
329        1 == unsafe { lp::set_bounds(self.lprec, col, lower, upper) }
330    }
331
332    pub fn set_bounds_mode(&mut self, tighten: BoundsMode) {
341        unsafe { lp::set_bounds_tighter(self.lprec, tighten as libc::c_uchar) };
342    }
343
344    pub fn get_bounds_mode(&self) -> BoundsMode {
348        if unsafe { lp::get_bounds_tighter(self.lprec) } == 1 {
349            BoundsMode::Restrictive
350        } else {
351            BoundsMode::None
352        }
353    }
354
355    pub fn set_constraint_type(&mut self, row: libc::c_int, contype: ConstraintType) -> bool {
357        1 == unsafe { lp::set_constr_type(self.lprec, row, contype as libc::c_int) }
358    }
359
360    pub fn get_constraint_type(&self, row: libc::c_int) -> Option<ConstraintType> {
362        if row > self.num_rows()+1 {
363            None
364        } else {
365            let res = unsafe { lp::get_constr_type(self.lprec, row) };
366            match res {
367                1 => Some(ConstraintType::Le),
368                2 => Some(ConstraintType::Ge),
369                3 => Some(ConstraintType::Eq),
370                _ => None,
371            }
372        }
373    }
374
375    pub fn set_unbounded(&mut self, col: libc::c_int) -> bool {
377        1 == unsafe { lp::set_unbounded(self.lprec, col) }
378    }
379
380    pub fn is_unbounded(&self, col: libc::c_int) -> Option<bool> {
382        if col > self.num_cols()+1 {
383            None
384        } else {
385            Some(unsafe { lp::is_unbounded(self.lprec, col) } == 1)
386        }
387    }
388
389    pub fn set_infinite(&mut self, infinity: f64) {
394        unsafe { lp::set_infinite(self.lprec, infinity) };
395    }
396
397    pub fn get_infinite(&self) -> f64 {
401        unsafe { lp::get_infinite(self.lprec) }
402    }
403
404    pub fn set_integer(&mut self, col: libc::c_int, must_be_integer: bool) -> bool {
406        1 == unsafe { lp::set_int(self.lprec, col, if must_be_integer { 1 } else { 0 }) }
407    }
408
409    pub fn is_integer(&self, col: libc::c_int) -> Option<bool> {
411        if col > self.num_cols()+1 {
412            None
413        } else {
414            Some(unsafe { lp::is_int(self.lprec, col) } == 1)
415        }
416    }
417
418    pub fn set_objective_function(&mut self, coeffs: &[f64]) -> bool {
422        assert!(coeffs.len() >= self.num_cols() as usize + 1);
423        1 == unsafe { lp::set_obj_fn(self.lprec, coeffs.as_ptr() as *mut _) }
424    }
425
426    pub fn scatter_objective_function(&mut self, coeffs: &[f64], indices: &[libc::c_int]) -> bool {
431        let len = std::cmp::max(coeffs.len(), indices.len());
432        debug_assert!(coeffs.len() == indices.len());
433        1 == unsafe { lp::set_obj_fnex(self.lprec, len as libc::c_int, coeffs.as_ptr() as *mut _, indices.as_ptr() as *mut _) }
434    }
435
436    pub fn set_constraint_range(&mut self, row: libc::c_int, range: f64) -> bool {
445        1 == unsafe { lp::set_rh_range(self.lprec, row, range) }
446    }
447
448    pub fn get_constraint_range(&self, row: libc::c_int) -> Option<f64> {
450        if row > self.num_rows() + 1 {
451            None
452        } else {
453            let delta = unsafe { lp::get_rh_range(self.lprec, row) };
454            if delta == self.get_infinite() {
455                None
456            } else {
457                Some(delta)
458            }
459        }
460    }
461
462    pub fn solve(&mut self) -> SolveStatus {
464        use SolveStatus::*;
465        match unsafe { lp::solve(self.lprec) } {
466            -2 => OutOfMemory,
467            -1 => NotRun,
468            0 => Optimal,
469            1 => Suboptimal,
470            2 => Infeasible,
471            3 => Unbounded,
472            4 => Degenerate,
473            5 => NumericalFailure,
474            6 => UserAbort,
475            7 => Timeout,
476            9 => Presolved,
477            10 => ProcFail,
478            11 => ProcBreak,
479            12 => FeasibleFound,
480            13 => NoFeasibleFound,
481            status => panic!("unknown solve status {}", status)
482        }
483    }
484
485    pub fn get_solution_variables<'a>(&self, vars: &'a mut [f64]) -> Option<&'a mut [f64]> {
490        let cols = self.num_cols();
491        if vars.len() < cols as usize {
492            None
493        } else {
494            unsafe { lp::get_variables(self.lprec, vars.as_mut_ptr()) };
495            Some(&mut vars[..cols as usize])
496        }
497    }
498
499    pub unsafe fn from_lprec(lprec: *mut lp::lprec) -> Problem {
503        Problem {
504            lprec: lprec
505        }
506    }
507
508    pub fn to_lprec(&self) -> *mut lp::lprec {
512        self.lprec
513    }
514
515    pub fn num_cols(&self) -> libc::c_int {
516        unsafe { lp::get_Ncolumns(self.lprec) }
517    }
518
519    pub fn num_rows(&self) -> libc::c_int {
520        unsafe { lp::get_Nrows(self.lprec) }
521    }
522}
523
524impl Drop for Problem {
525    fn drop(&mut self) {
526        unsafe { lp::delete_lp(self.lprec) }
527    }
528}
529
530impl Clone for Problem {
531    fn clone(&self) -> Problem {
532        let ptr = unsafe { lp::copy_lp(self.lprec) };
533        if ptr.is_null() {
534            panic!("OOM when trying to copy_lp")
535        }
536        Problem { lprec: ptr }
537    }
538}
539
540unsafe impl Send for Problem { }
541
542#[cfg(test)]
543mod tests {
544    use Problem;
545    #[test]
546    fn smoke() {
547        let mut lp = Problem::new(0, 0).unwrap();
548        assert_eq!(lp.solve(), ::SolveStatus::NotRun);
549    }
550}