
//#define BOOST_NUMERIC_BINDINGS_LAPACK_2

#include <cstddef>
#include <iostream>
#include <algorithm> 
#include <boost/numeric/bindings/lapack/driver/gesdd.hpp>
#include <boost/numeric/bindings/ublas/matrix.hpp>
#include <boost/numeric/bindings/ublas/vector.hpp>
#include <boost/numeric/bindings/std/vector.hpp>
#include "utils.h"

namespace ublas = boost::numeric::ublas;
namespace lapack = boost::numeric::bindings::lapack;

using std::size_t; 
using std::cout;
using std::endl; 

typedef double real_t; 
typedef ublas::matrix<real_t, ublas::column_major> m_t;
typedef ublas::vector<real_t> v_t;

int main() {

  cout << endl; 

  size_t m = 3, n = 2;   
  size_t minmn = m < n ? m : n; 
  m_t a (m, n);  
  a(0,0) = 1.; a(0,1) = 1.;
  a(1,0) = 0.; a(1,1) = 1.;
  a(2,0) = 1.; a(2,1) = 0.;

  m_t a2 (a); // for part 2
  m_t a3 (a); // for part 2

  print_m (a, "A"); 
  cout << endl; 

  v_t s (minmn);
  m_t u (m, m);
  m_t vt (n, n);

/*
  size_t lw; 

#ifndef BOOST_NUMERIC_BINDINGS_LAPACK_2
  lw = lapack::gesdd_work ('O', 'N', a); 
  cout << "opt N lw: " << lw << endl; 
  lw = lapack::gesdd_work ('O', 'A', a); 
  cout << "opt A lw: " << lw << endl; 
  lw = lapack::gesdd_work ('O', 'S', a); 
  cout << "opt S lw: " << lw << endl; 
  lw = lapack::gesdd_work ('O', 'O', a); 
  cout << "opt O lw: " << lw << endl; 
#endif 
  lw = lapack::gesdd_work ('M', 'A', a); 
  cout << "min lw: " << lw << endl << endl; 

#ifndef BOOST_NUMERIC_BINDINGS_LAPACK_2
  lw = lapack::gesdd_work ('O', 'A', a); 
#endif 

  std::vector<real_t> w (lw); 

  size_t liw = lapack::gesdd_iwork (a); 
  cout << "liw: " << liw << endl << endl; 
  std::vector<fortran_int_t> iw (liw); 
  
  lapack::gesdd ('A', a, s, u, vt, w, iw);  
*/
  lapack::gesdd ('A', a, s, u, vt);

  print_v (s, "s"); 
  cout << endl; 
  print_m (u, "U"); 
  cout << endl; 
  print_m (vt, "V^T"); 
  cout << endl; 

  m_t sm (m, n); 
  for (size_t i = 0; i < s.size(); ++i) 
    sm (i,i) = s (i); 
  print_m (sm, "S"); 
  cout << endl;

  a = ublas::prod (u, m_t (ublas::prod (sm, vt))); 
  print_m (a, "A == U S V^T"); 
  cout << endl; 

  // part 2 

  cout << endl << "part 2" << endl << endl; 
 
/*
#ifndef BOOST_NUMERIC_BINDINGS_LAPACK_2
  lapack::gesdd ('A', a2, s, u, vt);  
#else
  lapack::gesdd ('M', 'A', a2, s, u, vt);  
#endif
*/
  lapack::gesdd ('A', a2, s, u, vt, lapack::minimal_workspace());

  print_v (s, "s"); 
  cout << endl; 
  print_m (u, "U"); 
  cout << endl; 
  print_m (vt, "V^T"); 
  cout << endl; 

  for (size_t i = 0; i < s.size(); ++i) 
    sm (i,i) = s (i); 
  print_m (sm, "S"); 
  cout << endl;

  a2 = ublas::prod (u, m_t (ublas::prod (sm, vt))); 
  print_m (a2, "A == U S V^T"); 
  cout << endl;
  
  // part 3

  cout << endl << "part 3" << endl << endl; 

/*
#ifndef BOOST_NUMERIC_BINDINGS_LAPACK_2
  cout << "opt lw: " << lapack::gesdd_work ('O', 'N', a3) << endl << endl; 
  lapack::gesdd (a3, s);
#else 
  cout << "min lw: " << lapack::gesdd_work ('M', 'N', a3) << endl << endl; 
  lapack::gesdd ('M', 'N', a3, s, u, vt);
#endif 
*/
  lapack::gesdd ('N', a3, s, u, vt, lapack::minimal_workspace());

  print_v (s, "singular values only"); 
  cout << endl; 

  cout << endl; 
}

