use basin::{BasicState, CostFunction, Executor, Gradient, GradientDescent};

struct Rosenbrock;

impl CostFunction for Rosenbrock {
    type Param = Vec<f64>;
    type Output = f64;
    type Error = std::convert::Infallible;

    fn cost(&self, x: &Vec<f64>) -> Result<f64, std::convert::Infallible> {
        let a = 1.0;
        let b = 100.0;
        Ok((a - x[0]).powi(2) + b * (x[1] - x[0].powi(2)).powi(2))
    }
}

impl Gradient for Rosenbrock {
    type Gradient = Vec<f64>;

    fn gradient(&self, x: &Vec<f64>) -> Result<Vec<f64>, std::convert::Infallible> {
        let a = 1.0;
        let b = 100.0;
        Ok(vec![
            -2.0 * (a - x[0]) - 4.0 * b * x[0] * (x[1] - x[0].powi(2)),
            2.0 * b * (x[1] - x[0].powi(2)),
        ])
    }
}

fn main() {
    let problem = Rosenbrock;
    let initial = vec![-1.2, 1.0];

    let initial_cost = problem.cost(&initial).unwrap();
    println!("initial param: {:?}", initial);
    println!("initial cost:  {}", initial_cost);

    let solver = GradientDescent::new(0.001);
    let state = BasicState::new(initial);
    let result = Executor::new(problem, solver, state)
        .max_iter(50_000)
        .run()
        .unwrap();

    println!("final iter:    {}", result.iter());
    println!("final param:   {:?}", result.param());
    println!("final cost:    {}", result.cost());
    println!("termination:   {:?}", result.reason);
}
