/*
 * Copyright (c) 2016 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/>
 */

#[macro_use]
extern crate bundle;
#[macro_use]
extern crate log;
extern crate env_logger;

use bundle::{Real, DVector, Minorant, SimpleEvaluation, FirstOrderProblem, Solver, SolverParams};
use std::io;


struct QuadraticProblem {
    a: [[Real; 2]; 2],
    b: [Real; 2],
    c: Real,
}

impl QuadraticProblem {
    fn new() -> QuadraticProblem {
        QuadraticProblem {
            a : [[5.0, 1.0], [1.0, 4.0]],
            b : [-12.0, -10.0],
            c : 3.0,
        }
    }
}

impl<'a> FirstOrderProblem<'a> for QuadraticProblem {
    type Error = io::Error;
    type EvalResult = SimpleEvaluation;

    fn num_variables(&self) -> usize { 2 }

    #[allow(unused_variables)]
    fn evaluate(&'a mut self, fidx : usize, x : &DVector, nullstep_bnd : Real, relprec : Real) -> Result<Self::EvalResult, Self::Error> {
        assert!(fidx == 0);
        let mut objective = self.c;
        let mut g = dvec![0.0; 2];

        for i in 0..2 {
            for j in 0..2 {
                g[i] += self.a[i][j] * x[j];
            }
            objective += x[i] * (g[i] + self.b[i]);
            g[i] = 2.0*g[i] + self.b[i];
        }

        debug!("Evaluation at {}", x);
        debug!("  objective={}", objective);
        debug!("  subgradient={}", g);

        Ok(SimpleEvaluation {
            objective: objective,
            minorants: vec![
                Minorant {
                    constant: objective,
                    linear: g,
                }
            ],
        })
    }
}

fn main() {
    env_logger::init().unwrap();

    let f = QuadraticProblem::new();
    let mut solver = Solver::new_params(f, SolverParams {
        min_weight: 1.0,
        max_weight: 1.0,
        ..Default::default()
    }).unwrap();
    solver.solve().unwrap();
}
