// Copyright 2018 The Simons Foundation, Inc. - All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//    http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#include "ndm_spin_phase.hpp"

#include <Eigen/Dense>
#include <iostream>
#include <vector>
#include "Machine/rbm_spin.hpp"
#include "Utils/all_utils.hpp"
#include "Utils/log_cosh.hpp"

namespace netket {

using RealVectorType = NdmSpinPhase::RealVectorType;
using VectorType = AbstractMachine::VectorType;

void NdmSpinPhase::Init() {
  W1_.resize(nv_, nh_);
  U1_.resize(nv_, na_);
  b1_.resize(nv_);
  h1_.resize(nh_);
  d1_.resize(na_);

  W2_.resize(nv_, nh_);
  U2_.resize(nv_, na_);
  b2_.resize(nv_);
  h2_.resize(nh_);

  BatchSize(1);

  npar_ = 2 * nv_ * (nh_ + na_);

  if (useb_) {
    npar_ += 2 * nv_;
  } else {
    b1_.setZero();
    b2_.setZero();
  }

  if (useh_) {
    npar_ += 2 * nh_;
  } else {
    h1_.setZero();
    h2_.setZero();
  }

  if (used_) {
    npar_ += na_;
  } else {
    d1_.setZero();
  }

  InfoMessage() << "Phase NDM Initizialized with nvisible = " << nv_
                << " and nhidden  = " << nh_ << " and nancilla = " << na_
                << std::endl;
  InfoMessage() << "Using visible   bias = " << useb_ << std::endl;
  InfoMessage() << "Using hidden    bias  = " << useh_ << std::endl;
  InfoMessage() << "Using ancillary bias  = " << used_ << std::endl;
}

Index NdmSpinPhase::BatchSize() const noexcept { return thetas_r1_.rows(); }

void NdmSpinPhase::BatchSize(Index batch_size) {
  if (batch_size <= 0) {
    std::ostringstream msg;
    msg << "invalid batch size: " << batch_size
        << "; expected a positive number";
    throw InvalidInputError{msg.str()};
  }
  if (batch_size != BatchSize()) {
    thetas_r1_.resize(batch_size, nh_);
    thetas_r2_.resize(batch_size, nh_);
    thetas_c1_.resize(batch_size, nh_);
    thetas_c2_.resize(batch_size, nh_);

    lnthetas_r1_.resize(batch_size, nh_);
    lnthetas_r2_.resize(batch_size, nh_);
    lnthetas_c1_.resize(batch_size, nh_);
    lnthetas_c2_.resize(batch_size, nh_);

    thetasnew_r1_.resize(batch_size, nh_);
    thetasnew_r2_.resize(batch_size, nh_);
    thetasnew_c1_.resize(batch_size, nh_);
    thetasnew_c2_.resize(batch_size, nh_);

    lnthetasnew_r1_.resize(batch_size, nh_);
    lnthetasnew_r2_.resize(batch_size, nh_);
    lnthetasnew_c1_.resize(batch_size, nh_);
    lnthetasnew_c2_.resize(batch_size, nh_);

    thetas_a_.resize(batch_size, na_);
    lnthetas_a_.resize(batch_size, na_);
    thetas_a1_.resize(batch_size, na_);
    thetas_a2_.resize(batch_size, na_);
    thetasnew_a1_.resize(batch_size, na_);
    thetasnew_a2_.resize(batch_size, na_);
    pi_.resize(batch_size, na_);
    lnpi_.resize(batch_size, na_);
    lnpinew_.resize(batch_size, na_);

    vsum_.resize(batch_size, nv_);
    vdelta_.resize(batch_size, nv_);
  }
}

VectorType NdmSpinPhase::DerLogSingle(VisibleConstType vr, VisibleConstType vc,
                                      const any & /*cache*/) {
  VectorType der(npar_);

  const int impar = (npar_ + na_ * used_) / 2;

  if (useb_) {
    der.head(nv_) = 0.5 * (vr + vc);
    der.segment(impar, nv_) = I_ * 0.5 * (vr - vc);
  }
  lnthetas_r1_ = (W1_.transpose() * vr + h1_).array().tanh();
  lnthetas_r2_ = (W2_.transpose() * vr + h2_).array().tanh();
  lnthetas_c1_ = (W1_.transpose() * vc + h1_).array().tanh();
  lnthetas_c2_ = (W2_.transpose() * vc + h2_).array().tanh();

  if (useh_) {
    der.segment(useb_ * nv_, nh_) = 0.5 * (lnthetas_r1_ + lnthetas_c1_);
    der.segment(impar + useb_ * nv_, nh_) =
        I_ * 0.5 * (lnthetas_r2_ - lnthetas_c2_);
  }

  thetas_a1_ = 0.5 * U1_.transpose() * (vr + vc) + d1_;
  thetas_a2_ = 0.5 * U2_.transpose() * (vr - vc);
  lnpi_ = ((0.5 * U1_.transpose() * (vr + vc) + d1_).array() +
           I_ * (0.5 * U2_.transpose() * (vr - vc)).array())
              .tanh();

  if (used_) {
    der.segment(useb_ * nv_ + useh_ * nh_, na_) = lnpi_;
  }

  const int initw_1 = nv_ * useb_ + nh_ * useh_ + na_ * used_;
  const int initw_2 = nv_ * useb_ + nh_ * useh_;

  MatrixType wder =
      0.5 * (vr * lnthetas_r1_.transpose() + vc * lnthetas_c1_.transpose());
  der.segment(initw_1, nv_ * nh_) =
      Eigen::Map<VectorType>(wder.data(), nv_ * nh_);

  wder = 0.5 * I_ *
         (vr * lnthetas_r2_.transpose() - vc * lnthetas_c2_.transpose());
  der.segment(impar + initw_2, nv_ * nh_) =
      Eigen::Map<VectorType>(wder.data(), nv_ * nh_);

  const int initu_1 = initw_1 + nv_ * nh_;
  const int initu_2 = initw_2 + nv_ * nh_;

  MatrixType uder = 0.5 * (vr + vc) * lnpi_.transpose();
  der.segment(initu_1, nv_ * na_) =
      Eigen::Map<VectorType>(uder.data(), nv_ * na_);

  uder = 0.5 * I_ * (vr - vc) * lnpi_.transpose();
  der.segment(impar + initu_2, nv_ * na_) =
      Eigen::Map<VectorType>(uder.data(), nv_ * na_);

  return der;
}

VectorType NdmSpinPhase::GetParameters() {
  VectorType pars(npar_);

  const int impar = (npar_ + na_ * used_) / 2;

  if (useb_) {
    pars.head(nv_) = b1_;
    pars.segment(impar, nv_) = b2_;
  }

  if (useh_) {
    pars.segment(nv_ * useb_, nh_) = h1_;
    pars.segment(impar + nv_ * useb_, nh_) = h2_;
  }

  if (used_) {
    pars.segment(useb_ * nv_ + useh_ * nh_, na_) = d1_;
  }

  const int initw_1 = nv_ * useb_ + nh_ * useh_ + na_ * used_;
  const int initw_2 = nv_ * useb_ + nh_ * useh_;

  pars.segment(initw_1, nv_ * nh_) =
      Eigen::Map<RealVectorType>(W1_.data(), nv_ * nh_);
  pars.segment(impar + initw_2, nv_ * nh_) =
      Eigen::Map<RealVectorType>(W2_.data(), nv_ * nh_);

  const int initu_1 = initw_1 + nv_ * nh_;
  const int initu_2 = initw_2 + nv_ * nh_;

  pars.segment(initu_1, nv_ * na_) =
      Eigen::Map<RealVectorType>(U1_.data(), nv_ * na_);
  pars.segment(impar + initu_2, nv_ * na_) =
      Eigen::Map<RealVectorType>(U2_.data(), nv_ * na_);

  return pars;
}

void NdmSpinPhase::SetParameters(VectorConstRefType pars) {
  const int impar = (npar_ + na_ * used_) / 2;

  if (useb_) {
    b1_ = pars.head(nv_).real();
    b2_ = pars.segment(impar, nv_).real();
  }

  if (useh_) {
    h1_ = pars.segment(useb_ * nv_, nh_).real();
    h2_ = pars.segment(impar + useb_ * nv_, nh_).real();
  }

  if (used_) {
    d1_ = pars.segment(useb_ * nv_ + useh_ * nh_, na_).real();
  }

  const int initw_1 = nv_ * useb_ + nh_ * useh_ + na_ * used_;
  const int initw_2 = nv_ * useb_ + nh_ * useh_;

  VectorType Wpars = pars.segment(initw_1, nv_ * nh_);
  W1_ = Eigen::Map<MatrixType>(Wpars.data(), nv_, nh_).real();

  Wpars = pars.segment(impar + initw_2, nv_ * nh_);
  W2_ = Eigen::Map<MatrixType>(Wpars.data(), nv_, nh_).real();

  const int initu_1 = initw_1 + nv_ * nh_;
  const int initu_2 = initw_2 + nv_ * nh_;

  VectorType Upars = pars.segment(initu_1, nv_ * na_);
  U1_ = Eigen::Map<MatrixType>(Upars.data(), nv_, na_).real();

  Upars = pars.segment(impar + initu_2, nv_ * na_);
  U2_ = Eigen::Map<MatrixType>(Upars.data(), nv_, na_).real();
}

// Value of the logarithm of the wave-function
// using pre-computed look-up tables for efficiency
Complex NdmSpinPhase::LogValSingle(VisibleConstType vr, VisibleConstType vc,
                                   const any& /*lookup*/) {
  auto r1s = SumLogCosh(W1_.transpose() * vr + h1_);
  auto r2s = SumLogCosh(W2_.transpose() * vr + h2_);
  auto c1s = SumLogCosh(W1_.transpose() * vc + h1_);
  auto c2s = SumLogCosh(W2_.transpose() * vc + h2_);

  thetas_a1_ = 0.5 * U1_.transpose() * (vr + vc) + d1_;
  thetas_a2_ = 0.5 * U2_.transpose() * (vr - vc);

  auto lnpis = SumLogCosh(thetas_a1_ + I_ * thetas_a2_);

  auto gamma_1 = 0.5 * (r1s + c1s + (vr + vc).dot(b1_));

  auto gamma_2 = 0.5 * (r2s - c2s + (vr - vc).dot(b2_));

  return (gamma_1 + I_ * gamma_2 + lnpis);
}

void NdmSpinPhase::LogVal(Eigen::Ref<const RowMatrix<double>> vr,
                          Eigen::Ref<const RowMatrix<double>> vc,
                          Eigen::Ref<VectorType> out, const any& /*lup*/) {
  CheckShape(__FUNCTION__, "vr", {vr.rows(), vr.cols()},
             {vc.rows(), NvisiblePhysical()});
  CheckShape(__FUNCTION__, "vc", {vc.rows(), vc.cols()},
             {vr.rows(), NvisiblePhysical()});
  CheckShape(__FUNCTION__, "out", out.size(), vr.rows());

  BatchSize(vr.rows());

  vsum_ = vr + vc;
  vdelta_ = vr - vc;

  thetas_r1_ = (vr * W1_).rowwise() + h1_.transpose();
  thetas_r2_ = (vr * W2_).rowwise() + h2_.transpose();
  thetas_c1_ = (vc * W1_).rowwise() + h1_.transpose();
  thetas_c2_ = (vc * W2_).rowwise() + h2_.transpose();
  thetas_a_ =
      (0.5 * (vsum_ * U1_ + I_ * vdelta_ * U2_)).rowwise() + d1_.transpose();

  out.noalias() = (vsum_ * b1_ + I_ * vdelta_ * b2_);

#pragma omp parallel for schedule(static)
  for (auto j = Index{0}; j < BatchSize(); ++j) {
    out(j) += SumLogCosh(thetas_r1_.row(j));
  }
#pragma omp parallel for schedule(static)
  for (auto j = Index{0}; j < BatchSize(); ++j) {
    out(j) += I_ * SumLogCosh(thetas_r2_.row(j));
  }
#pragma omp parallel for schedule(static)
  for (auto j = Index{0}; j < BatchSize(); ++j) {
    out(j) += SumLogCosh(thetas_c1_.row(j));
  }
#pragma omp parallel for schedule(static)
  for (auto j = Index{0}; j < BatchSize(); ++j) {
    out(j) -= I_ * SumLogCosh(thetas_c2_.row(j));
  }

  // All previous term are multiplied by 0.5
  out = out * 0.5;

#pragma omp parallel for schedule(static)
  for (auto j = Index{0}; j < BatchSize(); ++j) {
    out(j) += SumLogCosh(thetas_a_.row(j));
  }
}

void NdmSpinPhase::DerLog(Eigen::Ref<const RowMatrix<double>> vr,
                          Eigen::Ref<const RowMatrix<double>> vc,
                          Eigen::Ref<RowMatrix<Complex>> out,
                          const any& /*cache*/) {
  CheckShape(__FUNCTION__, "vr", {vr.rows(), vr.cols()},
             {vc.rows(), NvisiblePhysical()});
  CheckShape(__FUNCTION__, "vc", {vc.rows(), vc.cols()},
             {vr.rows(), NvisiblePhysical()});
  CheckShape(__FUNCTION__, "out", {out.rows(), out.cols()},
             {vr.rows(), Npar()});
  BatchSize(vr.rows());

  vsum_ = vr + vc;
  vdelta_ = vr - vc;

  const int impar = (npar_ + na_ * used_) / 2;

  auto i = Index{0};
  auto i2 = Index{impar};
  if (useb_) {
    out.block(0, i, BatchSize(), nv_) = 0.5 * vsum_;
    out.block(0, impar, BatchSize(), nv_) = I_ * 0.5 * vdelta_;
    i += nv_;
    i2 += nv_;
  }

  thetas_r1_ = ((vr * W1_).rowwise() + h1_.transpose()).array().tanh();
  thetas_r2_ = ((vr * W2_).rowwise() + h2_.transpose()).array().tanh();
  thetas_c1_ = ((vc * W1_).rowwise() + h1_.transpose()).array().tanh();
  thetas_c2_ = ((vc * W2_).rowwise() + h2_.transpose()).array().tanh();
  thetas_a_ =
      ((0.5 * (vsum_ * U1_ + I_ * vdelta_ * U2_)).rowwise() + d1_.transpose())
          .array()
          .tanh();
  if (useh_) {
    out.block(0, i, BatchSize(), nh_) = 0.5 * (thetas_r1_ + thetas_c1_);
    out.block(0, i2, BatchSize(), nh_) = I_ * 0.5 * (thetas_r2_ - thetas_c2_);
    i += nh_;
    i2 += nh_;
  }

  if (used_) {
    out.block(0, i, BatchSize(), na_) = thetas_a_;
    i += na_;
  }

  // TODO: Rewrite all those using tensors

#pragma omp parallel for schedule(static)
  for (auto j = Index{0}; j < BatchSize(); ++j) {
    Eigen::Map<Eigen::MatrixXcd>{&out(j, i), W1_.rows(), W1_.cols()}.noalias() =
        0.5 * (vr.row(j).transpose() * thetas_r1_.row(j) +
               vc.row(j).transpose() * thetas_c1_.row(j));
  }

#pragma omp parallel for schedule(static)
  for (auto j = Index{0}; j < BatchSize(); ++j) {
    Eigen::Map<Eigen::MatrixXcd>{&out(j, i2), W1_.rows(), W1_.cols()}
        .noalias() = 0.5 * I_ *
                     (vr.row(j).transpose() * thetas_r2_.row(j) -
                      vc.row(j).transpose() * thetas_c2_.row(j));
  }

  i += nv_ * nh_;
  i2 += nv_ * nh_;

  // TODO: Rewrite all those using tensors

#pragma omp parallel for schedule(static)
  for (auto j = Index{0}; j < BatchSize(); ++j) {
    Eigen::Map<Eigen::MatrixXcd>{&out(j, i), U1_.rows(), U1_.cols()}.noalias() =
        0.5 * vsum_.row(j).transpose() * thetas_a_.row(j);
  }

#pragma omp parallel for schedule(static)
  for (auto j = Index{0}; j < BatchSize(); ++j) {
    Eigen::Map<Eigen::MatrixXcd>{&out(j, i2), U2_.rows(), U2_.cols()}
        .noalias() = 0.5 * I_ * vdelta_.row(j).transpose() * thetas_a_.row(j);
  }
}

void NdmSpinPhase::Save(const std::string &filename) const {
  json state;
  state["Name"] = "NdmSpinPhase";
  state["Nvisible"] = nv_;
  state["Nhidden"] = nh_;
  state["Nancilla"] = na_;
  state["UseVisibleBias"] = useb_;
  state["UseHiddenBias"] = useh_;
  state["UseAncillaBias"] = used_;
  state["b1"] = b1_;
  state["h1"] = h1_;
  state["d1"] = d1_;
  state["W1"] = W1_;
  state["U1"] = U1_;

  state["b2"] = b2_;
  state["h2"] = h2_;
  state["W2"] = W2_;
  state["U2"] = U2_;
  WriteJsonToFile(state, filename);
}

void NdmSpinPhase::Load(const std::string &filename) {
  auto pars = ReadJsonFromFile(filename);
  std::string name = FieldVal<std::string>(pars, "Name");
  if (name != "NdmSpinPhase") {
    throw InvalidInputError(
        "Error while constructing RbmSpinPhase from input parameters");
  }

  if (FieldExists(pars, "Nvisible")) {
    nv_ = FieldVal<int>(pars, "Nvisible");
  }
  if (nv_ != GetHilbertPhysical().Size()) {
    throw InvalidInputError(
        "Number of visible units is incompatible with given "
        "Hilbert space");
  }

  if (FieldExists(pars, "Nhidden")) {
    nh_ = FieldVal<int>(pars, "Nhidden");
  } else {
    nh_ = nv_ * double(FieldVal<double>(pars, "Alpha"));
  }

  if (FieldExists(pars, "Nancilla")) {
    na_ = FieldVal<int>(pars, "Nancilla");
  } else {
    na_ = nv_ * double(FieldVal<double>(pars, "Beta"));
  }

  useb_ = FieldOrDefaultVal(pars, "UseVisibleBias", true);
  useh_ = FieldOrDefaultVal(pars, "UseHiddenBias", true);
  used_ = FieldOrDefaultVal(pars, "UseAncillaBias", true);

  Init();

  // Loading parameters, if defined in the input
  if (FieldExists(pars, "b1")) {
    b1_ = FieldVal<RealVectorType>(pars, "b1");
    b2_ = FieldVal<RealVectorType>(pars, "b2");
  } else {
    b1_.setZero();
    b2_.setZero();
  }

  if (FieldExists(pars, "h1")) {
    h1_ = FieldVal<RealVectorType>(pars, "h1");
    h2_ = FieldVal<RealVectorType>(pars, "h2");
  } else {
    h1_.setZero();
    h2_.setZero();
  }

  if (FieldExists(pars, "d1")) {
    d1_ = FieldVal<RealVectorType>(pars, "d1");
  } else {
    d1_.setZero();
  }

  if (FieldExists(pars, "W1")) {
    W1_ = FieldVal<RealMatrixType>(pars, "W1");
    W2_ = FieldVal<RealMatrixType>(pars, "W2");
  }

  if (FieldExists(pars, "U1")) {
    U1_ = FieldVal<RealMatrixType>(pars, "U1");
    U2_ = FieldVal<RealMatrixType>(pars, "U2");
  }
}

bool NdmSpinPhase::IsHolomorphic() const noexcept { return false; }
};  // namespace netket


/*
 * Copyright (c) 2016 Abhishek Agrawal (abhishek.agrawal@protonmail.com)
 * Distributed under the MIT License.
 * See accompanying file LICENSE.md or copy at http://opensource.org/licenses/MIT
 */

#ifndef PARTICLE_AROUND_UNIFORMLY_ROTATING_ELLIPSOID
#define PARTICLE_AROUND_UNIFORMLY_ROTATING_ELLIPSOID

#include <cstdlib>
#include <iostream>
#include <sstream>
#include <fstream>
#include <string>
#include <cmath>
#include <limits>
#include <vector>

#include <boost/numeric/odeint.hpp>

#include "NAOS/constants.hpp"
#include "NAOS/basicMath.hpp"
#include "NAOS/basicAstro.hpp"
#include "NAOS/misc.hpp"
#include "NAOS/ellipsoidGravitationalAcceleration.hpp"

namespace naos
{

//! particle around ellipsoid integration
/*!
 * integrate the equations of motion for a particle around an ellipsoid. The gravitational accelerations
 * calculated using the ellipsoid gravitational potential model.
 */
void executeParticleAroundEllipsoid( const double alpha,
                                     const double beta,
                                     const double gamma,
                                     const double gravParameter,
                                     std::vector< double > asteroidRotationVector,
                                     std::vector< double > &initialOrbitalElements,
                                     const double initialStepSize,
                                     const double startTime,
                                     const double endTime,
                                     std::ostringstream &outputFilePath,
                                     const int dataSaveIntervals );

//! Trajectory calculation for regolith around an asteroid (modelled as ellipsoid here)
/*!
 * Same as the previous function, except that the initial conditions are now given as a cartesian
 * state. The initial cartesian state should be given in body fixed frame of the asteroid.
 */
void singleRegolithTrajectoryCalculator( const double alpha,
                                         const double beta,
                                         const double gamma,
                                         const double gravParameter,
                                         std::vector< double > asteroidRotationVector,
                                         std::vector< double > &initialCartesianStateVector,
                                         const double initialStepSize,
                                         const double startTime,
                                         const double endTime,
                                         std::ostringstream &outputFilePath,
                                         const int dataSaveIntervals );

} // namespace naos

#endif // PARTICLE_AROUND_UNIFORMLY_ROTATING_ELLIPSOID


//==================================================================================================
/*
  Copyright 2016 NumScale SAS

  Distributed under the Boost Software License, Version 1.0.
  (See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt)
*/
//==================================================================================================
#include <boost/simd/pack.hpp>
#include <algorithm>
#include <numeric>
#include <simd_test.hpp>

namespace bs = boost::simd;

template <typename T, std::size_t N, typename Env, typename Op>
void test_binop(Env& runtime, Op op)
{
  std::array<T, N> a;
  std::iota(a.begin(), a.end(), T(1));
  bs::pack<T, N> p0(a.begin(), a.end()), ref;

  std::transform(p0.begin(),p0.end(),p0.begin(),ref.begin(), op );
  std::transform(a.begin() ,a.end() ,a.begin() ,a.begin()  , op );
  STF_ALL_EQUAL(ref, a);
}

template <typename T, std::size_t N, typename Env, typename Op>
void test_selfop(Env& runtime, Op op)
{
  std::array<T, N> a;
  std::iota(a.begin(), a.end(), T(1));
  bs::pack<T, N> p0(a.begin(), a.end());

  for(std::size_t i=0;i<N;++i)
  {
    op(p0[i],p0[i]);
    op(a[i] ,a[i]);
  }

  STF_ALL_EQUAL(p0, a);
}

struct op_plus
{
  template<typename T, typename U> void operator()(T&& out, U const& in) const
  {
    std::forward<T>(out) += in;
  }
};

STF_CASE_TPL( "Check proxy + operator" , STF_NUMERIC_TYPES)
{
  std::plus<T> op;
  test_binop<T,  1>(runtime,op);
  test_binop<T,  2>(runtime,op);
  test_binop<T,  4>(runtime,op);
  test_binop<T,  8>(runtime,op);
  test_binop<T, 16>(runtime,op);
  test_binop<T, 32>(runtime,op);
}

STF_CASE_TPL( "Check proxy += operator" , STF_NUMERIC_TYPES)
{
  op_plus op;
  test_selfop<T,  1>(runtime,op);
  test_selfop<T,  2>(runtime,op);
  test_selfop<T,  4>(runtime,op);
  test_selfop<T,  8>(runtime,op);
  test_selfop<T, 16>(runtime,op);
  test_selfop<T, 32>(runtime,op);
}


/*
 * SOCI_ConvertersLong.hpp
 *
 *  Created on: 29 Aug 2016
 *      Author: gishara
 */

//Copyright (c) 2016 Singapore-MIT Alliance for Research and Technology
//Licensed under the terms of the MIT License, as described in the file:
//   license.txt   (http://opensource.org/licenses/MIT)

#pragma once

#include <boost/algorithm/string.hpp>
#include <boost/tokenizer.hpp>
#include <database/entity/JobsByIndustryTypeByTaz.hpp>
#include <database/entity/WorkersGrpByLogsumParams.hpp>
#include <map>
#include <soci/soci.h>
#include <string>
#include "database/entity/HedonicCoeffs.hpp"
#include "database/entity/LagPrivateT.hpp"
#include "database/entity/LtVersion.hpp"
#include "database/entity/HedonicLogsums.hpp"
#include "database/entity/TAOByUnitType.hpp"
#include "database/entity/StudyArea.hpp"
#include "database/entity/JobsWithIndustryTypeAndTazId.hpp"
#include "database/entity/ResidentialWTP_Coefs.hpp"
#include "database/entity/StudentStop.hpp"
#include "database/entity/EzLinkStop.hpp"

using namespace sim_mob;
using namespace long_term;
namespace soci
{

template<>
struct type_conversion<sim_mob::long_term::HedonicCoeffs>
{
    typedef values base_type;

    static void
    from_base(soci::values const & values, soci::indicator & indicator, sim_mob::long_term::HedonicCoeffs& hedonicCoeffs)
    {
        hedonicCoeffs.setPropertyTypeId(values.get<BigSerial>("property_type_id", INVALID_ID));
        hedonicCoeffs.setIntercept(values.get<double>("intercept", 0));
        hedonicCoeffs.setLogSqrtArea(values.get<double>("log_area", 0));
        hedonicCoeffs.setFreehold( values.get<double>("freehold", 0));
        hedonicCoeffs.setLogsumWeighted(values.get<double>("logsum_weighted", 0));
        hedonicCoeffs.setPms1km(values.get<double>("pms_1km", 0));
        hedonicCoeffs.setDistanceMallKm(values.get<double>("distance_mall_km", 0));
        hedonicCoeffs.setMrt200m(values.get<double>("mrt_200m", 0));
        hedonicCoeffs.setMrt_2_400m(values.get<double>("mrt_2_400m", 0));
        hedonicCoeffs.setExpress200m(values.get<double>("express_200m", 0));
        hedonicCoeffs.setBus400m(values.get<double>("bus2_400m", 0));
        hedonicCoeffs.setBusGt400m(values.get<double>("bus_gt400m", 0));
        hedonicCoeffs.setAge(values.get<double>("age", 0));
        hedonicCoeffs.setLogAgeSquared(values.get<double>("age_squared", 0));
        hedonicCoeffs.setMisage(values.get<double>("misage", 0));
        hedonicCoeffs.setStorey(values.get<double>("storey", 0));
        hedonicCoeffs.setStoreySquared(values.get<double>("storey_squared", 0));

    }
};

template<>
struct type_conversion<sim_mob::long_term::HedonicCoeffsByUnitType>
{
    typedef values base_type;

    static void
    from_base(soci::values const & values, soci::indicator & indicator, sim_mob::long_term::HedonicCoeffsByUnitType& hedonicCoeffsByUT)
    {
        hedonicCoeffsByUT.setUnitTypeId(values.get<BigSerial>("unit_type_id", INVALID_ID));
        hedonicCoeffsByUT.setIntercept(values.get<double>("intercept", 0));
        hedonicCoeffsByUT.setLogArea(values.get<double>("log_area", 0));
        hedonicCoeffsByUT.setFreehold( values.get<double>("freehold", 0));
        hedonicCoeffsByUT.setLogsumWeighted(values.get<double>("logsum_weighted", 0));
        hedonicCoeffsByUT.setPms1km(values.get<double>("pms_1km", 0));
        hedonicCoeffsByUT.setDistanceMallKm(values.get<double>("distance_mall_km", 0));
        hedonicCoeffsByUT.setMrt200m(values.get<double>("mrt_200m", 0));
        hedonicCoeffsByUT.setMrt2400m(values.get<double>("mrt_2_400m", 0));
        hedonicCoeffsByUT.setExpress200m(values.get<double>("express_200m", 0));
        hedonicCoeffsByUT.setBus2400m(values.get<double>("bus2_400m", 0));
        hedonicCoeffsByUT.setBusGt400m(values.get<double>("bus_gt400m", 0));
        hedonicCoeffsByUT.setAge(values.get<double>("age", 0));
        hedonicCoeffsByUT.setAgeSquared(values.get<double>("age_squared", 0));
        hedonicCoeffsByUT.setMisage(values.get<double>("misage", 0));
        hedonicCoeffsByUT.setNonMature(values.get<double>("non_mature", 0));
        hedonicCoeffsByUT.setOtherMature(values.get<double>("other_mature", 0));
        hedonicCoeffsByUT.setStorey(values.get<double>("storey", 0));
        hedonicCoeffsByUT.setStoreySquared(values.get<double>("storey_squared", 0));

    }
};

template<>
struct type_conversion<sim_mob::long_term::LagPrivateT>
{
    typedef values base_type;

    static void
    from_base(soci::values const & values, soci::indicator & indicator, sim_mob::long_term::LagPrivateT& lagPrivateT)
    {
        lagPrivateT.setPropertyTypeId(values.get<BigSerial>("property_type_id", INVALID_ID));
        lagPrivateT.setIntercept(values.get<double>("intercept", 0));
        lagPrivateT.setT4(values.get<double>("t4", 0));

    }
};

template<>
struct type_conversion<sim_mob::long_term::LtVersion>
{
    typedef values base_type;

    static void
    from_base(soci::values const & values, soci::indicator & indicator, sim_mob::long_term::LtVersion& ltVersion)
    {
        ltVersion.setId(values.get<long long>("id", INVALID_ID));
        ltVersion.setBase_version(values.get<string>("base_version", ""));
        ltVersion.setChange_date(values.get<std::tm>("change_date", tm()));
        ltVersion.setComments(values.get<string>("comments", ""));
        ltVersion.setUser_id(values.get<string>("userid", ""));
    }
};

template<>
struct type_conversion<sim_mob::long_term::HedonicLogsums>
{
    typedef values base_type;

    static void
    from_base(soci::values const & values, soci::indicator & indicator, sim_mob::long_term::HedonicLogsums& hedonicLogsums)
    {
        hedonicLogsums.setTazId(values.get<long long>("taz_id", INVALID_ID));
        hedonicLogsums.setLogsumWeighted(values.get<double>("logsum_weighted", 0));
    }
};

template<>
struct type_conversion<sim_mob::long_term::WorkersGrpByLogsumParams>
{
    typedef values base_type;

    static void
    from_base(soci::values const & values, soci::indicator & indicator, sim_mob::long_term::WorkersGrpByLogsumParams& workersGrpByLogsumParams)
    {
        workersGrpByLogsumParams.setIndividualId(values.get<long long>("id", INVALID_ID));
        workersGrpByLogsumParams.setLogsumCharacteristicsGroupId(values.get<int>("rowid", 0));
    }
};

template<>
struct type_conversion<sim_mob::long_term::BuildingMatch>
{
    typedef values base_type;

    static void
    from_base(soci::values const & values, soci::indicator & indicator, sim_mob::long_term::BuildingMatch& buildingMatch)
    {
        buildingMatch.setFm_building(values.get<long long>("fm_building_id", INVALID_ID));
        buildingMatch.setFm_building_id_2008(values.get<long long>("fm_building_id_2008", INVALID_ID));
        buildingMatch.setSla_building_id(values.get<string>("sla_building_id", ""));
        buildingMatch.setSla_inc_cnc(values.get<string>("sla_inc_cnc",""));
        buildingMatch.setMatch_code(values.get<int>("match_code",0));
        buildingMatch.setMatch_date(values.get<tm>("match_date",tm()));
    }
};

template<>
struct type_conversion<sim_mob::long_term::SlaBuilding>
{
    typedef values base_type;

    static void
    from_base(soci::values const & values, soci::indicator & indicator, sim_mob::long_term::SlaBuilding& slaBuilding)
    {
        slaBuilding.setSla_address_id(values.get<long long>("sla_address_id",0));
        slaBuilding.setSla_building_id(values.get<string>("sla_building_id",""));
        slaBuilding.setSla_inc_crc(values.get<string>("sla_inc_crc",""));
    }
};

template<>
struct type_conversion<sim_mob::long_term::TAOByUnitType>
{
    typedef values base_type;

    static void
    from_base(soci::values const & values, soci::indicator & indicator, sim_mob::long_term::TAOByUnitType& taoByUT)
    {
        taoByUT.setId(values.get<BigSerial>("id",0));
        taoByUT.setQuarter(values.get<std::string>("quarter",std::string()));
        taoByUT.setTreasuryBillYield1Year(values.get<double>("treasury_bill_yield_1year",0));
        taoByUT.setGdpRate(values.get<double>("gdp_rate",0));
        taoByUT.setInflation(values.get<double>("inflation",0));
        taoByUT.setTApartment7(values.get<double>("tapt_7",0));
        taoByUT.setTApartment8(values.get<double>("tapt_8",0));
        taoByUT.setTApartment9(values.get<double>("tapt_9",0));
        taoByUT.setTApartment10(values.get<double>("tapt_10",0));
        taoByUT.setTApartment11(values.get<double>("tapt_11",0));
        taoByUT.setTCondo12(values.get<double>("tcondo_12",0));
        taoByUT.setTCondo13(values.get<double>("tcondo_13",0));
        taoByUT.setTCondo14(values.get<double>("tcondo_14",0));
        taoByUT.setTCondo15(values.get<double>("tcondo_15",0));
        taoByUT.setTCondo16(values.get<double>("tcondo_16",0));
    }
};

template<>
struct type_conversion<sim_mob::long_term::LagPrivate_TByUnitType>
{
    typedef values base_type;

    static void
    from_base(soci::values const & values, soci::indicator & indicator, sim_mob::long_term::LagPrivate_TByUnitType& lagPvtTByUT)
    {
        lagPvtTByUT.setUnitTypeId(values.get<BigSerial>("unit_type_id",0));
        lagPvtTByUT.setIntercept(values.get<double>("intercept",0));
        lagPvtTByUT.setT4(values.get<double>("t4",0));
        lagPvtTByUT.setT5(values.get<double>("t5",0));
        lagPvtTByUT.setT6(values.get<double>("t6",0));
        lagPvtTByUT.setT7(values.get<double>("t7",0));
        lagPvtTByUT.setGdpRate(values.get<double>("gdp_rate",0));
    }
};

template<>
struct type_conversion<sim_mob::long_term::StudyArea>
{
    typedef values base_type;

    static void
    from_base(soci::values const & values, soci::indicator & indicator, sim_mob::long_term::StudyArea& studyArea)
    {
        studyArea.setId(values.get<BigSerial>("id",0));
        studyArea.setFmTazId(values.get<BigSerial>("fm_taz_id",0));
        studyArea.setStudyCode(values.get<std::string>("study_code",std::string()));
    }
};

template<>
struct type_conversion<sim_mob::long_term::JobAssignmentCoeffs>
{
    typedef values base_type;

    static void
    from_base(soci::values const & values, soci::indicator & indicator, sim_mob::long_term::JobAssignmentCoeffs& jobAssignmentCoeff)
    {
        jobAssignmentCoeff.setId(values.get<int>("id",0));
        jobAssignmentCoeff.setBetaInc1(values.get<double>("beta_inc1",0));
        jobAssignmentCoeff.setBetaInc2(values.get<double>("beta_inc2",0));
        jobAssignmentCoeff.setBetaInc3(values.get<double>("beta_inc3",0));
        jobAssignmentCoeff.setBetaLgs(values.get<double>("beta_lgs",0));
        jobAssignmentCoeff.setBetaS1(values.get<double>("beta_s1",0));
        jobAssignmentCoeff.setBetaS2(values.get<double>("beta_s2",0));
        jobAssignmentCoeff.setBetaS3(values.get<double>("beta_s3",0));
        jobAssignmentCoeff.setBetaS4(values.get<double>("beta_s4",0));
        jobAssignmentCoeff.setBetaS5(values.get<double>("beta_s5",0));
        jobAssignmentCoeff.setBetaS6(values.get<double>("beta_s6",0));
        jobAssignmentCoeff.setBetaS7(values.get<double>("beta_s7",0));
        jobAssignmentCoeff.setBetaS8(values.get<double>("beta_s8",0));
        jobAssignmentCoeff.setBetaS9(values.get<double>("beta_s9",0));
        jobAssignmentCoeff.setBetaS10(values.get<double>("beta_s10",0));
        jobAssignmentCoeff.setBetaS11(values.get<double>("beta_s11",0));
        jobAssignmentCoeff.setBetaS98(values.get<double>("beta_s98",0));
        jobAssignmentCoeff.setBetaLnJob(values.get<double>("beta_lnjob",0));
    }
};

template<>
struct type_conversion<sim_mob::long_term::JobsByIndustryTypeByTaz>
{
    typedef values base_type;

    static void
    from_base(soci::values const & values, soci::indicator & indicator, sim_mob::long_term::JobsByIndustryTypeByTaz& jobsByIndustryByTaz)
    {
        jobsByIndustryByTaz.setTazId(values.get<BigSerial>("taz_id",0));
        jobsByIndustryByTaz.setIndustryType1(values.get<int>("industry1",0));
        jobsByIndustryByTaz.setIndustryType2(values.get<int>("industry2",0));
        jobsByIndustryByTaz.setIndustryType3(values.get<int>("industry3",0));
        jobsByIndustryByTaz.setIndustryType4(values.get<int>("industry4",0));
        jobsByIndustryByTaz.setIndustryType5(values.get<int>("industry5",0));
        jobsByIndustryByTaz.setIndustryType6(values.get<int>("industry6",0));
        jobsByIndustryByTaz.setIndustryType7(values.get<int>("industry7",0));
        jobsByIndustryByTaz.setIndustryType8(values.get<int>("industry8",0));
        jobsByIndustryByTaz.setIndustryType9(values.get<int>("industry9",0));
        jobsByIndustryByTaz.setIndustryType10(values.get<int>("industry10",0));
        jobsByIndustryByTaz.setIndustryType11(values.get<int>("industry11",0));
        jobsByIndustryByTaz.setIndustryType98(values.get<int>("industry98",0));
    }
};

template<>
struct type_conversion<sim_mob::long_term::IndLogsumJobAssignment>
{
    typedef values base_type;

    static void
    from_base(soci::values const & values, soci::indicator & indicator, sim_mob::long_term::IndLogsumJobAssignment& indLogsumJobAssignment)
    {
        indLogsumJobAssignment.setIndividualId(values.get<BigSerial>("individual_id",0));
        indLogsumJobAssignment.setTazId(values.get<std::string>("taz_id",0));
        indLogsumJobAssignment.setLogsum(values.get<float>("logsum",.0));

    }
};

template<>
struct type_conversion<sim_mob::long_term::JobsWithIndustryTypeAndTazId>
{
    typedef values base_type;

    static void
    from_base(soci::values const & values, soci::indicator & indicator, sim_mob::long_term::JobsWithIndustryTypeAndTazId& jobsWithIndustryTypeAndTazId)
    {
        jobsWithIndustryTypeAndTazId.setJobId(values.get<BigSerial>("job_id",0));
        jobsWithIndustryTypeAndTazId.setIndustryTypeId(values.get<int>("industry_type_id",0));
        jobsWithIndustryTypeAndTazId.setTazId(values.get<BigSerial>("taz_id",0));

    }
};

template<>
struct type_conversion<sim_mob::long_term::School>
{
    typedef values base_type;

    static void
    from_base(soci::values const & values, soci::indicator & indicator, sim_mob::long_term::School& school)
    {
        school.setId(values.get<BigSerial>("id",0));
        school.setFmBuildingId(values.get<BigSerial>("fm_building_id",0));
        school.setFloorArea(values.get<double>("floor_area",0));
        school.setSchoolSlot(values.get<double>("school_slot",0));
        school.setCentroidX(values.get<double>("centroid_x",0));
        school.setCentroidY(values.get<double>("centroid_y",0));
        school.setGiftedProgram(values.get<int>("gifted_program",0));
        school.setSapProgram(values.get<int>("sap_program",0));
        school.setPlanningArea(values.get<std::string>("planning_area",std::string()));
        school.setTazName(values.get<BigSerial>("taz_name",0));
        school.setSchoolType(values.get<std::string>("school_type",std::string()));
        school.setArtProgram(values.get<int>("art_program",0));
        school.setMusicProgram(values.get<int>("music_program",0));
        school.setLangProgram(values.get<int>("lang_program",0));
        school.setStudentDensity(values.get<double>("student_den",0));
        school.setExpressTest(values.get<int>("express_test",0));
    }
};

template<>
struct type_conversion<sim_mob::long_term::EzLinkStop>
{
    typedef values base_type;

    static void
    from_base(soci::values const & values, soci::indicator & indicator, sim_mob::long_term::EzLinkStop& ezLinkStop)
    {
        ezLinkStop.setId(values.get<BigSerial>("id",0));
        ezLinkStop.setXCoord(values.get<double>("x_coord",0));
        ezLinkStop.setYCoord(values.get<double>("y_coord",0));
    }
};

template<>
struct type_conversion<sim_mob::long_term::StudentStop>
{
    typedef values base_type;

    static void
    from_base(soci::values const & values, soci::indicator & indicator, sim_mob::long_term::StudentStop& studentStop)
    {
        studentStop.setHomeStopEzLinkId(values.get<int>("home_stop_ez_link_id",0));
        studentStop.setSchoolStopEzLinkId(values.get<int>("school_stop_ez_link_id",0));
    }
};

template<>
struct type_conversion<sim_mob::long_term::SchoolDesk>
{
    typedef values base_type;

    static void
    from_base(soci::values const & values, soci::indicator & indicator, sim_mob::long_term::SchoolDesk& schoolDesk)
    {
        schoolDesk.setSchoolDeskId(values.get<BigSerial>("school_desk_id",0));
        schoolDesk.setSchoolId(values.get<BigSerial>("school_id",0));
    }
};

template<>
struct type_conversion<sim_mob::long_term::ResidentialWTP_Coefs>
{
    typedef values base_type;

    static void
    from_base(soci::values const & values, soci::indicator & indicator, sim_mob::long_term::ResidentialWTP_Coefs& wtpCoeffs)
    {
        wtpCoeffs.setId(values.get<int>("id",0));
        wtpCoeffs.setPropertyType(values.get<std::string>("property_type",std::string()));
        wtpCoeffs.setSde(values.get<double>("sde",0));
        wtpCoeffs.setM2(values.get<double>("m2",0));
        wtpCoeffs.setS2(values.get<double>("s2",0));
        wtpCoeffs.setConstant(values.get<double>("constant",0));
        wtpCoeffs.setLogArea(values.get<double>("log_area",0));
        wtpCoeffs.setLogsumTaz(values.get<double>("logsum_taz",0));
        wtpCoeffs.setAge(values.get<double>("age",0));
        wtpCoeffs.setAgeSquared(values.get<double>("age_squared",0));
        wtpCoeffs.setMissingAgeDummy(values.get<double>("missing_age_dummy",0));
        wtpCoeffs.setCarDummy(values.get<double>("car_dummy",0));
        wtpCoeffs.setCarIntoLogsumTaz(values.get<double>("car_into_logsum_taz",0));
        wtpCoeffs.setDistanceMall(values.get<double>("distance_mall",0));
        wtpCoeffs.setMrt200m400m(values.get<double>("mrt_200_400m_dummy",0));
        wtpCoeffs.setMatureDummy(values.get<double>("mature_dummy",0));
        wtpCoeffs.setMatureOtherDummy(values.get<double>("mature_other_dummy",0));
        wtpCoeffs.setFloorNumber(values.get<double>("floor_number",0));
        wtpCoeffs.setLogIncome(values.get<double>("log_income",0));
        wtpCoeffs.setLogIncomeIntoLogArea(values.get<double>("log_income_into_log_area",0));
        wtpCoeffs.setFreeholdApartment(values.get<double>("freehold_apartment",0));
        wtpCoeffs.setFreeholdCondo(values.get<double>("freehold_condo",0));
        wtpCoeffs.setFreeholdTerrace(values.get<double>("freehold_terrace",0));
        wtpCoeffs.setFreeholdDetached(values.get<double>("freehold_detached",0));
        wtpCoeffs.setBus200m400mDummy(values.get<double>("bus_200_400m_dummy",0));
        wtpCoeffs.setOneTwoFullTimeWorkerDummy(values.get<double>("one_two_fulltime_worker_dummy",0));;
        wtpCoeffs.setFullTimeWorkersTwoIntoLogArea(values.get<double>("fulltime_workers2_into_log_area",0));
        wtpCoeffs.setHhSizeworkersDiff(values.get<double>("hh_size_wrokers_diff",0));

    }
};

} //namespace soci


#include <iostream>
#include <fstream>
#include <sstream>
#include <iomanip>
#include <string>
#include <algorithm>
#include <iterator>
#include <numeric>
#include <ext/numeric>
#include <boost/program_options.hpp>
#include <boost/filesystem.hpp>

#include <stdlib.h>
#include <math.h>
#include <assert.h>

#include "mpi_kmeans.h"

namespace po = boost::program_options;

static unsigned int count_lines(const std::string& filename) {
	std::ifstream in(filename.c_str());
	if (in.fail()) {
		std::cerr << "count_lines, failed to open \"" << filename
			<< "\"." << std::endl;
		exit(EXIT_FAILURE);
	}
	unsigned int lines = 0;
	std::string line;
	while (in.eof() == false) {
		std::getline(in, line);
		if (line.size() == 0)
			continue;
		lines += 1;
	}
	in.close();
	return (lines);
}

static void write_cluster_centers(const std::string& output_filename,
								  const std::vector<std::vector<double> >& data_CX) {

	std::cout << "Writing cluster centers to \""
			  << output_filename << "\"" << std::endl;

	std::ofstream wout(output_filename.c_str());
	if (wout.fail()) {
		std::cerr << "Failed to open \"" << output_filename
				  << "\" for writing." << std::endl;
		exit(EXIT_FAILURE);
	}

	wout << std::setprecision(12);
	for (unsigned int m=0; m < data_CX.size(); ++m) {
		for (unsigned int n=0; n < data_CX[m].size(); ++n) {
			wout << (n==0? "":" ") << data_CX[m][n];
		}
		wout << std::endl;
	}
	wout.close();

	return;
}

static void write_cluster_centers(const std::string& output_filename,
								  double *data_CX,
								  unsigned int nof_clusters,
								  unsigned int dims) {

	std::cout << "Writing cluster centers to \""
			  << output_filename << "\"" << std::endl;

	std::ofstream wout(output_filename.c_str());
	if (wout.fail()) {
		std::cerr << "Failed to open \"" << output_filename
				  << "\" for writing." << std::endl;
		exit(EXIT_FAILURE);
	}

	wout << std::setprecision(12);
	unsigned int cntr = 0;
	for (unsigned int m=0; m < nof_clusters; ++m) {
		for (unsigned int n=0; n < dims; ++n) {
			wout << (n==0? "":" ") << data_CX[cntr];
			cntr += 1;
		}
		wout << std::endl;
	}
	wout.close();

	return;
}

static int read_problem_data(const std::string& train_filename,
							  std::vector<std::vector<double> >& data_X) {
	data_X.clear();

	std::ifstream in(train_filename.c_str());
	if (in.fail()) {
		std::cerr << "Failed to open file \""
				  << train_filename << "\" for reading." << std::endl;
		std::cerr << "Try mpi_assign --help" << std::endl;
		exit(EXIT_FAILURE);
	}

	std::string line;
	unsigned int ndims = 0;
	while (in.eof() == false) {
		std::getline(in,line);
		if (line.size() == 0)
			continue; // skip over empty lines

		// remove trailing whitespaces
		line.erase(line.find_last_not_of(" ")+1);

		std::vector<double> current_data;
		std::istringstream is(line);
		while (is.eof() == false) {
			double value;
			is >> value;
			current_data.push_back(value);
		}

		// Ensure the same number of dimensions for each point
		if (ndims == 0)
			ndims = current_data.size();
		assert(ndims == current_data.size());
		data_X.push_back(current_data);
	}
	in.close();

	if (data_X.size() == 0) {
		std::cerr << "No points read from file \"" << train_filename
				  << "\"" << std::endl;
		std::cerr << "Try mpi_assign --help" << std::endl;
		exit(EXIT_FAILURE);
	}

	return(data_X.size());

}

static int read_problem_data(const std::string& train_filename,
							  double *data_X) {

	std::ifstream in(train_filename.c_str());
	if (in.fail()) {
		std::cerr << "Failed to open file \""
				  << train_filename << "\" for reading." << std::endl;
		std::cerr << "Try mpi_assign --help" << std::endl;
		exit(EXIT_FAILURE);
	}

	unsigned int nof_points = count_lines(train_filename);
	if (nof_points == 0) {
		std::cerr << "No points read from file \"" << train_filename
				  << "\"" << std::endl;
		std::cerr << "Try mpi_assign --help" << std::endl;
		exit(EXIT_FAILURE);
	}

	std::string line;
	unsigned int ndims = 0;
	unsigned int cntr = 0;
	while (in.eof() == false) {
		std::getline(in,line);
		if (line.size() == 0)
			continue; // skip over empty lines

		// remove trailing whitespaces
		line.erase(line.find_last_not_of(" ")+1);

		std::vector<double> current_data;
		std::istringstream is(line);
		while (is.eof() == false) {
			double value;
			is >> value;
			current_data.push_back(value);
		}

		// Ensure the same number of dimensions for each point
		if (ndims == 0)
			ndims = current_data.size();
		assert(ndims == current_data.size());
		if (data_X==NULL)
			data_X = (double *)malloc(nof_points * ndims * sizeof(double));

		for (unsigned int i=0; i<ndims; ++i) {
			data_X[cntr] = current_data[i];
			cntr += 1;
		}
	}
	in.close();

	return(nof_points);

}

int main(int argc, char* argv[]) {

	std::string train_filename;
	std::string weight_filename;
	std::string output_filename;
	int nof_clusters;
	int nof_restarts;
	int maxiter;

	// Set Program options
	po::options_description generic("Generic Options");
	generic.add_options()
		("help","Produce help message")
		("verbose","Verbose output")
		;

	po::options_description input_options("Input/Output Options");
	input_options.add_options()
		("data",po::value<std::string>
		 (&train_filename)->default_value("data.txt"),
		 "Training file, one datum per line")
		("output",po::value<std::string>
		 (&output_filename)->default_value("output.txt"),
		 "Output file, one cluster center per line")
		("weights",po::value<std::string>
		 (&weight_filename)->default_value(""),
		 "Weighting of exmaples, one number per line")
		;

	po::options_description kmeans_options("K-Means Options");
	kmeans_options.add_options()
		("k",po::value<int>(&nof_clusters)->default_value(100),
		 "Number of clusters to generate")
		("restarts",po::value<int>(&nof_restarts)->default_value(0),
		 "Number of K-Means restarts. (0: single run)")
		("maxiter",po::value<int>(&maxiter)->default_value(0),
		 "Maximum number of K-Means iterations. (0: infinity)")
		;

	po::options_description all_options;
	all_options.add(generic).add(input_options).add(kmeans_options);
	po::variables_map vm;
	po::store(po::command_line_parser(argc,argv).options(all_options).run(), vm);
	po::notify(vm);

	bool verbose = vm.count("verbose");

	if (vm.count("help")) {
		std::cerr << "K-Means clustering" << std::endl;
		std::cerr << all_options << std::endl;
		std::cerr << std::endl;
		std::cerr << "Example:" << std::endl;
		std::cerr << "  mpi_kmeans --k 2 --data example.txt --output clusters.txt" << std::endl;
		exit(EXIT_SUCCESS);
	}

	// read in the problem
	std::cout << "Training file: " << train_filename << std::endl;
	std::vector<std::vector<double> > data_X; // so far kmeans does not support std::<vector>
	int nof_points = read_problem_data(train_filename,data_X);
	assert(nof_points>0);

	unsigned int dims = data_X[0].size();
	assert(dims>0);

	// convert points to double*
	double *X = (double *)malloc(nof_points * dims * sizeof(double));
	unsigned int cntr = 0;
	for (unsigned int m=0; m < data_X.size() ; ++m) {
		for (unsigned int n=0; n < data_X[m].size() ; ++n) {
			X[cntr] = data_X[m][n];
			cntr += 1;
		}
		data_X[m].clear();
	}
	data_X.clear();

	// read in weighting
	double *W = NULL;
	if (weight_filename != "") {
		std::cout << "Weighting file: " << weight_filename << std::endl;
		std::vector<std::vector<double> > data_W; // so far kmeans does not support std::<vector>
		int nof_points_wfile = read_problem_data(weight_filename,data_W);
		assert(nof_points_wfile == nof_points);

		unsigned int dims_wfile = data_W[0].size();
		assert(dims_wfile==1);

		// convert points to double*
		W = (double *)malloc(nof_points * sizeof(double));
		for (unsigned int m=0; m < data_W.size() ; ++m)
			W[m] = data_W[m][0];
		data_W.clear();

	}

	// start K-Means
	std::cout << "Starting Kmeans ..." << std::endl;
	std::cout << " ... with " << nof_points << " training points " <<std::endl;
	std::cout << " ... for " << nof_clusters << " clusters " <<std::endl;

	unsigned int *assignment = (unsigned int *)malloc(nof_points * sizeof(unsigned int));
	double *CX = (double *) calloc(nof_clusters * dims, sizeof(double));
	double sse = kmeans(CX, X, W, assignment, dims, nof_points, nof_clusters, maxiter, nof_restarts);
	free(X);
	assert(CX);

	std::cout << "Done!" << std::endl;
	std::cout << "Sum of Squared Error : " << sse << std::endl;

	// write the clusters
	// write_cluster_centers(output_filename,data_X);
	write_cluster_centers(output_filename,CX,nof_clusters,dims);

	// done
	exit(EXIT_SUCCESS);
}


//Trevor Hickey
//Homework 1: GCD
//Sept. 11

//STD
#include <iostream>
#include <string>
#include <cmath>
#include <functional>

//Boost
#include <boost/random/mersenne_twister.hpp>
#include <boost/random/uniform_int_distribution.hpp>
#include <boost/date_time/posix_time/posix_time.hpp>

//My Sources
#include "globals.hpp"         // types & constants
#include "sort_algorithms.hpp" // namespace
#include "algorithm.hpp"       // class
#include "test_suite.hpp"      // namespace

//Function Decelerations
void Validate_Algorithm_Correctness(void);
void Test_Algorithm_Speed(void);
void Output_Title_Of_Table(std::vector<Algorithm<>> const& algorithms);
void Perform_Size_Test(boost::random::mt19937 & gen, int size, std::vector<Algorithm<>> algorithms);
void Output_Results(int i, std::vector<Algorithm<>> const& algorithms);
template <typename IntegerType> void Run_Sort_Performance_Test(Algorithm<IntegerType> & alg, std::vector<Default_Test_Element> vec);

int main(){

	//test that the algorithms work
	Validate_Algorithm_Correctness();

	//output speed performance data for each algorithm
	//Test_Algorithm_Speed();

	return EXIT_SUCCESS;
}

//Function Definitions
void Validate_Algorithm_Correctness(void){

	//we generate random numbers of different sized lists and check to see if they are correctly sorted.
	//If there is an issue, the function will report a message through std::cerr
	Test_Suite::Check_Algorithm_Validity ( Sort::Bubble       );
	Test_Suite::Check_Algorithm_Validity ( Sort::Smart_Bubble );
	Test_Suite::Check_Algorithm_Validity ( Sort::Selection    );

	return;
}
void Test_Algorithm_Speed(void){

	//a vector containing all of the different algorithms we will be testing
	std::vector<Algorithm<>> algorithms;
	algorithms.push_back(Algorithm<>( "Bubble"        ,Sort::Bubble       ));
	algorithms.push_back(Algorithm<>( "Smart Bubble"  ,Sort::Smart_Bubble ));
	algorithms.push_back(Algorithm<>( "Selection"     ,Sort::Selection    ));

	//build a generator and test the algorithm across a range of bit lengths
	boost::random::mt19937 gen(std::time(0));
	Output_Title_Of_Table(algorithms);
	for (auto list_size = constant::min_number_of_elements; list_size < constant::max_number_of_elements+1; ++list_size){
		Perform_Size_Test(gen,list_size,algorithms);
	}

	return;
}

void Output_Title_Of_Table(std::vector<Algorithm<>> const& algorithms){

	std::cout << "testing the speed of sort algorithms in milliseconds (" << constant::num_of_trials << " trials for each list length)\n";
	//print out a terrible title just so we know what algorithm is in each column
	for (auto const& it: algorithms){std::cout << "|" << it.name << "|";}
	std::cout << '\n';

	return;
}

void Perform_Size_Test(boost::random::mt19937 & gen, int size, std::vector<Algorithm<>> algorithms){

	Default_Test_Element a;
	std::vector<Default_Test_Element> vec;
	auto dist = Test_Suite::Get_Specified_Bit_Length_Distribution(30);
	for (int x = 0; x < constant::num_of_trials; ++x){

		//fill up the vector
		for (int i = 0; i < size; ++i){
			a = dist(gen);
			vec.push_back(a);
		}

		//test a sort on the vector
		for (auto & it: algorithms){
			Run_Sort_Performance_Test(it,vec);
		}
		vec.clear();
	}

	//once data is collected for each algorithm in regards to
	//specific list lengths, output the results.
	//NOTE: this data will be lost after the function returns.
	//This is because the algorithm classes will be re-used on different list lengths.
	Output_Results(size,algorithms);

	return;
}
void Output_Results(int i, std::vector<Algorithm<>> const& algorithms){

	//output the total time it took to run the number of trials
	//*You know what, I'm just going to calculate the average in whatever graph software I use
	std::cout << "list-size " << i << ">";
	for (auto it: algorithms){
		std::cout << " " << "(" << (it.total_time) << ")";
	}
	std::cout << '\n';

	return;
}
template <typename IntegerType> void Run_Sort_Performance_Test(Algorithm<IntegerType> & alg, std::vector<Default_Test_Element> vec){

	boost::posix_time::ptime startTime, endTime;

	//time the execution of the algorithm
	startTime = boost::posix_time::microsec_clock::local_time();
	alg.fun(vec);
	endTime = boost::posix_time::microsec_clock::local_time();

	alg.total_time += (endTime-startTime);
	++alg.trials;

	return;
}


/* Copyright (c) 2016, Julian Straub <jstraub@csail.mit.edu> Licensed
 * under the MIT license. See the license file LICENSE.
 */

#include <Eigen/Dense>

#include <pangolin/pangolin.h>
#include <pangolin/utils/file_utils.h>
#include <pangolin/utils/timer.h>
#include <pangolin/gl/gl.h>
#include <pangolin/gl/glsl.h>
#include <pangolin/gl/glvbo.h>
#include <pangolin/gl/gldraw.h>
#include <pangolin/utils/timer.h>

#include <tdp/gui/quickView.h>
#include <tdp/drivers/inertial/3dmgx3_45.h>
#include <tdp/inertial/imu_outstream.h>
#include <tdp/manifold/SO3.h>
#include <tdp/manifold/SE3.h>

#include <tdp/inertial/pose_interpolator.h>
#include <tdp/inertial/imu_factory.h>

int main( int argc, char* argv[] )
{
  const std::string dflt_output_uri = "./imu.pango";
  std::string input_uri = "";
  std::string output_uri = dflt_output_uri;
  if( argc > 1 ) {
    input_uri = std::string(argv[1]);
    output_uri = argc > 2 ? std::string(argv[2]) : dflt_output_uri;
  }

  // Create OpenGL window - guess sensible dimensions
  int menue_w = 180;
  pangolin::CreateWindowAndBind( "GUI", 1000+menue_w, 800);
  pangolin::CreatePanel("ui").SetBounds(0.,1.,0.,pangolin::Attach::Pix(menue_w));

  // Assume packed OpenGL data unless otherwise specified
  glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
  glPixelStorei(GL_PACK_ALIGNMENT, 1);
  glEnable (GL_BLEND);
  glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

  pangolin::View& container = pangolin::Display("container");
  container.SetLayout(pangolin::LayoutEqual)
    .SetBounds(0., 1.0, pangolin::Attach::Pix(menue_w), 1.0);

  // Define Camera Render Object (for view / scene browsing)
  pangolin::OpenGlRenderState s_cam(
      pangolin::ProjectionMatrix(640,480,420,420,320,240,0.1,1000),
      pangolin::ModelViewLookAt(0,0.5,-3, 0,0,0, pangolin::AxisNegY)
      );
  // Add named OpenGL viewport to window and provide 3D Handler
  pangolin::View& d_cam = pangolin::CreateDisplay()
    .SetHandler(new pangolin::Handler3D(s_cam));
  container.AddDisplay(d_cam);

  pangolin::View& plotters = pangolin::Display("plotters");
  plotters.SetLayout(pangolin::LayoutEqualVertical);
  pangolin::DataLog logAcc;
  pangolin::Plotter plotAcc(&logAcc, -1000.f,1.f, -10.f,10.f, 10.f, 0.1f);
  plotters.AddDisplay(plotAcc);
  pangolin::DataLog logOmega;
  pangolin::Plotter plotOmega(&logOmega, -1000.f,1.f, -1.f,1.f, 10.f, 0.1f);
  plotters.AddDisplay(plotOmega);
  pangolin::DataLog logMag;
  pangolin::Plotter plotMag(&logMag, -1000.f,1.f, -10.f,10.f, 10.f, 0.1f);
  plotters.AddDisplay(plotMag);
  container.AddDisplay(plotters);

  pangolin::Var<bool> logImu("ui.log IMU", true, true);
  pangolin::Var<bool> verbose("ui.verbose", false, true);
  pangolin::Var<bool> applyCalib("ui.apply R_ir", false, true);
  pangolin::Var<bool> useTHost("ui.use t_host", false, true);

  tdp::ImuInterface* imu = tdp::OpenImu(input_uri);
  if (!imu) return 1;
  imu->Start();

  tdp::ImuOutStream imu_out(output_uri);
  imu_out.Open(input_uri, imu->GetProperties());

  tdp::PoseInterpolator imuInterp;
  tdp::ThreadedValue<bool> receiveImu(true);
  tdp::ThreadedValue<size_t> numReceived(0);
  std::thread receiverThread (
    [&]() {
      tdp::ImuObs imuObs;
      tdp::ImuObs imuObsPrev;
      bool calibrated = false;
      Eigen::Vector3f gyro_bias = Eigen::Vector3f::Zero();
      int numCalib = 0;
      while(receiveImu.Get()) {
        if (imu->GrabNext(imuObs)) {

          if (logImu)
            imu_out.WriteStream(imuObs);

          if (!calibrated && numReceived.Get() > 10
            && imuObs.omega.norm() < 2./180.*M_PI) {
            gyro_bias += imuObs.omega;
            numCalib ++;
            std::cout << imuObs.omega.norm()*180./M_PI << std::endl;
          } else if (!calibrated && numReceived.Get() > 10) {
            calibrated = true;
            gyro_bias /= numCalib;
            std::cout << "IMU gyro calibrated: " << gyro_bias.transpose()
              << std::endl;
          } else {
            imuObs.omega -= gyro_bias;
          }

          Eigen::Matrix<float,6,1> se3 = Eigen::Matrix<float,6,1>::Zero();
          se3.topRows(3) = imuObs.omega;
          if (numReceived.Get() == 0) {
            imuInterp.Add(imuObs.t_host, tdp::SE3f());
          } else {
            int64_t dt_ns = imuObs.t_device - imuObsPrev.t_device;
            imuInterp.Add(imuObs.t_host, se3, dt_ns);
          }
          imuObsPrev = imuObs;
          numReceived.Increment();

          logAcc.Log(imuObs.acc(0),imuObs.acc(1),imuObs.acc(2));
          logMag.Log(imuObs.rpy(0), imuObs.rpy(1), imuObs.rpy(2));
          logOmega.Log(imuObs.omega(0), imuObs.omega(1), imuObs.omega(2));

        }
        std::this_thread::sleep_for(std::chrono::microseconds(100));
      }
    });

  Eigen::Matrix3f R_ir;
  R_ir << 0, 0,-1,
       0,-1, 0,
       -1, 0, 0;
  tdp::SE3f T_ir(R_ir,Eigen::Vector3f::Zero());

  tdp::SO3f R_wi;
  tdp::ImuObs imuObsPrev;
  size_t numReceivedPrev = 0;
  while(!pangolin::ShouldQuit())
  {
    // clear the OpenGL render buffers
    glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT);
    glColor3f(1.0f, 1.0f, 1.0f);
    // get next frames from the video source

    size_t numObs = numReceived.Get() - numReceivedPrev;
    plotAcc.ScrollView(numObs,0);
    plotMag.ScrollView(numObs,0);
    plotOmega.ScrollView(numObs,0);
    numReceivedPrev = numReceived.Get();

    int64_t tNow = pangolin::Time_us(pangolin::TimeNow())*1000;
    tdp::SE3f T_wi = imuInterp[tNow];

    // Draw 3D stuff
    glEnable(GL_DEPTH_TEST);
    d_cam.Activate(s_cam);
    pangolin::glDrawAxis(1.0f);
    // draw the axis
    if (applyCalib) {
      T_wi = T_wi * T_ir;
    }
    pangolin::glDrawAxis<float>(T_wi.matrix(),0.8f);
    glDisable(GL_DEPTH_TEST);
    // finish this frame
    pangolin::FinishFrame();
  }
  receiveImu.Set(false);
  receiverThread.join();
  imu->Stop();
  imu_out.Close();

  return 0;
}


/*
 Copyright (C) 2019 Quaternion Risk Management Ltd
 All rights reserved.

 This file is part of ORE, a free-software/open-source library
 for transparent pricing and risk analysis - http://opensourcerisk.org

 ORE is free software: you can redistribute it and/or modify it
 under the terms of the Modified BSD License.  You should have received a
 copy of the license along with this program.
 The license is also available online at <http://opensourcerisk.org>

 This program is distributed on the basis that it will form a useful
 contribution to risk analytics and model standardisation, but WITHOUT
 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 FITNESS FOR A PARTICULAR PURPOSE. See the license for more details.
*/

/*! \file ored/marketdata/strike.hpp
    \brief Classes for representing a strike using various conventions.
    \ingroup marketdata
*/

#pragma once

#include <ql/experimental/fx/deltavolquote.hpp>
#include <ql/option.hpp>
#include <ql/types.hpp>

#include <boost/optional.hpp>
#include <boost/serialization/base_object.hpp>
#include <boost/serialization/export.hpp>
#include <boost/serialization/version.hpp>
#include <boost/serialization/optional.hpp>
#include <boost/shared_ptr.hpp>

namespace ore {
namespace data {

/*! Abstract base class to hold information that describes a strike.
    Need to use Base in class Name to differentiate from existing ore::data::Strike.
*/
class BaseStrike {
public:
    virtual ~BaseStrike() {}

    //! Populate the Strike object from \p strStrike
    virtual void fromString(const std::string& strStrike) = 0;

    //! Write the Strike object to string
    virtual std::string toString() const = 0;

    //! Will be used for Strike comparison.
    friend bool operator==(const BaseStrike& lhs, const BaseStrike& rhs);

protected:
    //! Override in derived classes to compare specific Strikes.
    virtual bool equal_to(const BaseStrike& other) const = 0;

private:
    //! Serialization
    friend class boost::serialization::access;
    template <class Archive> void serialize(Archive& ar, const unsigned int version);
};

/*! Strike implementation where the strike is described by a single number that represents the absolute strike level.
 */
class AbsoluteStrike : public BaseStrike {
public:
    //! Default constructor.
    AbsoluteStrike();

    //! Constructor with explicit strike.
    AbsoluteStrike(QuantLib::Real strike);

    //! Return the absolute strike level.
    QuantLib::Real strike() const;

    /*! Populate AbsoluteStrike object from \p strStrike which should be a number.
        An exception is thrown if \p strStrike cannot be parsed as a \c QuantLib::Real.
    */
    void fromString(const std::string& strStrike) override;

    /*! Writes the AbsoluteStrike object to string. This returns the string representation of
        the absolute strike number.
    */
    std::string toString() const override;

protected:
    bool equal_to(const BaseStrike& other) const override;

private:
    QuantLib::Real strike_;
    //! Serialization
    friend class boost::serialization::access;
    template <class Archive> void serialize(Archive& ar, const unsigned int version);
};

/*! Strike implementation where the strike is described by a delta type, an option type and a delta level.
 */
class DeltaStrike : public BaseStrike {
public:
    //! Default constructor.
    DeltaStrike();

    //! Explicit constructor
    DeltaStrike(QuantLib::DeltaVolQuote::DeltaType deltaType, QuantLib::Option::Type optionType, QuantLib::Real delta);

    //! \name Inspectors
    //@{
    //! Return the delta type
    QuantLib::DeltaVolQuote::DeltaType deltaType() const;

    //! Return the option type
    QuantLib::Option::Type optionType() const;

    //! Return the delta level
    QuantLib::Real delta() const;
    //@}

    /*! Populate DeltaStrike object from \p strStrike.

        The \p strStrike is expected to be of the form `DEL / Spot|Fwd|PaSpot|PaFwd / Call|Put / DELTA_VALUE`. An
        exception is thrown if \p strStrike is not of this form and cannot be parsed properly.
    */
    void fromString(const std::string& strStrike) override;

    /*! Writes the DeltaStrike object to string.

        The string representation of the DeltaStrike object is of the form
        `DEL / Spot|Fwd|PaSpot|PaFwd / Call|Put / DELTA_VALUE`.
    */
    std::string toString() const override;

protected:
    bool equal_to(const BaseStrike& other) const override;

private:
    QuantLib::DeltaVolQuote::DeltaType deltaType_;
    QuantLib::Option::Type optionType_;
    QuantLib::Real delta_;
    //! Serialization
    friend class boost::serialization::access;
    template <class Archive> void serialize(Archive& ar, const unsigned int version);
};

/*! Strike implementation for an at-the-money strike of various types.
 */
class AtmStrike : public BaseStrike {
public:
    //! Default constructor.
    AtmStrike();

    /*! Explicit constructor. Note that:
        - an \p atmType of \c AtmNull throws an error.
        - if \p atmType is \c AtmDeltaNeutral, a \c deltaType is needed.
        - if \p atmType is not \c AtmDeltaNeutral, \c deltaType should not be provided.
        - if \p atmType is \c AtmPutCall50, \p deltaType must be \c DeltaVolQuote::Fwd.
    */
    AtmStrike(QuantLib::DeltaVolQuote::AtmType atmType,
              boost::optional<QuantLib::DeltaVolQuote::DeltaType> deltaType = boost::none);

    //! \name Inspectors
    //@{
    //! Return the ATM type
    QuantLib::DeltaVolQuote::AtmType atmType() const;

    //! Return the delta type
    boost::optional<QuantLib::DeltaVolQuote::DeltaType> deltaType() const;
    //@}

    /*! Populate AtmStrike object from \p strStrike.

        The \p strStrike is expected to be of the form:
        `ATM / AtmSpot|AtmFwd|AtmDeltaNeutral|AtmVegaMax|AtmGammaMax|AtmPutCall50` followed by an optional
        `/ DEL / Spot|Fwd|PaSpot|PaFwd` to specify the delta if it is needed. An exception is thrown if \p strStrike
        is not of this form and cannot be parsed properly.
    */
    void fromString(const std::string& strStrike) override;

    /*! Writes the AtmStrike object to string.

        The string representation of the DeltaStrike object is of the form
        `ATM / AtmSpot|AtmFwd|AtmDeltaNeutral|AtmVegaMax|AtmGammaMax|AtmPutCall50` followed by an optional
        `/ DEL / Spot|Fwd|PaSpot|PaFwd` if the delta type has been populated.
    */
    std::string toString() const override;

protected:
    bool equal_to(const BaseStrike& other) const override;

private:
    QuantLib::DeltaVolQuote::AtmType atmType_;
    boost::optional<QuantLib::DeltaVolQuote::DeltaType> deltaType_;

    //! Perform validation
    void check() const;

    //! Serialization
    friend class boost::serialization::access;
    template <class Archive> void serialize(Archive& ar, const unsigned int version);
};

/*! Strike implementation where the strike is described by a moneyness type and a moneyness level.
 */
class MoneynessStrike : public BaseStrike {
public:
    /*! The MoneynessStrike type.
        - When the moneyness type is \c Spot, the moneyness level will be interpreted as the implicit strike divided
          by the spot value.
        - When the moneyness type is \c Forward, the moneyness level will be interpreted as the implicit strike
          divided by the forward value.
    */
    enum class Type { Spot, Forward };

    //! Default constructor.
    MoneynessStrike();

    //! Explicit constructor.
    MoneynessStrike(Type type, QuantLib::Real moneyness);

    //! \name Inspectors
    //@{
    //! Return the moneyness type
    Type type() const;

    //! Return the moneyness level.
    QuantLib::Real moneyness() const;
    //@}

    /*! Populate MoneynessStrike object from \p strStrike.

        The \p strStrike is expected to be of the form `MNY / Spot|Fwd / MONEYNESS_VALUE`.
    */
    void fromString(const std::string& strStrike) override;

    /*! Writes the MoneynessStrike object to string.

        The string representation of the MoneynessStrike object is of the form `MNY / Spot|Fwd / MONEYNESS_VALUE`.
    */
    std::string toString() const override;

protected:
    bool equal_to(const BaseStrike& other) const override;

private:
    Type type_;
    QuantLib::Real moneyness_;

    //! Serialization
    friend class boost::serialization::access;
    template <class Archive> void serialize(Archive& ar, const unsigned int version);
};

//! Write \p strike to stream.
std::ostream& operator<<(std::ostream& os, const BaseStrike& strike);

//! Write \p deltaType to stream. Not provided in QuantLib so add it here.
std::ostream& operator<<(std::ostream& os, QuantLib::DeltaVolQuote::DeltaType type);

//! Write \p atmType to stream. Not provided in QuantLib so add it here.
std::ostream& operator<<(std::ostream& os, QuantLib::DeltaVolQuote::AtmType type);

//! Write MoneynessStrike::Type, \p type, to stream.
std::ostream& operator<<(std::ostream& os, MoneynessStrike::Type type);

//! Parse MoneynessStrike::Type from \p type
MoneynessStrike::Type parseMoneynessType(const std::string& type);

//! Parse a Strike from its string representation, \p strStrike.
boost::shared_ptr<BaseStrike> parseBaseStrike(const std::string& strStrike);

template <class Archive> void registerBaseStrike(Archive& ar) {
    ar.template register_type<AbsoluteStrike>();
    ar.template register_type<DeltaStrike>();
    ar.template register_type<AtmStrike>();
    ar.template register_type<MoneynessStrike>();
}

} // namespace data
} // namespace ore

BOOST_CLASS_EXPORT_KEY(ore::data::AbsoluteStrike);
BOOST_CLASS_EXPORT_KEY(ore::data::DeltaStrike);
BOOST_CLASS_EXPORT_KEY(ore::data::AtmStrike);
BOOST_CLASS_EXPORT_KEY(ore::data::MoneynessStrike);


//==================================================================================================
/*!
  @file

    @copyright 2016 NumScale SAS

  Distributed under the Boost Software License, Version 1.0.
  (See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt)
*/
//==================================================================================================
#ifndef BOOST_SIMD_FUNCTION_POPCNT_HPP_INCLUDED
#define BOOST_SIMD_FUNCTION_POPCNT_HPP_INCLUDED

#if defined(DOXYGEN_ONLY)
namespace boost { namespace simd
{

  /*!
    @ingroup group-bitwise
    This function object returns the number of set bits in the input.

    @see  clz, ctz

    @par Header <boost/simd/function/popcnt.hpp>

    @par Example:

      @snippet popcnt.cpp popcnt

    @par Possible output:

      @snippet popcnt.txt popcnt

  **/
  as_integer_t<Value> popcnt(Value const& x);
} }
#endif

#include <boost/simd/function/scalar/popcnt.hpp>
#include <boost/simd/function/simd/popcnt.hpp>

#endif


#include <iostream>
#include <sstream>
#include <string>
#include <unordered_map>
#include <vector>
#include <set>
#include <dlib/clustering.h>
#include "realty_data.h"

bool file_exists(const std::string& filename) {
    std::ifstream infile(filename);
    return infile.good();
}

int main(int argc, char *argv[]) {
    bool executed_correctly = true;
    std::stringstream whats_wrong;
    std::string filename;

    if (argc != 2) {
        executed_correctly = false;
        whats_wrong << "Incorrect number of arguments";
    } else {
        filename = argv[1];
        for (const auto& ext: std::set<std::string>{".model", ".data"})
            if (!file_exists(filename + ext)) {
                executed_correctly = false;
                whats_wrong << "No such file: " << filename + ext;
                break;
            }
    }

    if (!executed_correctly) {
        std::cout << "Error: " << whats_wrong.str() << std::endl;
        std::cout << "Execute with one argument - modelfname, e.g.:" << std::endl;
        std::cout << argv[0] << " modelfname" << std::endl;
        exit(0);
    }

    std::cout << "Loading model..." << std::endl;
    dlib::kcentroid<realty_kernel_t> model_k_centroids;
    dlib::kkmeans<realty_kernel_t> model(model_k_centroids);
    dlib::deserialize(filename + ".model") >> model;

    std::cout << "Loading data..." << std::endl;
    std::vector<realty_entry_full_t> realty_entries_full;
    dlib::deserialize(filename + ".data") >> realty_entries_full;
    std::cout << "Done loading model and data" << std::endl;

    std::unordered_map<int, std::vector<realty_entry_full_t>> class2realty_entries;
    for (const auto& realty_entry_full: realty_entries_full)
        class2realty_entries[model(convert_realty_entry_full(realty_entry_full))].emplace_back(realty_entry_full);

    realty_entry_t current_entry;
    std::string buf;
    while (std::cin >> buf) {
        parse_entry_from_string(buf, current_entry);
        auto& suitable_entries = class2realty_entries[model(current_entry)];
        std::cout << "Total entries in same cluster: " << suitable_entries.size() << std::endl;
        std::sort(suitable_entries.begin(), suitable_entries.end(), [current_entry](auto lhs, auto rhs) {
            return calculate_haversine_distance(lhs, current_entry) < calculate_haversine_distance(rhs, current_entry);
        });
        for (const auto& suitable_entry:suitable_entries)
            std::cout << suitable_entry << "\n";
    }
}


// Contains get_min_count, the core optimization of the spreadsort algorithm.
// Also has other helper functions commonly useful across variants.

//          Copyright Steven J. Ross 2001 - 2014.
// Distributed under the Boost Software License, Version 1.0.
//    (See accompanying file LICENSE_1_0.txt or copy at
//          http://www.boost.org/LICENSE_1_0.txt)

// See http://www.boost.org/libs/sort for library home page.

/*
Some improvements suggested by:
Phil Endecott and Frank Gennari
*/

#ifndef BOOST_SORT_SPREADSORT_DETAIL_SPREAD_SORT_COMMON_HPP
#define BOOST_SORT_SPREADSORT_DETAIL_SPREAD_SORT_COMMON_HPP
#include <algorithm>
#include <boost/cstdint.hpp>
#include <boost/serialization/static_warning.hpp>
#include <boost/sort/pdqsort/pdqsort.hpp>
#include <boost/sort/spreadsort/detail/constants.hpp>
#include <boost/static_assert.hpp>
#include <cstring>
#include <functional>
#include <limits>
#include <vector>

namespace boost {
namespace sort {
namespace spreadsort {
namespace detail {
// This only works on unsigned data types
template <typename T> inline unsigned rough_log_2_size(const T &input) {
  unsigned result = 0;
  // The && is necessary on some compilers to avoid infinite loops
  // it doesn't significantly impair performance
  while ((result < (8 * sizeof(T))) && (input >> result))
    ++result;
  return result;
}

// Gets the minimum size to call spreadsort on to control worst-case runtime.
// This is called for a set of bins, instead of bin-by-bin, to minimize
// runtime overhead.
// This could be replaced by a lookup table of sizeof(Div_type)*8 but this
// function is more general.
template <unsigned log_mean_bin_size, unsigned log_min_split_count,
          unsigned log_finishing_count>
inline size_t get_min_count(unsigned log_range) {
  const size_t typed_one = 1;
  const unsigned min_size = log_mean_bin_size + log_min_split_count;
  // Assuring that constants have valid settings
  BOOST_STATIC_ASSERT(log_min_split_count <= max_splits &&
                      log_min_split_count > 0);
  BOOST_STATIC_ASSERT(max_splits > 1 && max_splits < (8 * sizeof(unsigned)));
  BOOST_STATIC_ASSERT(max_finishing_splits >= max_splits &&
                      max_finishing_splits < (8 * sizeof(unsigned)));
  BOOST_STATIC_ASSERT(log_mean_bin_size >= 0);
  BOOST_STATIC_ASSERT(log_finishing_count >= 0);
  // if we can complete in one iteration, do so
  // This first check allows the compiler to optimize never-executed code out
  if (log_finishing_count < min_size) {
    if (log_range <= min_size && log_range <= max_splits) {
      // Return no smaller than a certain minimum limit
      if (log_range <= log_finishing_count)
        return typed_one << log_finishing_count;
      return typed_one << log_range;
    }
  }
  const unsigned base_iterations = max_splits - log_min_split_count;
  // sum of n to n + x = ((x + 1) * (n + (n + x)))/2 + log_mean_bin_size
  const unsigned base_range =
      ((base_iterations + 1) * (max_splits + log_min_split_count)) / 2 +
      log_mean_bin_size;
  // Calculating the required number of iterations, and returning
  // 1 << (iteration_count + min_size)
  if (log_range < base_range) {
    unsigned result = log_min_split_count;
    for (unsigned offset = min_size; offset < log_range; offset += ++result)
      ;
    // Preventing overflow; this situation shouldn't occur
    if ((result + log_mean_bin_size) >= (8 * sizeof(size_t)))
      return typed_one << ((8 * sizeof(size_t)) - 1);
    return typed_one << (result + log_mean_bin_size);
  }
  // A quick division can calculate the worst-case runtime for larger ranges
  unsigned remainder = log_range - base_range;
  // the max_splits - 1 is used to calculate the ceiling of the division
  unsigned bit_length = ((((max_splits - 1) + remainder) / max_splits) +
                         base_iterations + min_size);
  // Preventing overflow; this situation shouldn't occur
  if (bit_length >= (8 * sizeof(size_t)))
    return typed_one << ((8 * sizeof(size_t)) - 1);
  // n(log_range)/max_splits + C, optimizing worst-case performance
  return typed_one << bit_length;
}

// Resizes the bin cache and bin sizes, and initializes each bin size to 0.
// This generates the memory overhead to use in radix sorting.
template <class RandomAccessIter>
inline RandomAccessIter *
size_bins(size_t *bin_sizes, std::vector<RandomAccessIter> &bin_cache,
          unsigned cache_offset, unsigned &cache_end, unsigned bin_count) {
  // Clear the bin sizes
  for (size_t u = 0; u < bin_count; u++)
    bin_sizes[u] = 0;
  // Make sure there is space for the bins
  cache_end = cache_offset + bin_count;
  if (cache_end > bin_cache.size())
    bin_cache.resize(cache_end);
  return &(bin_cache[cache_offset]);
}
} // namespace detail
} // namespace spreadsort
} // namespace sort
} // namespace boost

#endif


// ---------------------------------------------------------------------
//
// Copyright (c) 2018 - 2019 by the IBAMR developers
// All rights reserved.
//
// This file is part of IBAMR.
//
// IBAMR is free software and is distributed under the 3-clause BSD
// license. The full text of the license can be found in the file
// COPYRIGHT at the top level directory of IBAMR.
//
// ---------------------------------------------------------------------

// Config files
#include <SAMRAI_config.h>

// Headers for basic PETSc functions
#include <petscsys.h>

// Headers for basic SAMRAI objects
#include <BergerRigoutsos.h>
#include <CartesianGridGeometry.h>
#include <LoadBalancer.h>
#include <StandardTagAndInitialize.h>

// Headers for basic libMesh objects
#include <libmesh/equation_systems.h>
#include <libmesh/exodusII_io.h>
#include <libmesh/mesh.h>
#include <libmesh/mesh_generation.h>
#include <libmesh/mesh_triangle_interface.h>

// Headers for application-specific algorithm/data structure objects
#include <ibamr/IBExplicitHierarchyIntegrator.h>
#include <ibamr/IBFEDirectForcingKinematics.h>
#include <ibamr/IBFEMethod.h>
#include <ibamr/INSStaggeredHierarchyIntegrator.h>

#include <ibtk/AppInitializer.h>
#include <ibtk/IBTKInit.h>
#include <ibtk/IBTK_MPI.h>
#include <ibtk/LEInteractor.h>
#include <ibtk/ibtk_utilities.h>
#include <ibtk/libmesh_utilities.h>
#include <ibtk/muParserCartGridFunction.h>
#include <ibtk/muParserRobinBcCoefs.h>

#include <boost/multi_array.hpp>

// Set up application namespace declarations
#include <ibamr/app_namespaces.h>

inline double
kernel(double x)
{
    x += 4.;
    const double x2 = x * x;
    const double x3 = x * x2;
    const double x4 = x * x3;
    const double x5 = x * x4;
    const double x6 = x * x5;
    const double x7 = x * x6;
    if (x <= 0.)
        return 0.;
    else if (x <= 1.)
        return .1984126984126984e-3 * x7;
    else if (x <= 2.)
        return .1111111111111111e-1 * x6 - .1388888888888889e-2 * x7 - .3333333333333333e-1 * x5 +
               .5555555555555556e-1 * x4 - .5555555555555556e-1 * x3 + .3333333333333333e-1 * x2 -
               .1111111111111111e-1 * x + .1587301587301587e-2;
    else if (x <= 3.)
        return .4333333333333333 * x5 - .6666666666666667e-1 * x6 + .4166666666666667e-2 * x7 - 1.500000000000000 * x4 +
               3.055555555555556 * x3 - 3.700000000000000 * x2 + 2.477777777777778 * x - .7095238095238095;
    else if (x <= 4.)
        return 9. * x4 - 1.666666666666667 * x5 + .1666666666666667 * x6 - .6944444444444444e-2 * x7 -
               28.44444444444444 * x3 + 53. * x2 - 54.22222222222222 * x + 23.59047619047619;
    else if (x <= 5.)
        return 96. * x3 - 22.11111111111111 * x4 + 3. * x5 - .2222222222222222 * x6 + .6944444444444444e-2 * x7 -
               245.6666666666667 * x2 + 344. * x - 203.9650793650794;
    else if (x <= 6.)
        return 483.5000000000000 * x2 - 147.0555555555556 * x3 + 26.50000000000000 * x4 - 2.833333333333333 * x5 +
               .1666666666666667 * x6 - .4166666666666667e-2 * x7 - 871.2777777777778 * x + 664.0904761904762;
    else if (x <= 7.)
        return 943.1222222222222 * x - 423.7000000000000 * x2 + 104.9444444444444 * x3 - 15.50000000000000 * x4 +
               1.366666666666667 * x5 - .6666666666666667e-1 * x6 + .1388888888888889e-2 * x7 - 891.1095238095238;
    else if (x <= 8.)
        return 416.1015873015873 - 364.0888888888889 * x + 136.5333333333333 * x2 - 28.44444444444444 * x3 +
               3.555555555555556 * x4 - .2666666666666667 * x5 + .1111111111111111e-1 * x6 - .1984126984126984e-3 * x7;
    else
        return 0.;
} // kernel

void
cylinder_kinematics(double /*data_time*/, Eigen::Vector3d& U_com, Eigen::Vector3d& W_com, void* /*ctx*/)
{
    U_com.setZero();
    W_com.setZero();

    return;
} // cylinder_kinematics

// Function prototypes
static ofstream drag_stream, lift_stream;
static double R;
void postprocess_data(Pointer<PatchHierarchy<NDIM> > patch_hierarchy,
                      Pointer<INSHierarchyIntegrator> navier_stokes_integrator,
                      Mesh& mesh,
                      EquationSystems* equation_systems,
                      const int iteration_num,
                      const double loop_time,
                      const string& data_dump_dirname);

/*******************************************************************************
 * For each run, the input filename and restart information (if needed) must   *
 * be given on the command line.  For non-restarted case, command line is:     *
 *                                                                             *
 *    executable <input file name>                                             *
 *                                                                             *
 * For restarted run, command line is:                                         *
 *                                                                             *
 *    executable <input file name> <restart directory> <restart number>        *
 *                                                                             *
 *******************************************************************************/

int
main(int argc, char* argv[])
{
    // Initialize IBAMR and libraries. Deinitialization is handled by this object as well.
    IBTKInit ibtk_init(argc, argv, MPI_COMM_WORLD);
    const LibMeshInit& init = ibtk_init.getLibMeshInit();

    { // cleanup dynamically allocated objects prior to shutdown

        // Parse command line options, set some standard options from the input
        // file, initialize the restart database (if this is a restarted run),
        // and enable file logging.
        Pointer<AppInitializer> app_initializer = new AppInitializer(argc, argv, "IB.log");
        Pointer<Database> input_db = app_initializer->getInputDatabase();

        // Setup user-defined kernel function.
        LEInteractor::s_kernel_fcn = &kernel;
        LEInteractor::s_kernel_fcn_stencil_size = 8;

        // Get various standard options set in the input file.
        const bool dump_viz_data = app_initializer->dumpVizData();
        const int viz_dump_interval = app_initializer->getVizDumpInterval();
        const bool uses_visit = dump_viz_data && app_initializer->getVisItDataWriter();
#ifdef LIBMESH_HAVE_EXODUS_API
        const bool uses_exodus = dump_viz_data && !app_initializer->getExodusIIFilename().empty();
#else
        const bool uses_exodus = false;
        if (!app_initializer->getExodusIIFilename().empty())
        {
            plog << "WARNING: libMesh was compiled without Exodus support, so no "
                 << "Exodus output will be written in this program.\n";
        }
#endif
        const string exodus_filename = app_initializer->getExodusIIFilename();

        const bool dump_restart_data = app_initializer->dumpRestartData();
        const int restart_dump_interval = app_initializer->getRestartDumpInterval();
        const string restart_dump_dirname = app_initializer->getRestartDumpDirectory();
        const string restart_read_dirname = app_initializer->getRestartReadDirectory();
        const int restart_restore_num = app_initializer->getRestartRestoreNumber();

        const bool dump_postproc_data = app_initializer->dumpPostProcessingData();
        const int postproc_data_dump_interval = app_initializer->getPostProcessingDataDumpInterval();
        const string postproc_data_dump_dirname = app_initializer->getPostProcessingDataDumpDirectory();
        if (dump_postproc_data && (postproc_data_dump_interval > 0) && !postproc_data_dump_dirname.empty())
        {
            Utilities::recursiveMkdir(postproc_data_dump_dirname);
        }

        const bool dump_timer_data = app_initializer->dumpTimerData();
        const int timer_dump_interval = app_initializer->getTimerDumpInterval();

        // Create a simple FE mesh.
        Mesh solid_mesh(init.comm(), NDIM);
        const double dx = input_db->getDouble("DX");
        const double ds = input_db->getDouble("MFAC") * dx;
        string elem_type = input_db->getString("ELEM_TYPE");
        R = input_db->getDouble("R");
        if (NDIM == 2 && (elem_type == "TRI3" || elem_type == "TRI6"))
        {
#ifdef LIBMESH_HAVE_TRIANGLE
            const int num_circum_nodes = ceil(2.0 * M_PI * R / ds);
            for (int k = 0; k < num_circum_nodes; ++k)
            {
                const double theta = 2.0 * M_PI * static_cast<double>(k) / static_cast<double>(num_circum_nodes);
                solid_mesh.add_point(libMesh::Point(R * cos(theta), R * sin(theta)));
            }
            TriangleInterface triangle(solid_mesh);
            triangle.triangulation_type() = TriangleInterface::GENERATE_CONVEX_HULL;
            triangle.elem_type() = Utility::string_to_enum<ElemType>(elem_type);
            triangle.desired_area() = 1.5 * sqrt(3.0) / 4.0 * ds * ds;
            triangle.insert_extra_points() = true;
            triangle.smooth_after_generating() = true;
            triangle.triangulate();
#else
            TBOX_ERROR("ERROR: libMesh appears to have been configured without support for Triangle,\n"
                       << "       but Triangle is required for TRI3 or TRI6 elements.\n");
#endif
        }
        else
        {
            // NOTE: number of segments along boundary is 4*2^r.
            const double num_circum_segments = 2.0 * M_PI * R / ds;
            const int r = log2(0.25 * num_circum_segments);
            MeshTools::Generation::build_sphere(solid_mesh, R, r, Utility::string_to_enum<ElemType>(elem_type));
        }

        // Ensure nodes on the surface are on the analytic boundary.
        MeshBase::element_iterator el_end = solid_mesh.elements_end();
        for (MeshBase::element_iterator el = solid_mesh.elements_begin(); el != el_end; ++el)
        {
            Elem* const elem = *el;
            for (unsigned int side = 0; side < elem->n_sides(); ++side)
            {
                const bool at_mesh_bdry = !elem->neighbor_ptr(side);
                if (!at_mesh_bdry) continue;
                for (unsigned int k = 0; k < elem->n_nodes(); ++k)
                {
                    if (!elem->is_node_on_side(k, side)) continue;
                    Node& n = elem->node_ref(k);
                    n = R * n.unit();
                }
            }
        }
        solid_mesh.prepare_for_use();
        Mesh& mesh = solid_mesh;

        // Create major algorithm and data objects that comprise the
        // application.  These objects are configured from the input database
        // and, if this is a restarted run, from the restart database.
        Pointer<INSHierarchyIntegrator> navier_stokes_integrator = new INSStaggeredHierarchyIntegrator(
            "INSStaggeredHierarchyIntegrator",
            app_initializer->getComponentDatabase("INSStaggeredHierarchyIntegrator"));

        Pointer<IBFEMethod> ib_method_ops =
            new IBFEMethod("IBFEMethod",
                           app_initializer->getComponentDatabase("IBFEMethod"),
                           &mesh,
                           app_initializer->getComponentDatabase("GriddingAlgorithm")->getInteger("max_levels"),
                           /*register_for_restart*/ true,
                           restart_read_dirname,
                           restart_restore_num);
        Pointer<IBHierarchyIntegrator> time_integrator =
            new IBExplicitHierarchyIntegrator("IBHierarchyIntegrator",
                                              app_initializer->getComponentDatabase("IBHierarchyIntegrator"),
                                              ib_method_ops,
                                              navier_stokes_integrator);
        Pointer<CartesianGridGeometry<NDIM> > grid_geometry = new CartesianGridGeometry<NDIM>(
            "CartesianGeometry", app_initializer->getComponentDatabase("CartesianGeometry"));
        Pointer<PatchHierarchy<NDIM> > patch_hierarchy = new PatchHierarchy<NDIM>("PatchHierarchy", grid_geometry);
        Pointer<StandardTagAndInitialize<NDIM> > error_detector =
            new StandardTagAndInitialize<NDIM>("StandardTagAndInitialize",
                                               time_integrator,
                                               app_initializer->getComponentDatabase("StandardTagAndInitialize"));
        Pointer<BergerRigoutsos<NDIM> > box_generator = new BergerRigoutsos<NDIM>();
        Pointer<LoadBalancer<NDIM> > load_balancer =
            new LoadBalancer<NDIM>("LoadBalancer", app_initializer->getComponentDatabase("LoadBalancer"));
        Pointer<GriddingAlgorithm<NDIM> > gridding_algorithm =
            new GriddingAlgorithm<NDIM>("GriddingAlgorithm",
                                        app_initializer->getComponentDatabase("GriddingAlgorithm"),
                                        error_detector,
                                        box_generator,
                                        load_balancer);

        // Create IBFE direct forcing kinematics object.
        Pointer<IBFEDirectForcingKinematics> df_kinematics_ops = new IBFEDirectForcingKinematics(
            "cylinder_dfk",
            app_initializer->getComponentDatabase("CylinderIBFEDirectForcingKinematics"),
            ib_method_ops,
            /*part*/ 0,
            /*register_for_restart*/ true);
        ib_method_ops->registerDirectForcingKinematics(df_kinematics_ops, /*part*/ 0);

        // Specify structure kinematics
        FreeRigidDOFVector solve_dofs;
        solve_dofs.setZero();
        df_kinematics_ops->setSolveRigidBodyVelocity(solve_dofs);
        df_kinematics_ops->registerKinematicsFunction(&cylinder_kinematics, NULL);

        // Configure the IBFE solver.
        ib_method_ops->initializeFEEquationSystems();
        EquationSystems* equation_systems = ib_method_ops->getFEDataManager()->getEquationSystems();

        // Create Eulerian initial condition specification objects.
        if (input_db->keyExists("VelocityInitialConditions"))
        {
            Pointer<CartGridFunction> u_init = new muParserCartGridFunction(
                "u_init", app_initializer->getComponentDatabase("VelocityInitialConditions"), grid_geometry);
            navier_stokes_integrator->registerVelocityInitialConditions(u_init);
        }

        if (input_db->keyExists("PressureInitialConditions"))
        {
            Pointer<CartGridFunction> p_init = new muParserCartGridFunction(
                "p_init", app_initializer->getComponentDatabase("PressureInitialConditions"), grid_geometry);
            navier_stokes_integrator->registerPressureInitialConditions(p_init);
        }

        // Create Eulerian boundary condition specification objects (when necessary).
        const IntVector<NDIM>& periodic_shift = grid_geometry->getPeriodicShift();
        vector<RobinBcCoefStrategy<NDIM>*> u_bc_coefs(NDIM);
        if (periodic_shift.min() > 0)
        {
            for (unsigned int d = 0; d < NDIM; ++d)
            {
                u_bc_coefs[d] = NULL;
            }
        }
        else
        {
            for (unsigned int d = 0; d < NDIM; ++d)
            {
                const std::string bc_coefs_name = "u_bc_coefs_" + std::to_string(d);

                const std::string bc_coefs_db_name = "VelocityBcCoefs_" + std::to_string(d);

                u_bc_coefs[d] = new muParserRobinBcCoefs(
                    bc_coefs_name, app_initializer->getComponentDatabase(bc_coefs_db_name), grid_geometry);
            }
            navier_stokes_integrator->registerPhysicalBoundaryConditions(u_bc_coefs);
        }

        // Create Eulerian body force function specification objects.
        if (input_db->keyExists("ForcingFunction"))
        {
            Pointer<CartGridFunction> f_fcn = new muParserCartGridFunction(
                "f_fcn", app_initializer->getComponentDatabase("ForcingFunction"), grid_geometry);
            time_integrator->registerBodyForceFunction(f_fcn);
        }

        // Set up visualization plot file writers.
        Pointer<VisItDataWriter<NDIM> > visit_data_writer = app_initializer->getVisItDataWriter();
        if (uses_visit)
        {
            time_integrator->registerVisItDataWriter(visit_data_writer);
        }
        std::unique_ptr<ExodusII_IO> exodus_io(uses_exodus ? new ExodusII_IO(mesh) : NULL);

        // Check to see if this is a restarted run to append current exodus files
        if (uses_exodus)
        {
            const bool from_restart = RestartManager::getManager()->isFromRestart();
            exodus_io->append(from_restart);
        }

        // Initialize hierarchy configuration and data on all patches.
        ib_method_ops->initializeFEData();
        time_integrator->initializePatchHierarchy(patch_hierarchy, gridding_algorithm);

        // Deallocate initialization objects.
        app_initializer.setNull();

        // Print the input database contents to the log file.
        plog << "Input database:\n";
        input_db->printClassData(plog);

        // Write out initial visualization data.
        int iteration_num = time_integrator->getIntegratorStep();
        double loop_time = time_integrator->getIntegratorTime();
        if (dump_viz_data)
        {
            pout << "\n\nWriting visualization files...\n\n";
            if (uses_visit)
            {
                time_integrator->setupPlotData();
                visit_data_writer->writePlotData(patch_hierarchy, iteration_num, loop_time);
            }
            if (uses_exodus)
            {
                exodus_io->write_timestep(
                    exodus_filename, *equation_systems, iteration_num / viz_dump_interval + 1, loop_time);
            }
        }

        // Open streams to save lift and drag coefficients.
        if (IBTK_MPI::getRank() == 0)
        {
            drag_stream.open("C_D.curve", ios_base::out | ios_base::trunc);
            lift_stream.open("C_L.curve", ios_base::out | ios_base::trunc);

            drag_stream.precision(10);
            lift_stream.precision(10);
        }

        // Main time step loop.
        double loop_time_end = time_integrator->getEndTime();
        double dt = 0.0;
        while (!MathUtilities<double>::equalEps(loop_time, loop_time_end) && time_integrator->stepsRemaining())
        {
            iteration_num = time_integrator->getIntegratorStep();
            loop_time = time_integrator->getIntegratorTime();

            pout << "\n";
            pout << "+++++++++++++++++++++++++++++++++++++++++++++++++++\n";
            pout << "At beginning of timestep # " << iteration_num << "\n";
            pout << "Simulation time is " << loop_time << "\n";

            dt = time_integrator->getMaximumTimeStepSize();
            time_integrator->advanceHierarchy(dt);
            loop_time += dt;

            pout << "\n";
            pout << "At end       of timestep # " << iteration_num << "\n";
            pout << "Simulation time is " << loop_time << "\n";
            pout << "+++++++++++++++++++++++++++++++++++++++++++++++++++\n";
            pout << "\n";

            // At specified intervals, write visualization and restart files,
            // print out timer data, and store hierarchy data for post
            // processing.
            iteration_num += 1;
            const bool last_step = !time_integrator->stepsRemaining();
            if (dump_viz_data && (iteration_num % viz_dump_interval == 0 || last_step))
            {
                pout << "\nWriting visualization files...\n\n";
                if (uses_visit)
                {
                    time_integrator->setupPlotData();
                    visit_data_writer->writePlotData(patch_hierarchy, iteration_num, loop_time);
                }
                if (uses_exodus)
                {
                    exodus_io->write_timestep(
                        exodus_filename, *equation_systems, iteration_num / viz_dump_interval + 1, loop_time);
                }
            }
            if (dump_restart_data && (iteration_num % restart_dump_interval == 0 || last_step))
            {
                pout << "\nWriting restart files...\n\n";
                RestartManager::getManager()->writeRestartFile(restart_dump_dirname, iteration_num);
                ib_method_ops->writeFEDataToRestartFile(restart_dump_dirname, iteration_num);
            }
            if (dump_timer_data && (iteration_num % timer_dump_interval == 0 || last_step))
            {
                pout << "\nWriting timer data...\n\n";
                TimerManager::getManager()->print(plog);
            }
            if (dump_postproc_data && (iteration_num % postproc_data_dump_interval == 0 || last_step))
            {
                postprocess_data(patch_hierarchy,
                                 navier_stokes_integrator,
                                 mesh,
                                 equation_systems,
                                 iteration_num,
                                 loop_time,
                                 postproc_data_dump_dirname);
            }
        }

        // Close the logging streams.
        if (IBTK_MPI::getRank() == 0)
        {
            drag_stream.close();
            lift_stream.close();
        }

        // Cleanup Eulerian boundary condition specification objects (when
        // necessary).
        for (unsigned int d = 0; d < NDIM; ++d) delete u_bc_coefs[d];

    } // cleanup dynamically allocated objects prior to shutdown
} // main

void
postprocess_data(Pointer<PatchHierarchy<NDIM> > /*patch_hierarchy*/,
                 Pointer<INSHierarchyIntegrator> /*navier_stokes_integrator*/,
                 Mesh& mesh,
                 EquationSystems* equation_systems,
                 const int /*iteration_num*/,
                 const double loop_time,
                 const string& /*data_dump_dirname*/)
{
    const unsigned int dim = mesh.mesh_dimension();
    double F_integral[NDIM];
    for (unsigned int d = 0; d < NDIM; ++d) F_integral[d] = 0.0;

    System& F_system = equation_systems->get_system(IBFEMethod::FORCE_SYSTEM_NAME);
    NumericVector<double>* F_vec = F_system.solution.get();
    NumericVector<double>* F_ghost_vec = F_system.current_local_solution.get();
    copy_and_synch(*F_vec, *F_ghost_vec);
    const DofMap& dof_map = F_system.get_dof_map();
    FEType fe_type = dof_map.variable_type(0);
    std::unique_ptr<FEBase> fe(FEBase::build(dim, fe_type));
    std::unique_ptr<QBase> qrule = fe_type.default_quadrature_rule(dim);
    fe->attach_quadrature_rule(qrule.get());
    const vector<double>& JxW = fe->get_JxW();
    const vector<vector<double> >& phi = fe->get_phi();

    std::vector<std::vector<unsigned int> > dof_indices(NDIM);
    boost::multi_array<double, 2> F_node;
    VectorValue<double> F;

    const MeshBase::const_element_iterator el_begin = mesh.active_local_elements_begin();
    const MeshBase::const_element_iterator el_end = mesh.active_local_elements_end();
    for (MeshBase::const_element_iterator el_it = el_begin; el_it != el_end; ++el_it)
    {
        Elem* const elem = *el_it;
        fe->reinit(elem);
        for (unsigned int d = 0; d < NDIM; ++d)
        {
            dof_map.dof_indices(elem, dof_indices[d], d);
        }
        get_values_for_interpolation(F_node, *F_ghost_vec, dof_indices);

        const unsigned int n_qp = qrule->n_points();
        for (unsigned int qp = 0; qp < n_qp; ++qp)
        {
            interpolate(F, qp, F_node, phi);
            for (int d = 0; d < NDIM; ++d)
            {
                F_integral[d] += F(d) * JxW[qp];
            }
        }
    }
    IBTK_MPI::sumReduction(F_integral, NDIM);
    static const double rho = 1.0;
    static const double U_max = 1.0;
    static const double D = 2.0 * R;
    if (IBTK_MPI::getRank() == 0)
    {
        drag_stream << loop_time << " " << -F_integral[0] / (0.5 * rho * U_max * U_max * D) << endl;
        lift_stream << loop_time << " " << -F_integral[1] / (0.5 * rho * U_max * U_max * D) << endl;
    }
    return;
} // postprocess_data