/* ************************************************************************* */
TEST( InvDepthFactor, optimize) {

  // landmark 5 meters infront of camera
  Point3 landmark(5, 0, 1);

  Point2 expected_uv = level_camera.project(landmark);

  InvDepthCamera3<Cal3_S2> inv_camera(level_pose, K);
  LieVector inv_landmark(5, 1., 0., 1., 0., 0.);
  LieScalar inv_depth(1./4);

  gtsam::NonlinearFactorGraph graph;
  Values initial;

  InverseDepthFactor::shared_ptr factor(new InverseDepthFactor(expected_uv, sigma,
      Symbol('x',1), Symbol('l',1), Symbol('d',1), K));
  graph.push_back(factor);
  graph.add(PoseConstraint(Symbol('x',1),level_pose));
  initial.insert(Symbol('x',1), level_pose);
  initial.insert(Symbol('l',1), inv_landmark);
  initial.insert(Symbol('d',1), inv_depth);

  LevenbergMarquardtParams lmParams;
  Values result = LevenbergMarquardtOptimizer(graph, initial, lmParams).optimize();
  EXPECT(assert_equal(initial, result, 1e-9));

  /// Add a second camera

  // add a camera 1 meter to the right
  Pose3 right_pose = level_pose * Pose3(Rot3(), Point3(2,0,0));
  SimpleCamera right_camera(right_pose, *K);

  InvDepthCamera3<Cal3_S2> right_inv_camera(right_pose, K);

  Point3 landmark1(6,0,1);
  Point2 right_uv = right_camera.project(landmark1);


  InverseDepthFactor::shared_ptr factor1(new InverseDepthFactor(right_uv, sigma,
      Symbol('x',2), Symbol('l',1),Symbol('d',1),K));
  graph.push_back(factor1);

  graph.add(PoseConstraint(Symbol('x',2),right_pose));

  initial.insert(Symbol('x',2), right_pose);

  // TODO: need to add priors to make this work with
//    Values result2 = optimize<NonlinearFactorGraph>(graph, initial,
//      NonlinearOptimizationParameters(),MULTIFRONTAL, GN);

  Values result2 = LevenbergMarquardtOptimizer(graph, initial, lmParams).optimize();
  Point3 l1_result2 = InvDepthCamera3<Cal3_S2>::invDepthTo3D(
      result2.at<LieVector>(Symbol('l',1)),
      result2.at<LieScalar>(Symbol('d',1)));

  EXPECT(assert_equal(landmark1, l1_result2, 1e-9));
}
/* ************************************************************************* */
TEST( InvDepthFactor, Project4) {

  // landmark 4m to the left and 1m up from camera
  // inv depth landmark xyz at origion
  Point3 landmark(1, 4, 2);
  Point2 expected = level_camera.project(landmark);

  InvDepthCamera3<Cal3_S2> inv_camera(level_pose, K);
  Vector5 diag_landmark((Vector(5) << 0., 0., 0., atan(4.0/1), atan(2./sqrt(1.+16.))).finished());
  double inv_depth(1./sqrt(1.+16.+4.));
  Point2 actual = inv_camera.project(diag_landmark, inv_depth);
  EXPECT(assert_equal(expected, actual));
}
/* ************************************************************************* */
TEST( InvDepthFactor, Project2) {

  // landmark 1m to the left and 1m up from camera
  // inv landmark xyz is same as camera xyz, so depth  actually doesn't matter
  Point3 landmark(1, 1, 2);
  Point2 expected = level_camera.project(landmark);

  InvDepthCamera3<Cal3_S2> inv_camera(level_pose, K);
  Vector5 diag_landmark((Vector(5) << 0., 0., 1., M_PI/4., atan(1.0/sqrt(2.0))).finished());
  double inv_depth(1/sqrt(3.0));
  Point2 actual = inv_camera.project(diag_landmark, inv_depth);
  EXPECT(assert_equal(expected, actual));
}
/* ************************************************************************* */
TEST( InvDepthFactor, Project1) {

  // landmark 5 meters infront of camera
  Point3 landmark(5, 0, 1);

  Point2 expected_uv = level_camera.project(landmark);

  InvDepthCamera3<Cal3_S2> inv_camera(level_pose, K);
  Vector5 inv_landmark((Vector(5) << 1., 0., 1., 0., 0.).finished());
  double inv_depth(1./4);
  Point2 actual_uv = inv_camera.project(inv_landmark, inv_depth);
  EXPECT(assert_equal(expected_uv, actual_uv,1e-8));
  EXPECT(assert_equal(Point2(640,480), actual_uv));
}
using namespace boost::assign;

// Some common constants
static const boost::shared_ptr<Cal3_S2> sharedCal = //
    boost::make_shared<Cal3_S2>(1500, 1200, 0, 640, 480);

// Looking along X-axis, 1 meter above ground plane (x-y)
static const Rot3 upright = Rot3::Ypr(-M_PI / 2, 0., -M_PI / 2);
static const Pose3 pose1 = Pose3(upright, gtsam::Point3(0, 0, 1));
SimpleCamera camera1(pose1, *sharedCal);

// landmark ~5 meters infront of camera
static const Point3 landmark(5, 0.5, 1.2);

// 1. Project two landmarks into two cameras and triangulate
Point2 z1 = camera1.project(landmark);

//******************************************************************************
TEST( triangulation, TriangulationFactor ) {

  Key pointKey(1);
  SharedNoiseModel model;
  typedef TriangulationFactor<SimpleCamera> Factor;
  Factor factor(camera1, z1, model, pointKey);

  // Use the factor to calculate the Jacobians
  Matrix HActual;
  factor.evaluateError(landmark, HActual);

  Matrix HExpected = numericalDerivative11<Vector,Point3>(
      boost::bind(&Factor::evaluateError, &factor, _1, boost::none), landmark);