NULL-jacobians are handled correctly in generated autodiff code

The autodiff code generator is extended in the following ways:
  - Evaluate the complete jacobian into a temporary array
  - Copy the temporary jacobian to the result if != NULL

Change-Id: If5ed0bff3b31e93a2d7d5dc718862cb648dba474
diff --git a/include/ceres/codegen/autodiff.h b/include/ceres/codegen/autodiff.h
index 3160bb3..0a2a65d 100644
--- a/include/ceres/codegen/autodiff.h
+++ b/include/ceres/codegen/autodiff.h
@@ -131,7 +131,7 @@
   //    jacobians[0][1] = v_352;
   //    ...
   for (int i = 0, total_param_id = 0; i < kNumParameterBlocks;
-       ++i, total_param_id += ParameterDims::GetDim(i)) {
+       total_param_id += ParameterDims::GetDim(i), ++i) {
     for (int r = 0; r < kNumResiduals; ++r) {
       for (int j = 0; j < ParameterDims::GetDim(i); ++j) {
         internal::MakeOutput(
@@ -191,20 +191,65 @@
   // EvaluateResidualAndJacobian. This combined function is compatible to
   // CostFunction::Evaluate. Therefore the generated code can be directly used
   // in SizedCostFunctions.
-
   output.emplace_back("bool Evaluate(double const* const* parameters,");
   output.emplace_back("              double* residuals,");
-  output.emplace_back("              double** jacobians)");
-  output.emplace_back("{");
-  output.emplace_back("   if (residuals && jacobians) {");
-  output.emplace_back("     EvaluateResidualAndJacobian(");
-  output.emplace_back("         parameters,");
-  output.emplace_back("         residuals,");
-  output.emplace_back("         jacobians);");
+  output.emplace_back("              double** jacobians) {");
+  output.emplace_back("   if (jacobians) {");
+
+  // Create a tmp array of all jacobians and use it for evaluation.
+  // The generated code for a <2,3,1,2> cost functor is:
+  //   double jacobians_data[6];
+  //   double* jacobians_ptrs[] = {
+  //       jacobians_data + 0,
+  //       jacobians_data + 6,
+  //       jacobians_data + 8,
+  //   };
+  output.emplace_back("     double jacobians_data[" +
+                      std::to_string(kNumParameters * kNumResiduals) + "];");
+  output.emplace_back("     double* jacobians_ptrs[] = {");
+  for (int i = 0, total_param_id = 0; i < kNumParameterBlocks;
+       total_param_id += ParameterDims::GetDim(i), ++i) {
+    output.emplace_back("       jacobians_data + " +
+                        std::to_string(kNumResiduals * total_param_id) + ",");
+  }
+  output.emplace_back("     };");
+
+  // Evaluate into the tmp array.
+  output.emplace_back(
+      "     EvaluateResidualAndJacobian(parameters, residuals, "
+      "jacobians_ptrs);");
+
+  // Copy the computed jacobians into the output array. Add an if-statement to
+  // test for null-jacobians. The generated code for a <2,3,1,2> cost functor
+  // is:
+  //    if (jacobians[0]) {
+  //      for (int i = 0; i < 6; ++i) {
+  //        jacobians[0][i] = jacobians_tmp[0][i];
+  //      }
+  //    }
+  //    if (jacobians[1]) {
+  //      for (int i = 0; i < 2; ++i) {
+  //        jacobians[1][i] = jacobians_tmp[1][i];
+  //      }
+  //    }
+  //    if (jacobians[2]) {
+  //      for (int i = 0; i < 4; ++i) {
+  //        jacobians[2][i] = jacobians_tmp[2][i];
+  //      }
+  //    }
+  for (int i = 0; i < kNumParameterBlocks; ++i) {
+    output.emplace_back("     if (jacobians[" + std::to_string(i) + "]) {");
+    output.emplace_back(
+        "       for (int i = 0; i < " +
+        std::to_string(ParameterDims::GetDim(i) * kNumResiduals) + "; ++i) {");
+    output.emplace_back("         jacobians[" + std::to_string(i) +
+                        "][i] = jacobians_ptrs[" + std::to_string(i) + "][i];");
+    output.emplace_back("       }");
+    output.emplace_back("     }");
+  }
+  output.emplace_back("     return true;");
   output.emplace_back("   }");
-  output.emplace_back("   else if (residuals) {");
-  output.emplace_back("     EvaluateResidual(parameters,residuals);");
-  output.emplace_back("   }");
+  output.emplace_back("   EvaluateResidual(parameters, residuals);");
   output.emplace_back("   return true;");
   output.emplace_back("}");