#define BOOST_TEST_MODULE
#include <boost/test/unit_test.hpp>
#include <util/test_macros.hpp>
#include <iostream>

#include <unistd.h>
#include <timer/timer.hpp>
#include <unity/lib/unity_sarray.hpp>
#include <unity/lib/flex_dict_view.hpp>
#include <unity/toolkits/text/topic_model.hpp>
#include <unity/toolkits/text/cgs.hpp>
#include <unity/toolkits/text/alias.hpp>

using namespace turi;
using namespace text;

struct topic_model_test {
  unity_sarray* sa;
  std::vector<flexible_type> vocab;
  size_t ROW_COUNT;
  size_t ELEMENT_COUNT;

 public:
  topic_model_test() {
    global_logger().set_log_level(LOG_FATAL);
    ROW_COUNT = 10;
    ELEMENT_COUNT = 5;
    setUp();
  }

  void setUp() {
    std::vector<flexible_type> vocab = {"a", "b", "c", "d"};

    // Construct synthetic daata
    std::vector<flexible_type> v(ROW_COUNT);
    for(size_t i = 0; i < ROW_COUNT; ++i) {
      std::vector<std::pair<flexible_type, flexible_type>> elem;

      for(size_t j = 0; j < vocab.size(); ++j) {
        elem.push_back(std::make_pair(vocab[j], i * j + 1));
      }
      v[i] = flexible_type(elem);
    }

    sa = new unity_sarray();
    sa->construct_from_vector(v, flex_type_enum::DICT);

    size_t i = 0;  // i is row, j is element index in each row
    sa->begin_iterator();
    while(true) {
      auto vec = sa->iterator_get_next(1);
      if (vec.size() == 0) break;

      flex_dict_view fdv = vec[0];
      size_t j = 0;
      for(auto iter = fdv.begin(); iter != fdv.end(); iter++) {
        std::pair<flexible_type, flexible_type> val = *iter;
        /* TS_ASSERT_EQUALS(val.second, i * j); */
        /* std::cout << "doc " << i << " token " << j */
        /*           << " (word,count) " << val.first << " " << val.second */
        /*           << std::endl; */
        j++;
      }
      i++;
    }
  }

  void create_large_example(size_t num_documents=30000,
                            size_t max_word_frequency=200,
                            size_t doc_length=150,
                            size_t vocab_size=30000) {

    // Construct synthetic daata
    std::vector<flexible_type> v(num_documents);
    std::vector<std::pair<flexible_type, flexible_type>> elem(doc_length);
    for(size_t i = 0; i < num_documents; ++i) {
      for(size_t j = 0; j < doc_length; ++j) {
        flexible_type word_id = random::fast_uniform<size_t>(0, vocab_size);
        size_t freq = random::fast_uniform<size_t>(1, max_word_frequency);
        elem[j] = std::make_pair((flex_string) word_id, freq);
      }
      /* std::cout << flexible_type(elem) << std::endl; */
      v[i] = flexible_type(elem);
    }

    sa = new unity_sarray();
    sa->construct_from_vector(v, flex_type_enum::DICT);
  }

  void test_example_properly_formed() {
    TS_ASSERT_EQUALS(sa->dtype(), flex_type_enum::DICT);
    TS_ASSERT_EQUALS(sa->size(), ROW_COUNT);
  }

  void test_sparse_mat() {
    auto z = spmat(100);
    TS_ASSERT_EQUALS(z.num_rows(), 100);
    TS_ASSERT_EQUALS(z.get(0, 0), 0);
    TS_ASSERT_EQUALS(z.get_row(0).size(), 0);
    z.increment(5, 5, 20);
    TS_ASSERT_EQUALS(z.get(5, 5), 20);
    TS_ASSERT_EQUALS(z.get_row(5).size(), 1);
    z.increment(5, 5, -20);
    TS_ASSERT_EQUALS(z.get(5, 5), 0);
    z.trim(5);
    TS_ASSERT_EQUALS(z.get_row(5).size(), 0);
    z.increment(99, 0, 1);
    z.increment(99, 0, -1);
    z.increment(99, 2, 1);
    z.increment(99, 2, -1);
    z.trim(99);
    TS_ASSERT_EQUALS(z.get_row(99).size(), 0);

    arma::Mat<int> m;
    z.increment(2, 3, 1);
    m = z.as_matrix();
    TS_ASSERT_EQUALS(m.n_rows, 100);
    TS_ASSERT_EQUALS(m.n_cols, 4);
    TS_ASSERT_EQUALS(m(2, 3), 1);
    z.increment(80, 300, 1);
    m = z.as_matrix();
    TS_ASSERT_EQUALS(m.n_rows, 100);
    TS_ASSERT_EQUALS(m.n_cols, 301);
    TS_ASSERT_EQUALS(m(80, 300), 1);
  }

  void test_topic_model() {

     // Initialize topic model with the above SArray
    std::map<std::string, flexible_type> options;
    options["verbose"] = true;
    options["num_topics"] = 3;
    options["num_iterations"] = 500;
    options["print_interval"] = 1;
    options["num_burnin"] = 3;
    options["alpha"] = .1;
    options["beta"] = .01;
    auto dataset = sa->get_underlying_sarray();

    std::shared_ptr<cgs_topic_model> m;
    m.reset(new cgs_topic_model);
    m->init_options(options);
    m->train(dataset, true);

    // Test retrieval of most probable words per topic
    size_t num_words = 2;
    size_t topic_id = 0;
    auto top_words = m->get_topic(topic_id, num_words);
    TS_ASSERT_EQUALS(top_words.first.size(), num_words);
    TS_ASSERT_EQUALS(top_words.second.size(), num_words);
    for (auto w : top_words.first) {
      TS_ASSERT_EQUALS(w.get_type(), flex_type_enum::STRING);
    }

    // Test initialization from old topics
    m->set_topics(m->get_topics_matrix(), m->get_vocabulary(), 1000);
    m->train(dataset, true);
    TS_ASSERT(m->is_trained());

    // Make predictions
    size_t num_burnin = 10;
    auto pred = m->predict_gibbs(dataset, num_burnin);
    TS_ASSERT(m->is_trained());
    auto pred_counts = m->predict_counts(dataset, num_burnin);
    /* TS_ASSERT(pred_counts.n_rows == dataset->size()); */

    // Test validation set
    m.reset(new cgs_topic_model);
    m->init_options(options);
    m->init_validation(dataset, dataset);
    m->train(dataset, true);
    TS_ASSERT(m->is_trained());

  }

  void test_alias_solver() {
    // Initialize topic model with the above SArray
    std::map<std::string, flexible_type> options;
    options["verbose"] = true;
    options["num_topics"] = 3;
    options["num_iterations"] = 5;
    options["print_interval"] = 1;
    options["num_burnin"] = 3;
    options["alpha"] = .1;
    options["beta"] = .01;
    auto dataset = sa->get_underlying_sarray();

    // Test alias method solver
    std::shared_ptr<alias_topic_model> model;
    model.reset(new alias_topic_model);
    model->init_options(options);
    model->train(dataset, true);

  }

  void test_alias_solver_on_large() {

    global_logger().set_log_level(LOG_PROGRESS);

    timer ti;
    ti.start();
    random::seed(0);
    create_large_example(5, 10, 10);
    /* std::cout << "Example created in " << ti.current_time() << std::endl; */

    // Initialize topic model with the above SArray
    std::map<std::string, flexible_type> options;
    options["verbose"] = false;
    options["num_topics"] = 100;
    options["num_iterations"] = 1;
    options["print_interval"] = 1;
    options["alpha"] = .1;
    options["beta"] = .01;
    auto dataset = sa->get_underlying_sarray();

    // Test alias method solver
    std::shared_ptr<alias_topic_model> model;
    model.reset(new alias_topic_model);
    model->init_options(options);
    model->init_validation(dataset, dataset);
    model->train(dataset, true);

  }

};

BOOST_FIXTURE_TEST_SUITE(_topic_model_test, topic_model_test)
BOOST_AUTO_TEST_CASE(test_example_properly_formed) {
  topic_model_test::test_example_properly_formed();
}
BOOST_AUTO_TEST_CASE(test_sparse_mat) {
  topic_model_test::test_sparse_mat();
}
BOOST_AUTO_TEST_CASE(test_topic_model) {
  topic_model_test::test_topic_model();
}
BOOST_AUTO_TEST_CASE(test_alias_solver) {
  topic_model_test::test_alias_solver();
}
BOOST_AUTO_TEST_CASE(test_alias_solver_on_large) {
  topic_model_test::test_alias_solver_on_large();
}
BOOST_AUTO_TEST_SUITE_END()


/*    Copyright (c) 2010-2019, Delft University of Technology
 *    All rigths reserved
 *
 *    This file is part of the Tudat. Redistribution and use in source and
 *    binary forms, with or without modification, are permitted exclusively
 *    under the terms of the Modified BSD license. You should have received
 *    a copy of the license with this file. If not, please or visit:
 *    http://tudat.tudelft.nl/LICENSE.
 */

#include <boost/bind.hpp>

#include "Tudat/Astrodynamics/Gravitation/timeDependentSphericalHarmonicsGravityField.h"

namespace tudat
{

namespace gravitation
{

//! Function to (re)set the gravity field variations
void TimeDependentSphericalHarmonicsGravityField::setFieldVariationSettings(
        const std::shared_ptr< GravityFieldVariationsSet > gravityFieldVariationUpdateSettings,
        const bool updateCorrections )
{
    // Set new variation set.
    gravityFieldVariationsSet_ = gravityFieldVariationUpdateSettings;

    // Update correction functions if necessary.
    if( updateCorrections )
    {
        updateCorrectionFunctions( );
    }
}

//! Function to clear all gravity field variations
void TimeDependentSphericalHarmonicsGravityField::clearVariations( )
{
    gravityFieldVariationsSet_ = std::shared_ptr< GravityFieldVariationsSet >( );
    correctionFunctions_.clear( );
}

//! Update gravity field to current time.
void TimeDependentSphericalHarmonicsGravityField::update( const double time )
{
    // Initialize current coefficients to nominal values.
    sineCoefficients_ = nominalSineCoefficients_;
    cosineCoefficients_ = nominalCosineCoefficients_;

    // Iterate over all corrections.
    for( unsigned int i = 0; i < correctionFunctions_.size( ); i++ )
    {
        // Add correction of this iteration to current coefficients.
        correctionFunctions_[ i ]( time, sineCoefficients_, cosineCoefficients_ );
    }
}

} // namespace gravitation

} // namespace tudat


// Distributed under the MIT License.
// See LICENSE.txt for details.

#include "Framework/TestingFramework.hpp"

#include <algorithm>
#include <boost/iterator/transform_iterator.hpp>
#include <cstddef>
#include <cstdint>
#include <memory>
#include <string>
#include <vector>

#include "DataStructures/DataVector.hpp"
#include "DataStructures/Tensor/TensorData.hpp"
#include "IO/H5/AccessType.hpp"
#include "IO/H5/File.hpp"
#include "IO/H5/VolumeData.hpp"
#include "NumericalAlgorithms/Spectral/Spectral.hpp"
#include "NumericalAlgorithms/SphericalHarmonics/Strahlkorper.hpp"
#include "NumericalAlgorithms/SphericalHarmonics/YlmSpherepack.hpp"
#include "Utilities/Algorithm.hpp"
#include "Utilities/ErrorHandling/Error.hpp"
#include "Utilities/FileSystem.hpp"
#include "Utilities/MakeString.hpp"
#include "Utilities/Numeric.hpp"

namespace {
template <typename T>
T multiply(const double obs_value, const T& component) {
  T result = component;
  for (auto& t : result) {
    t *= obs_value;
  }
  return result;
}

// Check that the volume data is correct
template <typename DataType>
void check_volume_data(
    const std::string& h5_file_name, const uint32_t version_number,
    const size_t observation_id, const double observation_value,
    const std::vector<DataType>& tensor_components_and_coords,
    const std::vector<std::string>& grid_names,
    const std::vector<std::vector<Spectral::Basis>>& bases,
    const std::vector<std::vector<Spectral::Quadrature>>& quadratures,
    const std::vector<std::vector<size_t>>& extents,
    const std::vector<std::string>& expected_components,
    const std::vector<std::vector<size_t>>& grid_data_orders) {
  h5::H5File<h5::AccessType::ReadOnly> file_read{h5_file_name};
  const auto& volume_file =
      file_read.get<h5::VolumeData>("/element_data", version_number);

  CHECK(extents == volume_file.get_extents(observation_id));
  CHECK(volume_file.get_observation_value(observation_id) == observation_value);
  // Check that all of the grid names were written correctly by checking their
  // equality of elements
  const std::vector<std::string> read_grid_names =
      volume_file.get_grid_names(observation_id);
  [&read_grid_names, &grid_names]() {
    auto sortable_grid_names = grid_names;
    auto sortable_read_grid_names = read_grid_names;
    std::sort(sortable_grid_names.begin(), sortable_grid_names.end(),
              std::less<>{});
    std::sort(sortable_read_grid_names.begin(), sortable_read_grid_names.end(),
              std::less<>{});
    REQUIRE(sortable_read_grid_names == sortable_grid_names);
  }();
  // Find the order the grids were written in
  std::vector<size_t> grid_positions(read_grid_names.size());
  for (size_t i = 0; i < grid_positions.size(); i++) {
    auto grid_name = grid_names[i];
    auto position =
        std::find(read_grid_names.begin(), read_grid_names.end(), grid_name);
    // We know the grid name is in the read_grid_names because of the previous
    // so we know `position` is an actual pointer to an element
    grid_positions[i] =
        static_cast<size_t>(std::distance(read_grid_names.begin(), position));
  }
  auto read_bases = volume_file.get_bases(observation_id);
  alg::sort(read_bases, std::less<>{});
  auto read_quadratures = volume_file.get_quadratures(observation_id);
  alg::sort(read_quadratures, std::less<>{});
  // We need non-const bases and quadratures in order to sort them, and we
  // need them in their string form,
  const auto& stringify = [](const auto& bases_or_quadratures) {
    std::vector<std::vector<std::string>> local_target_data{};
    local_target_data.reserve(bases_or_quadratures.size() + 1);
    for (const auto& element_data : bases_or_quadratures) {
      std::vector<std::string> target_axis_data{};
      target_axis_data.reserve(element_data.size() + 1);
      for (const auto& axis_datum : element_data) {
        target_axis_data.emplace_back(MakeString{} << axis_datum);
      }
      local_target_data.push_back(target_axis_data);
    }
    return local_target_data;
  };
  auto target_bases = stringify(bases);
  alg::sort(target_bases, std::less<>{});
  auto target_quadratures = stringify(quadratures);
  alg::sort(target_quadratures, std::less<>{});
  CHECK(target_bases == read_bases);
  CHECK(target_quadratures == read_quadratures);

  const auto read_components =
      volume_file.list_tensor_components(observation_id);
  CHECK(alg::all_of(read_components,
                    [&expected_components](const std::string& id) {
                      return alg::found(expected_components, id);
                    }));
  // Helper Function to get number of points on a particular grid
  const auto accumulate_extents = [](const std::vector<size_t>& grid_extents) {
    return alg::accumulate(grid_extents, 1, std::multiplies<>{});
  };

  const auto read_extents = volume_file.get_extents(observation_id);
  std::vector<size_t> element_num_points(
      boost::make_transform_iterator(read_extents.begin(), accumulate_extents),
      boost::make_transform_iterator(read_extents.end(), accumulate_extents));
  const auto read_points_by_element = [&element_num_points]() {
    std::vector<size_t> read_points(element_num_points.size());
    read_points[0] = 0;
    for (size_t index = 1; index < element_num_points.size(); index++) {
      read_points[index] =
          read_points[index - 1] + element_num_points[index - 1];
    }
    return read_points;
  }();
  // Given a DataType, corresponding to contiguous data read out of a
  // file, find the data which was written by the grid whose extents are
  // found at position `grid_index` in the vector of extents.
  const auto get_grid_data = [&element_num_points, &read_points_by_element](
                                 const DataVector& all_data,
                                 const size_t grid_index) {
    DataType result(element_num_points[grid_index]);
    // clang-tidy: do not use pointer arithmetic
    std::copy(&all_data[read_points_by_element[grid_index]],
              &all_data[read_points_by_element[grid_index]] +  // NOLINT
                  element_num_points[grid_index],
              result.begin());
    return result;
  };
  // The tensor components can be written in any order to the file, we loop
  // over the expected components rather than the read components because they
  // are in a particular order.
  for (size_t i = 0; i < expected_components.size(); i++) {
    const auto& component = expected_components[i];
    // for each grid
    for (size_t j = 0; j < grid_names.size(); j++) {
      CHECK(get_grid_data(
                volume_file.get_tensor_component(observation_id, component),
                grid_positions[j]) ==
            multiply(observation_value,
                     tensor_components_and_coords[grid_data_orders[j][i]]));
    }
  }
}

void test_strahlkorper() {
  constexpr size_t l_max = 12;
  constexpr size_t m_max = 12;
  constexpr double sphere_radius = 4.0;
  constexpr std::array<double, 3> center{{5.0, 6.0, 7.0}};
  const Strahlkorper<Frame::Inertial> strahlkorper{l_max, m_max, sphere_radius,
                                                   center};
  const YlmSpherepack& ylm = strahlkorper.ylm_spherepack();
  const std::array<DataVector, 2> theta_phi = ylm.theta_phi_points();
  const DataVector theta = theta_phi[0];
  const DataVector phi = theta_phi[1];
  const DataVector sin_theta = sin(theta);
  const DataVector radius = ylm.spec_to_phys(strahlkorper.coefficients());
  const std::string grid_name{"AhA"};
  const std::vector<DataVector> tensor_and_coord_data{
      radius * sin_theta * cos(phi), radius * sin_theta * sin(phi),
      radius * cos(theta), cos(2.0 * theta)};
  const std::vector<TensorComponent> tensor_components{
      {grid_name + "/InertialCoordinates_x", tensor_and_coord_data[0]},
      {grid_name + "/InertialCoordinates_y", tensor_and_coord_data[1]},
      {grid_name + "/InertialCoordinates_z", tensor_and_coord_data[2]},
      {grid_name + "/TestScalar", tensor_and_coord_data[3]}};

  const std::vector<size_t> observation_ids{4444};
  const std::vector<double> observation_values{1.0};
  const std::vector<Spectral::Basis> bases{2,
                                           Spectral::Basis::SphericalHarmonic};
  const std::vector<Spectral::Quadrature> quadratures{
      {Spectral::Quadrature::Gauss, Spectral::Quadrature::Equiangular}};

  const std::string h5_file_name{"Unit.IO.H5.VolumeData.Strahlkorper.h5"};
  const uint32_t version_number = 4;
  if (file_system::check_if_file_exists(h5_file_name)) {
    file_system::rm(h5_file_name, true);
  }

  const std::vector<size_t> extents{
      {ylm.physical_extents()[0], ylm.physical_extents()[1]}};

  {
    h5::H5File<h5::AccessType::ReadWrite> strahlkorper_file{h5_file_name};
    auto& volume_file = strahlkorper_file.insert<h5::VolumeData>(
        "/element_data", version_number);
    volume_file.write_volume_data(
        observation_ids[0], observation_values[0],
        std::vector<ElementVolumeData>{
            {extents, tensor_components, bases, quadratures}});
    strahlkorper_file.close_current_object();

    // Open the read volume file and check that the observation id and values
    // are correct.
    const auto& volume_file_read =
        strahlkorper_file.get<h5::VolumeData>("/element_data", version_number);
    const auto read_observation_ids = volume_file_read.list_observation_ids();
    CHECK(read_observation_ids == std::vector<size_t>{4444});
    CHECK(volume_file_read.get_observation_value(observation_ids[0]) ==
          observation_values[0]);
  }

  check_volume_data(h5_file_name, version_number, observation_ids[0],
                    observation_values[0], tensor_and_coord_data, {{grid_name}},
                    {bases}, {quadratures}, {extents},
                    {"InertialCoordinates_x", "InertialCoordinates_y",
                     "InertialCoordinates_z", "TestScalar"},
                    {{0, 1, 2, 3}});

  if (file_system::check_if_file_exists(h5_file_name)) {
    file_system::rm(h5_file_name, true);
  }
}

template <typename DataType>
void test() {
  const std::string h5_file_name("Unit.IO.H5.VolumeData.h5");
  const uint32_t version_number = 4;
  if (file_system::check_if_file_exists(h5_file_name)) {
    file_system::rm(h5_file_name, true);
  }

  h5::H5File<h5::AccessType::ReadWrite> my_file(h5_file_name);
  const std::vector<DataType> tensor_components_and_coords{
      {8.9, 7.6, 3.9, 2.1, 18.9, 17.6, 13.9, 12.1},
      {0.0, 1.0, 0.0, 1.0, 0.0, 1.0, 0.0, 1.0},
      {0.0, 0.0, 1.0, 1.0, 0.0, 0.0, 1.0, 1.0},
      {0.0, 0.0, 0.0, 0.0, 1.0, 1.0, 1.0, 1.0},
      {-78.9, -7.6, -1.9, 8.1, 6.3, 8.7, 9.8, 0.2},
      {-7.9, 7.6, 1.9, -8.1, -6.3, 2.7, 6.8, -0.2},
      {17.9, 27.6, 21.9, -28.1, -26.3, 32.7, 26.8, -30.2}};
  const std::vector<size_t> observation_ids{8435087234, size_t(-1)};
  const std::vector<double> observation_values{8.0, 2.3};
  const std::vector<std::string> grid_names{"[[2,3,4]]", "[[5,6,7]]"};
  const std::vector<std::vector<Spectral::Basis>> bases{
      {3, Spectral::Basis::Chebyshev}, {3, Spectral::Basis::Legendre}};
  const std::vector<std::vector<Spectral::Quadrature>> quadratures{
      {3, Spectral::Quadrature::Gauss},
      {3, Spectral::Quadrature::GaussLobatto}};
  {
    auto& volume_file =
        my_file.insert<h5::VolumeData>("/element_data", version_number);
    const auto write_to_file = [&volume_file, &tensor_components_and_coords,
                                &grid_names, &bases,
                                &quadratures](const size_t observation_id,
                                              const double observation_value) {
      std::string first_grid = grid_names.front();
      std::string last_grid = grid_names.back();
      volume_file.write_volume_data(
          observation_id, observation_value,
          std::vector<ElementVolumeData>{
              {{2, 2, 2},
               {TensorComponent{first_grid + "/S",
                                multiply(observation_value,
                                         tensor_components_and_coords[0])},
                TensorComponent{first_grid + "/x-coord",
                                multiply(observation_value,
                                         tensor_components_and_coords[1])},
                TensorComponent{first_grid + "/y-coord",
                                multiply(observation_value,
                                         tensor_components_and_coords[2])},
                TensorComponent{first_grid + "/z-coord",
                                multiply(observation_value,
                                         tensor_components_and_coords[3])},
                TensorComponent{first_grid + "/T_x",
                                multiply(observation_value,
                                         tensor_components_and_coords[4])},
                TensorComponent{first_grid + "/T_y",
                                multiply(observation_value,
                                         tensor_components_and_coords[5])},
                TensorComponent{first_grid + "/T_z",
                                multiply(observation_value,
                                         tensor_components_and_coords[6])}},
               bases.front(),
               quadratures.front()},
              // Second Element Data
              {{2, 2, 2},
               {TensorComponent{last_grid + "/S",
                                multiply(observation_value,
                                         tensor_components_and_coords[1])},
                TensorComponent{last_grid + "/x-coord",
                                multiply(observation_value,
                                         tensor_components_and_coords[0])},
                TensorComponent{last_grid + "/y-coord",
                                multiply(observation_value,
                                         tensor_components_and_coords[5])},
                TensorComponent{last_grid + "/z-coord",
                                multiply(observation_value,
                                         tensor_components_and_coords[3])},
                TensorComponent{last_grid + "/T_x",
                                multiply(observation_value,
                                         tensor_components_and_coords[6])},
                TensorComponent{last_grid + "/T_y",
                                multiply(observation_value,
                                         tensor_components_and_coords[4])},
                TensorComponent{last_grid + "/T_z",
                                multiply(observation_value,
                                         tensor_components_and_coords[2])}},
               bases.back(),
               quadratures.back()}});
    };
    for (size_t i = 0; i < observation_ids.size(); ++i) {
      write_to_file(observation_ids[i], observation_values[i]);
    }
  }
  // Open the read volume file and check that the observation id and values are
  // correct.
  const auto& volume_file =
      my_file.get<h5::VolumeData>("/element_data", version_number);
  const auto read_observation_ids = volume_file.list_observation_ids();
  // The observation IDs should be sorted by their observation value
  CHECK(read_observation_ids == std::vector<size_t>{size_t(-1), 8435087234});
  {
    INFO("Test find_observation_id");
    std::vector<size_t> found_observation_ids(observation_values.size());
    std::transform(observation_values.begin(), observation_values.end(),
                   found_observation_ids.begin(),
                   [&volume_file](const double observation_value) {
                     return volume_file.find_observation_id(observation_value);
                   });
    CHECK(found_observation_ids == observation_ids);
  }

  for (size_t i = 0; i < observation_ids.size(); ++i) {
    check_volume_data(
        h5_file_name, version_number, observation_ids[i], observation_values[i],
        tensor_components_and_coords, grid_names, bases, quadratures,
        {{2, 2, 2}, {2, 2, 2}},
        {"S", "x-coord", "y-coord", "z-coord", "T_x", "T_y", "T_z"},
        {{0, 1, 2, 3, 4, 5, 6}, {1, 0, 5, 3, 6, 4, 2}});
  }

  {
    INFO("offset_and_length_for_grid");
    const size_t observation_id = observation_ids.front();
    // [find_offset]
    const auto all_grid_names = volume_file.get_grid_names(observation_id);
    const auto all_extents = volume_file.get_extents(observation_id);
    const auto first_grid_offset_and_length = h5::offset_and_length_for_grid(
        grid_names.front(), all_grid_names, all_extents);
    // [find_offset]
    CHECK(first_grid_offset_and_length.first == 0);
    CHECK(first_grid_offset_and_length.second == 8);
    const auto last_grid_offset_and_length = h5::offset_and_length_for_grid(
        grid_names.back(), all_grid_names, all_extents);
    CHECK(last_grid_offset_and_length.first == 8);
    CHECK(last_grid_offset_and_length.second == 8);
  }

  if (file_system::check_if_file_exists(h5_file_name)) {
    file_system::rm(h5_file_name, true);
  }
}
}  // namespace

SPECTRE_TEST_CASE("Unit.IO.H5.VolumeData", "[Unit][IO][H5]") {
  test<DataVector>();
  test<std::vector<float>>();
  test_strahlkorper();

#ifdef SPECTRE_DEBUG
  CHECK_THROWS_WITH(
      []() {
        const std::string h5_file_name(
            "Unit.IO.H5.VolumeData.ComponentFormat.h5");
        const uint32_t version_number = 4;
        if (file_system::check_if_file_exists(h5_file_name)) {
          file_system::rm(h5_file_name, true);
        }
        h5::H5File<h5::AccessType::ReadWrite> my_file(h5_file_name);
        auto& volume_file =
            my_file.insert<h5::VolumeData>("/element_data", version_number);
        volume_file.write_volume_data(
            100, 10.0,
            {{{2},
              {TensorComponent{"S", DataVector{1.0, 2.0}}},
              {Spectral::Basis::Legendre},
              {Spectral::Quadrature::Gauss}}});
      }(),
      Catch::Contains(
          "The expected format of the tensor component names is "
          "'GROUP_NAME/COMPONENT_NAME' but could not find a '/' in"));
  CHECK_THROWS_WITH(
      []() {
        const std::string h5_file_name(
            "Unit.IO.H5.VolumeData.ComponentFormat1.h5");
        const uint32_t version_number = 4;
        if (file_system::check_if_file_exists(h5_file_name)) {
          file_system::rm(h5_file_name, true);
        }
        h5::H5File<h5::AccessType::ReadWrite> my_file(h5_file_name);
        auto& volume_file =
            my_file.insert<h5::VolumeData>("/element_data", version_number);
        volume_file.write_volume_data(
            100, 10.0,
            {{{2},
              {TensorComponent{"A/S", DataVector{1.0, 2.0}},
               TensorComponent{"S", DataVector{1.0, 2.0}}},
              {Spectral::Basis::Legendre},
              {Spectral::Quadrature::Gauss}}});
      }(),
      Catch::Contains(
          "The expected format of the tensor component names is "
          "'GROUP_NAME/COMPONENT_NAME' but could not find a '/' in"));
  CHECK_THROWS_WITH(
      []() {
        const std::string h5_file_name("Unit.IO.H5.VolumeData.WriteTwice.h5");
        const uint32_t version_number = 4;
        if (file_system::check_if_file_exists(h5_file_name)) {
          file_system::rm(h5_file_name, true);
        }
        h5::H5File<h5::AccessType::ReadWrite> my_file(h5_file_name);
        auto& volume_file =
            my_file.insert<h5::VolumeData>("/element_data", version_number);
        volume_file.write_volume_data(
            100, 10.0,
            {{{2},
              {TensorComponent{"A/S", DataVector{1.0, 2.0}},
               TensorComponent{"A/S", DataVector{1.0, 2.0}}},
              {Spectral::Basis::Legendre},
              {Spectral::Quadrature::Gauss}}});
      }(),
      Catch::Contains(
          "Trying to write tensor component 'S' which already exists in HDF5 "
          "file in group 'element_data.vol/ObservationId100'"));
#endif

  CHECK_THROWS_WITH(
      []() {
        const std::string h5_file_name(
            "Unit.IO.H5.VolumeData.FindNoObservationId.h5");
        const uint32_t version_number = 4;
        if (file_system::check_if_file_exists(h5_file_name)) {
          file_system::rm(h5_file_name, true);
        }
        h5::H5File<h5::AccessType::ReadWrite> h5_file(h5_file_name);
        auto& volume_file =
            h5_file.insert<h5::VolumeData>("/element_data", version_number);
        volume_file.write_volume_data(
            100, 10.0,
            {{{2},
              {TensorComponent{"A/S", DataVector{1.0, 2.0}}},
              {Spectral::Basis::Legendre},
              {Spectral::Quadrature::Gauss}}});
        volume_file.find_observation_id(11.0);
      }(),
      Catch::Contains("No observation with value"));
}


// All content Copyright (C) 2018 Genomics plc
#ifndef VARIANT_NORMALIZER_HPP
#define VARIANT_NORMALIZER_HPP

#include <boost/numeric/ublas/matrix.hpp>
#include <boost/numeric/ublas/matrix_proxy.hpp>
#include <boost/optional/optional.hpp>

#include "utils/referenceSequence.hpp"
#include "utils/NeedlemanWunsch.hpp"
#include "variant/type/variant.hpp"

namespace wecall
{
namespace variant
{
    class VariantNormalizer
    {
        using scoreMatrix_t = boost::numeric::ublas::matrix< int32_t >;

        using bitType_t = int64_t;
        using traceMatrix_t = boost::numeric::ublas::matrix< bitType_t >;

    public:
        VariantNormalizer( const utils::referenceSequencePtr_t & referenceSequence )
            : m_referenceSequence( referenceSequence )
        {
        }

        variantSet_t getNormalized( const caller::Region & region,
                                    const utils::BasePairSequence & sequence,
                                    const boost::optional< variantSet_t > unnormalizedVariants ) const;

    private:
        const utils::referenceSequencePtr_t m_referenceSequence;
        const uint64_t m_maxMatrixSize = 50000;
    };
}
}
#endif


#include <string>
#include <iostream>
#include <stdexcept>
#include <iomanip>

#include <boost/program_options.hpp>

#include <session.hh>

#include <scylla_blas/matrix.hh>
#include <scylla_blas/vector.hh>
#include <scylla_blas/config.hh>

#include "sparse_matrix_value_generator.hh"
#include "random_value_factory.hh"

#include "arnoldi.hh"

struct options {
    std::string host{};
    uint16_t port{};
    scylla_blas::index_t m;
    scylla_blas::index_t n;
    scylla_blas::index_t block_size;
    int64_t workers;
    int64_t scheduler_sleep_time;
    bool print_matrices = false;
};

void parse_arguments(int argc, char *argv[], options &options) {
    namespace po = boost::program_options;
    po::options_description desc(fmt::format("Usage: {} [options]", argv[0]));
    po::options_description opt("Options");
    opt.add_options()
            ("help", "Show program help")
            ("block_size,B", po::value<scylla_blas::index_t>(&options.block_size)->default_value(DEFAULT_BLOCK_SIZE), "Block size to use")
            ("workers,W", po::value<int64_t>(&options.workers)->default_value(DEFAULT_WORKER_COUNT), "How many workers can we use")
            ("sleep_time,S", po::value<int64_t>(&options.scheduler_sleep_time)->default_value(DEFAULT_SCHEDULER_SLEEP_TIME_MICROSECONDS), "How long should scheduler sleep waiting for task")
            ("print", "Print matrices")
            (",n", po::value<scylla_blas::index_t>(&options.n)->required(), "How many iterations of algorithm should be performed")
            (",m", po::value<scylla_blas::index_t>(&options.m)->required(), "Dimension of input matrix")
            ("host,H", po::value<std::string>(&options.host)->required(), "Address on which Scylla can be reached")
            ("port,P", po::value<uint16_t>(&options.port)->default_value(SCYLLA_DEFAULT_PORT), "port number on which Scylla can be reached");
    desc.add(opt);
    try {
        auto parsed = po::command_line_parser(argc, argv)
                .options(desc)
                .run();
        po::variables_map vm;
        po::store(parsed, vm);
        if (vm.count("help") || argc == 1) {
            std::cout << desc << "\n";
            std::exit(0);
        }

        if (vm.count("print")) {
            options.print_matrices = true;
        }

        po::notify(vm);
    } catch (std::exception &e) {
        LogCritical("error: {}", e.what());
        std::exit(1);
    } catch (...) {
        LogCritical("Exception of unknown type!");
        std::exit(1);
    }
}

template<class T>
void init_matrix(scylla_blas::routine_scheduler &scheduler,
                 std::shared_ptr<scylla_blas::matrix<T>>& matrix_ptr);

const id_t INITIAL_MATRIX_ID = 123456;

int main(int argc, char **argv) {
    struct options op;
    parse_arguments(argc, argv, op);

    if (op.n > op.m) {
        throw std::runtime_error("n cannot be greater than m");
    }

    auto session = std::make_shared<scmd::session>(op.host, std::to_string(op.port));

    arnoldi::containers c = arnoldi::containers<float>(session, INITIAL_MATRIX_ID, op.m, op.n, op.block_size);

    auto arnoldi_iteration = arnoldi(session, op.workers, op.scheduler_sleep_time);

    // Initialize matrix A with random values.
    std::shared_ptr<value_factory<float>> f =
            std::make_shared<random_value_factory<float>>(0, 9, 142);
    init_matrix<float>(arnoldi_iteration.get_scheduler(), c.A);

    // Set vector to (1, 0, 0 ... 0).
    c.b->update_value(1, 1.0f);

    if(op.print_matrices) c.A->print_octave(std::cout);
    arnoldi_iteration.compute(c.A, c.b, op.n, c.h, c.Q, c.v, c.q, c.t);
    if(op.print_matrices) c.Q->print_octave(std::cout);
    if(op.print_matrices) c.h->print_octave(std::cout);
}

template<class T>
void load_matrix_from_generator(const std::shared_ptr<scmd::session> &session, matrix_value_generator<T> &gen,
                                scylla_blas::matrix<T> &matrix) {
    scylla_blas::vector_segment<T> next_row;
    scylla_blas::matrix_value<T> prev_val (-1, -1, 0);

    while(gen.has_next()) {
        scylla_blas::matrix_value<T> next_val = gen.next();

        if (prev_val.row_index != -1 && next_val.row_index != prev_val.row_index) {
            matrix.insert_row(prev_val.row_index, next_row);
            next_row.clear();
        }

        next_row.emplace_back(next_val.col_index, next_val.value);
        prev_val = next_val;
    }

    if (prev_val.row_index != -1) {
        matrix.insert_row(prev_val.row_index, next_row);
    }

    LogInfo("Loaded a matrix {} from a generator", matrix.get_id());
}

template<class T>
void init_matrix(scylla_blas::routine_scheduler &scheduler,
                 std::shared_ptr<scylla_blas::matrix<T>> &matrix_ptr) {
    matrix_ptr->clear_all();
    double load = 0.2;
    scheduler.srmgen(load, *matrix_ptr);
}


// Copyright 2019 Google LLC & Bastiaan Konings
// 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.

// written by bastiaan konings schuiling 2008 - 2015
// this work is public domain. the code is undocumented, scruffy, untested, and should generally not be used for anything important.
// i do not offer support, so don't ask. to be used for inspiration :)

#include "main.hpp"

#include "utils.hpp"

#include "systems/graphics/resources/texture.hpp"
#include "managers/resourcemanagerpool.hpp"

#include <boost/algorithm/string.hpp>
#include <cmath>

float GetQuantizedDirectionBias() {
  return GetConfiguration()->GetReal("gameplay_quantizeddirectionbias", _default_QuantizedDirectionBias);
}

void QuantizeDirection(Vector3 &inputDirection, float bias) {

  // digitize input

  Vector3 inputDirectionNorm = inputDirection.GetNormalized(0);

  int directions = GetConfiguration()->GetInt("gameplay_quantizeddirectioncount", 8);

  radian angle = inputDirectionNorm.GetAngle2D();
  angle /= pi * 2.0f;
  angle = std::round(angle * directions);
  angle /= directions;
  angle *= pi * 2.0f;

  inputDirection = (inputDirectionNorm * (1.0 - bias) + (Vector3(1, 0, 0).GetRotated2D(angle) * bias)).GetNormalized(inputDirectionNorm) * inputDirection.GetLength();
}

Vector3 GetProjectedCoord(const Vector3 &pos3D, boost::intrusive_ptr<Camera> camera) {
  Matrix4 rotMat;
  rotMat.ConstructInverse(camera->GetDerivedPosition(), Vector3(1, 1, 1), camera->GetDerivedRotation());
  float fov = camera->GetFOV();

  // cotangent
  float f = 1.0 / tan((fov / 360.0 * pi * 2) / 2.0);

  Vector3 contextSize3D = GetGraphicsSystem()->GetContextSize();
  float aspect = contextSize3D.coords[0] / contextSize3D.coords[1];
  float aspect2D = GetMenuTask()->GetWindowManager()->GetAspectRatio();
  if (aspect2D < aspect) aspect = aspect2D;

  //printf("aspect: %f\n", aspect);
  float zNear = 40.0;
  float zFar = 270.0;

  Matrix4 perspMat;
  perspMat.elements[0] = f / aspect;
  perspMat.elements[5] = f;
  perspMat.elements[10] = (zFar + zNear) / (zNear - zFar);
  perspMat.elements[11] = (2 * zFar * zNear) / (zNear - zFar);
  perspMat.elements[14] = -1;

  Matrix4 resMat = perspMat * rotMat;

  float x, y, z, w;
  resMat.MultiplyVec4(pos3D.coords[0], pos3D.coords[1], pos3D.coords[2], 1, x, y, z, w);

  Vector3 result;
  result.coords[0] = x / w;
  result.coords[1] = y / w;

  //printf("%f %f %f\n", x / w, y / w, z / w);

  result.coords[1] = -result.coords[1];

  result.coords[1] *= aspect2D / aspect;

  result += 1.0;
  result *= 0.5;
  result *= 100;

  return result;
}

int GetVelocityID(e_Velocity velo, bool treatDribbleAsWalk) {
  int id = 0;
  switch (velo) {
    case e_Velocity_Idle:
      id = 0;
      break;
    case e_Velocity_Dribble:
      id = 1;
      break;
    case e_Velocity_Walk:
      id = 2;
      break;
    case e_Velocity_Sprint:
      id = 3;
      break;
    default:
      id = 0;
      break;
  }
  if (treatDribbleAsWalk && id > 1) id--;
  return id;
}

std::map < e_PositionName, std::vector<Stat> > defaultProfiles;

e_PositionName GetPositionName(const std::string &shortcut) {

  if (shortcut == "GK") return e_PositionName_GK;
  if (shortcut == "SW") return e_PositionName_SW;
  if (shortcut == "D") return e_PositionName_D;
  if (shortcut == "WB") return e_PositionName_WB;
  if (shortcut == "DM") return e_PositionName_DM;
  if (shortcut == "M") return e_PositionName_M;
  if (shortcut == "AM") return e_PositionName_AM;
  if (shortcut == "F") return e_PositionName_F;
  if (shortcut == "ST") return e_PositionName_ST;

  Log(e_FatalError, "utils.cpp", "GetPositionName", "Position '" + shortcut + "' not found!");
  return e_PositionName_M;
}

void GetWeightedPositions(const std::string &positionString, std::vector<WeightedPosition> &weightedPositions) {
  // split by comma
  std::vector<std::string> positions;
  tokenize(positionString, positions, ",");
  for (unsigned int i = 0; i < positions.size(); i++) {
    // trim leading/trailing space
    boost::algorithm::trim(positions.at(i));
    // remove L/C/R notice (after space)
    std::size_t firstspace = positions.at(i).find_first_of(" ");
    if (firstspace != std::string::npos) positions.at(i) = positions.at(i).substr(0, firstspace);

    if (positions.at(i).find_first_of("/") != std::string::npos) {
      // position mixture
      std::vector<std::string> subpositions;
      tokenize(positions.at(i), subpositions, "/");
      for (unsigned int j = 0; j < subpositions.size(); j++) {
        WeightedPosition weightedPosition;
        weightedPosition.positionName = GetPositionName(subpositions.at(j));
        weightedPosition.weight = 1.0 / subpositions.size();
        weightedPositions.push_back(weightedPosition);
      }
    } else {
      // just one position
      WeightedPosition weightedPosition;
      weightedPosition.positionName = GetPositionName(positions.at(i));
      weightedPosition.weight = 1.0;
      weightedPositions.push_back(weightedPosition);
    }
  }

  /*
  for (unsigned int i = 0; i < weightedPositions.size(); i++) {
    printf("[%i:%f]", weightedPositions.at(i).positionName, weightedPositions.at(i).weight);
  }
  printf("\n");
  */
}

void InitDefaultProfiles() {
  // std::map < e_PositionName, std::vector<Stat> > defaultProfiles;

  std::vector<Stat> statsGK;
  statsGK.push_back((Stat){"physical_balance", 0.6});
  statsGK.push_back((Stat){"physical_reaction", 0.8});
  statsGK.push_back((Stat){"physical_acceleration", 0.5});
  statsGK.push_back((Stat){"physical_velocity", 0.4});
  statsGK.push_back((Stat){"physical_stamina", 0.5});
  statsGK.push_back((Stat){"physical_agility", 0.6});
  statsGK.push_back((Stat){"physical_shotpower", 0.6});
  statsGK.push_back((Stat){"technical_standingtackle", 0.2});
  statsGK.push_back((Stat){"technical_slidingtackle", 0.2});
  statsGK.push_back((Stat){"technical_ballcontrol", 0.3});
  statsGK.push_back((Stat){"technical_dribble", 0.2});
  statsGK.push_back((Stat){"technical_shortpass", 0.6});
  statsGK.push_back((Stat){"technical_highpass", 0.7});
  statsGK.push_back((Stat){"technical_header", 0.2});
  statsGK.push_back((Stat){"technical_shot", 0.3});
  statsGK.push_back((Stat){"technical_volley", 0.2});
  statsGK.push_back((Stat){"mental_calmness", 0.5});
  statsGK.push_back((Stat){"mental_workrate", 0.5});
  statsGK.push_back((Stat){"mental_resilience", 0.5});
  statsGK.push_back((Stat){"mental_defensivepositioning", 0.9});
  statsGK.push_back((Stat){"mental_offensivepositioning", 0.1});
  statsGK.push_back((Stat){"mental_vision", 0.5});
  defaultProfiles.insert(std::pair< e_PositionName, std::vector<Stat> >(e_PositionName_GK, statsGK));

  std::vector<Stat> statsSW;
  statsSW.push_back((Stat){"physical_balance", 0.7});
  statsSW.push_back((Stat){"physical_reaction", 0.7});
  statsSW.push_back((Stat){"physical_acceleration", 0.5});
  statsSW.push_back((Stat){"physical_velocity", 0.5});
  statsSW.push_back((Stat){"physical_stamina", 0.5});
  statsSW.push_back((Stat){"physical_agility", 0.4});
  statsSW.push_back((Stat){"physical_shotpower", 0.7});
  statsSW.push_back((Stat){"technical_standingtackle", 0.8});
  statsSW.push_back((Stat){"technical_slidingtackle", 0.8});
  statsSW.push_back((Stat){"technical_ballcontrol", 0.3});
  statsSW.push_back((Stat){"technical_dribble", 0.2});
  statsSW.push_back((Stat){"technical_shortpass", 0.4});
  statsSW.push_back((Stat){"technical_highpass", 0.4});
  statsSW.push_back((Stat){"technical_header", 0.6});
  statsSW.push_back((Stat){"technical_shot", 0.2});
  statsSW.push_back((Stat){"technical_volley", 0.2});
  statsSW.push_back((Stat){"mental_calmness", 0.5});
  statsSW.push_back((Stat){"mental_workrate", 0.5});
  statsSW.push_back((Stat){"mental_resilience", 0.5});
  statsSW.push_back((Stat){"mental_defensivepositioning", 0.8});
  statsSW.push_back((Stat){"mental_offensivepositioning", 0.2});
  statsSW.push_back((Stat){"mental_vision", 0.4});
  defaultProfiles.insert(std::pair< e_PositionName, std::vector<Stat> >(e_PositionName_SW, statsSW));

  std::vector<Stat> statsD;
  statsD.push_back((Stat){"physical_balance", 0.8});
  statsD.push_back((Stat){"physical_reaction", 0.7});
  statsD.push_back((Stat){"physical_acceleration", 0.5});
  statsD.push_back((Stat){"physical_velocity", 0.5});
  statsD.push_back((Stat){"physical_stamina", 0.5});
  statsD.push_back((Stat){"physical_agility", 0.5});
  statsD.push_back((Stat){"physical_shotpower", 0.6});
  statsD.push_back((Stat){"technical_standingtackle", 0.8});
  statsD.push_back((Stat){"technical_slidingtackle", 0.8});
  statsD.push_back((Stat){"technical_ballcontrol", 0.5});
  statsD.push_back((Stat){"technical_dribble", 0.3});
  statsD.push_back((Stat){"technical_shortpass", 0.5});
  statsD.push_back((Stat){"technical_highpass", 0.5});
  statsD.push_back((Stat){"technical_header", 0.6});
  statsD.push_back((Stat){"technical_shot", 0.3});
  statsD.push_back((Stat){"technical_volley", 0.2});
  statsD.push_back((Stat){"mental_calmness", 0.5});
  statsD.push_back((Stat){"mental_workrate", 0.5});
  statsD.push_back((Stat){"mental_resilience", 0.5});
  statsD.push_back((Stat){"mental_defensivepositioning", 0.8});
  statsD.push_back((Stat){"mental_offensivepositioning", 0.3});
  statsD.push_back((Stat){"mental_vision", 0.6});
  defaultProfiles.insert(std::pair< e_PositionName, std::vector<Stat> >(e_PositionName_D, statsD));

  std::vector<Stat> statsWB;
  statsWB.push_back((Stat){"physical_balance", 0.6});
  statsWB.push_back((Stat){"physical_reaction", 0.5});
  statsWB.push_back((Stat){"physical_acceleration", 0.6});
  statsWB.push_back((Stat){"physical_velocity", 0.6});
  statsWB.push_back((Stat){"physical_stamina", 0.5});
  statsWB.push_back((Stat){"physical_agility", 0.5});
  statsWB.push_back((Stat){"physical_shotpower", 0.5});
  statsWB.push_back((Stat){"technical_standingtackle", 0.7});
  statsWB.push_back((Stat){"technical_slidingtackle", 0.7});
  statsWB.push_back((Stat){"technical_ballcontrol", 0.5});
  statsWB.push_back((Stat){"technical_dribble", 0.5});
  statsWB.push_back((Stat){"technical_shortpass", 0.7});
  statsWB.push_back((Stat){"technical_highpass", 0.7});
  statsWB.push_back((Stat){"technical_header", 0.5});
  statsWB.push_back((Stat){"technical_shot", 0.4});
  statsWB.push_back((Stat){"technical_volley", 0.3});
  statsWB.push_back((Stat){"mental_calmness", 0.5});
  statsWB.push_back((Stat){"mental_workrate", 0.5});
  statsWB.push_back((Stat){"mental_resilience", 0.5});
  statsWB.push_back((Stat){"mental_defensivepositioning", 0.6});
  statsWB.push_back((Stat){"mental_offensivepositioning", 0.4});
  statsWB.push_back((Stat){"mental_vision", 0.6});
  defaultProfiles.insert(std::pair< e_PositionName, std::vector<Stat> >(e_PositionName_WB, statsWB));

  std::vector<Stat> statsDM;
  statsDM.push_back((Stat){"physical_balance", 0.7});
  statsDM.push_back((Stat){"physical_reaction", 0.6});
  statsDM.push_back((Stat){"physical_acceleration", 0.5});
  statsDM.push_back((Stat){"physical_velocity", 0.5});
  statsDM.push_back((Stat){"physical_stamina", 0.5});
  statsDM.push_back((Stat){"physical_agility", 0.5});
  statsDM.push_back((Stat){"physical_shotpower", 0.6});
  statsDM.push_back((Stat){"technical_standingtackle", 0.7});
  statsDM.push_back((Stat){"technical_slidingtackle", 0.6});
  statsDM.push_back((Stat){"technical_ballcontrol", 0.5});
  statsDM.push_back((Stat){"technical_dribble", 0.4});
  statsDM.push_back((Stat){"technical_shortpass", 0.7});
  statsDM.push_back((Stat){"technical_highpass", 0.7});
  statsDM.push_back((Stat){"technical_header", 0.5});
  statsDM.push_back((Stat){"technical_shot", 0.4});
  statsDM.push_back((Stat){"technical_volley", 0.3});
  statsDM.push_back((Stat){"mental_calmness", 0.5});
  statsDM.push_back((Stat){"mental_workrate", 0.5});
  statsDM.push_back((Stat){"mental_resilience", 0.5});
  statsDM.push_back((Stat){"mental_defensivepositioning", 0.6});
  statsDM.push_back((Stat){"mental_offensivepositioning", 0.4});
  statsDM.push_back((Stat){"mental_vision", 0.6});
  defaultProfiles.insert(std::pair< e_PositionName, std::vector<Stat> >(e_PositionName_DM, statsDM));

  std::vector<Stat> statsM;
  statsM.push_back((Stat){"physical_balance", 0.4});
  statsM.push_back((Stat){"physical_reaction", 0.5});
  statsM.push_back((Stat){"physical_acceleration", 0.6});
  statsM.push_back((Stat){"physical_velocity", 0.6});
  statsM.push_back((Stat){"physical_stamina", 0.5});
  statsM.push_back((Stat){"physical_agility", 0.6});
  statsM.push_back((Stat){"physical_shotpower", 0.6});
  statsM.push_back((Stat){"technical_standingtackle", 0.4});
  statsM.push_back((Stat){"technical_slidingtackle", 0.3});
  statsM.push_back((Stat){"technical_ballcontrol", 0.7});
  statsM.push_back((Stat){"technical_dribble", 0.6});
  statsM.push_back((Stat){"technical_shortpass", 0.8});
  statsM.push_back((Stat){"technical_highpass", 0.7});
  statsM.push_back((Stat){"technical_header", 0.4});
  statsM.push_back((Stat){"technical_shot", 0.5});
  statsM.push_back((Stat){"technical_volley", 0.4});
  statsM.push_back((Stat){"mental_calmness", 0.5});
  statsM.push_back((Stat){"mental_workrate", 0.5});
  statsM.push_back((Stat){"mental_resilience", 0.5});
  statsM.push_back((Stat){"mental_defensivepositioning", 0.5});
  statsM.push_back((Stat){"mental_offensivepositioning", 0.5});
  statsM.push_back((Stat){"mental_vision", 0.7});
  defaultProfiles.insert(std::pair< e_PositionName, std::vector<Stat> >(e_PositionName_M, statsM));

  std::vector<Stat> statsAM;
  statsAM.push_back((Stat){"physical_balance", 0.4});
  statsAM.push_back((Stat){"physical_reaction", 0.5});
  statsAM.push_back((Stat){"physical_acceleration", 0.6});
  statsAM.push_back((Stat){"physical_velocity", 0.6});
  statsAM.push_back((Stat){"physical_stamina", 0.5});
  statsAM.push_back((Stat){"physical_agility", 0.7});
  statsAM.push_back((Stat){"physical_shotpower", 0.7});
  statsAM.push_back((Stat){"technical_standingtackle", 0.2});
  statsAM.push_back((Stat){"technical_slidingtackle", 0.2});
  statsAM.push_back((Stat){"technical_ballcontrol", 0.6});
  statsAM.push_back((Stat){"technical_dribble", 0.7});
  statsAM.push_back((Stat){"technical_shortpass", 0.6});
  statsAM.push_back((Stat){"technical_highpass", 0.5});
  statsAM.push_back((Stat){"technical_header", 0.4});
  statsAM.push_back((Stat){"technical_shot", 0.7});
  statsAM.push_back((Stat){"technical_volley", 0.5});
  statsAM.push_back((Stat){"mental_calmness", 0.5});
  statsAM.push_back((Stat){"mental_workrate", 0.5});
  statsAM.push_back((Stat){"mental_resilience", 0.5});
  statsAM.push_back((Stat){"mental_defensivepositioning", 0.3});
  statsAM.push_back((Stat){"mental_offensivepositioning", 0.7});
  statsAM.push_back((Stat){"mental_vision", 0.4});
  defaultProfiles.insert(std::pair< e_PositionName, std::vector<Stat> >(e_PositionName_AM, statsAM));

  std::vector<Stat> statsF;
  statsF.push_back((Stat){"physical_balance", 0.4});
  statsF.push_back((Stat){"physical_reaction", 0.5});
  statsF.push_back((Stat){"physical_acceleration", 0.7});
  statsF.push_back((Stat){"physical_velocity", 0.7});
  statsF.push_back((Stat){"physical_stamina", 0.5});
  statsF.push_back((Stat){"physical_agility", 0.7});
  statsF.push_back((Stat){"physical_shotpower", 0.7});
  statsF.push_back((Stat){"technical_standingtackle", 0.2});
  statsF.push_back((Stat){"technical_slidingtackle", 0.1});
  statsF.push_back((Stat){"technical_ballcontrol", 0.5});
  statsF.push_back((Stat){"technical_dribble", 0.8});
  statsF.push_back((Stat){"technical_shortpass", 0.6});
  statsF.push_back((Stat){"technical_highpass", 0.6});
  statsF.push_back((Stat){"technical_header", 0.4});
  statsF.push_back((Stat){"technical_shot", 0.7});
  statsF.push_back((Stat){"technical_volley", 0.4});
  statsF.push_back((Stat){"mental_calmness", 0.5});
  statsF.push_back((Stat){"mental_workrate", 0.5});
  statsF.push_back((Stat){"mental_resilience", 0.5});
  statsF.push_back((Stat){"mental_defensivepositioning", 0.25});
  statsF.push_back((Stat){"mental_offensivepositioning", 0.75});
  statsF.push_back((Stat){"mental_vision", 0.5});
  defaultProfiles.insert(std::pair< e_PositionName, std::vector<Stat> >(e_PositionName_F, statsF));

  std::vector<Stat> statsST;
  statsST.push_back((Stat){"physical_balance", 0.3});
  statsST.push_back((Stat){"physical_reaction", 0.6});
  statsST.push_back((Stat){"physical_acceleration", 0.6});
  statsST.push_back((Stat){"physical_velocity", 0.6});
  statsST.push_back((Stat){"physical_stamina", 0.5});
  statsST.push_back((Stat){"physical_agility", 0.7});
  statsST.push_back((Stat){"physical_shotpower", 0.8});
  statsST.push_back((Stat){"technical_standingtackle", 0.1});
  statsST.push_back((Stat){"technical_slidingtackle", 0.1});
  statsST.push_back((Stat){"technical_ballcontrol", 0.5});
  statsST.push_back((Stat){"technical_dribble", 0.7});
  statsST.push_back((Stat){"technical_shortpass", 0.5});
  statsST.push_back((Stat){"technical_highpass", 0.4});
  statsST.push_back((Stat){"technical_header", 0.6});
  statsST.push_back((Stat){"technical_shot", 0.8});
  statsST.push_back((Stat){"technical_volley", 0.7});
  statsST.push_back((Stat){"mental_calmness", 0.5});
  statsST.push_back((Stat){"mental_workrate", 0.5});
  statsST.push_back((Stat){"mental_resilience", 0.5});
  statsST.push_back((Stat){"mental_defensivepositioning", 0.2});
  statsST.push_back((Stat){"mental_offensivepositioning", 0.8});
  statsST.push_back((Stat){"mental_vision", 0.3});
  defaultProfiles.insert(std::pair< e_PositionName, std::vector<Stat> >(e_PositionName_ST, statsST));

  // give all positions the same stats on average

  std::map < e_PositionName, std::vector<Stat> > ::iterator iter = defaultProfiles.begin();
  while (iter != defaultProfiles.end()) {
    std::vector<Stat> &stats = iter->second;
    float totalStats = 0.0f;
    int statsAmount = stats.size();
    for (unsigned int j = 0; j < stats.size(); j++) {
      totalStats += stats.at(j).value;
    }
    float averageStat = totalStats / (float)statsAmount;
    // average should be 0.5...
    for (unsigned int j = 0; j < stats.size(); j++) {
      stats.at(j).value += (0.5f - averageStat);
      assert(stats.at(j).value > 0.0f && stats.at(j).value < 1.0f);
      // center around 1.0.. (0.5 .. 1.5)
      //stats.at(j).value += 0.5f;
    }
    iter++;
  }

  // defenders are underrated..
/* edit: THIS SHOULD BE CALCULATED IN AVERAGESTARTSTAT NOT IN PROFILE YOU N00B!!
  iter = defaultProfiles.begin();
  float pos = 0.0;
  while (iter != defaultProfiles.end()) {
    std::vector<Stat> &stats = iter->second;
    for (unsigned int j = 0; j < stats.size(); j++) {
      stats.at(j).value *= 1.0f + pow(pos, 1.4f) * 0.2f; // defenders are low in the map, since it's sorted by enum '0 .. 8 value'
      assert(stats.at(j).value > 0.0f && stats.at(j).value < 1.0f);
    }
    iter++;
    pos += 0.1; // 9 entries total
  }
*/
}

void GetDefaultProfile(const std::vector<WeightedPosition> &weightedPositions, std::vector<Stat> &averageProfile) {

  // total weight
  float totalWeight = 0.0f;
  for (unsigned int i = 0; i < weightedPositions.size(); i++) {
    //printf("** adding weight %f\n", weightedPositions.at(i).weight);
    totalWeight += weightedPositions.at(i).weight;
  }
  //printf("** done adding %i weights\n", weightedPositions.size());

  averageProfile.push_back((Stat){"physical_balance", 0.0});
  averageProfile.push_back((Stat){"physical_reaction", 0.0});
  averageProfile.push_back((Stat){"physical_acceleration", 0.0});
  averageProfile.push_back((Stat){"physical_velocity", 0.0});
  averageProfile.push_back((Stat){"physical_stamina", 0.0});
  averageProfile.push_back((Stat){"physical_agility", 0.0});
  averageProfile.push_back((Stat){"physical_shotpower", 0.0});
  averageProfile.push_back((Stat){"technical_standingtackle", 0.0});
  averageProfile.push_back((Stat){"technical_slidingtackle", 0.0});
  averageProfile.push_back((Stat){"technical_ballcontrol", 0.0});
  averageProfile.push_back((Stat){"technical_dribble", 0.0});
  averageProfile.push_back((Stat){"technical_shortpass", 0.0});
  averageProfile.push_back((Stat){"technical_highpass", 0.0});
  averageProfile.push_back((Stat){"technical_header", 0.0});
  averageProfile.push_back((Stat){"technical_shot", 0.0});
  averageProfile.push_back((Stat){"technical_volley", 0.0});
  averageProfile.push_back((Stat){"mental_calmness", 0.0});
  averageProfile.push_back((Stat){"mental_workrate", 0.0});
  averageProfile.push_back((Stat){"mental_resilience", 0.0});
  averageProfile.push_back((Stat){"mental_defensivepositioning", 0.0});
  averageProfile.push_back((Stat){"mental_offensivepositioning", 0.0});
  averageProfile.push_back((Stat){"mental_vision", 0.0});

  for (unsigned int i = 0; i < weightedPositions.size(); i++) {
    std::map < e_PositionName, std::vector<Stat> > ::iterator iter = defaultProfiles.find(weightedPositions.at(i).positionName);
    assert(iter != defaultProfiles.end());
    const std::vector<Stat> &stats = iter->second;
    for (unsigned int j = 0; j < stats.size(); j++) {
      //printf("|| %f, %f, %f\n", stats.at(j).value, weightedPositions.at(i).weight, totalWeight);
      averageProfile.at(j).value += stats.at(j).value * (weightedPositions.at(i).weight / totalWeight);
    }
  }

  /* verbose
  for (unsigned int i = 0; i < averageStats.size(); i++) {
    printf("%s: %f\n", averageStats.at(i).name.c_str(), averageStats.at(i).value);
  }
  */

}

std::string GetProfileString(const std::vector<Stat> &profileStats) {
  std::string result;
  for (unsigned int i = 0; i < profileStats.size(); i++) {
    result.append("<" + profileStats.at(i).name + ">" + real_to_str(profileStats.at(i).value) + "</" + profileStats.at(i).name + ">\n");
  }
  return result;
}

/* deprecated
float GetAverageStatFromValue(int age, int value) {
  // right now: overall lowest/highest value: 10/45643480

  float currentStat = clamp((float)value / 46000000.0f, 0.000001f, 0.99f);

  // player value works exponentially.. rich clubs are willing to pay lots extra for that little bit more skill
  currentStat = pow(currentStat, 0.2f);
  //printf("%i, %f\n", value, currentStat);

  // young players are 'overpriced', as in, their price is higher than the corresponding stat, since 'potential' is considered in the price
  // same for old players, the other way around. 'curve' this right
  int base = 15; // youngest (40 is oldest)
  float exaggerate = 1.5f;
  float valueToStatMultiplier[26];

  valueToStatMultiplier[15 - base] = 0.64;
  valueToStatMultiplier[16 - base] = 0.66;
  valueToStatMultiplier[17 - base] = 0.68;
  valueToStatMultiplier[18 - base] = 0.70;
  valueToStatMultiplier[19 - base] = 0.73;
  valueToStatMultiplier[20 - base] = 0.76;
  valueToStatMultiplier[21 - base] = 0.79;
  valueToStatMultiplier[22 - base] = 0.82;
  valueToStatMultiplier[23 - base] = 0.85;
  valueToStatMultiplier[24 - base] = 0.88;
  valueToStatMultiplier[25 - base] = 0.91;
  valueToStatMultiplier[26 - base] = 0.94;
  valueToStatMultiplier[27 - base] = 0.97;
  valueToStatMultiplier[28 - base] = 1.00; // toppling point - at this age the 'value' graph cuts the 'stats' graph
  valueToStatMultiplier[29 - base] = 1.04;
  valueToStatMultiplier[30 - base] = 1.08;
  valueToStatMultiplier[31 - base] = 1.12;
  valueToStatMultiplier[32 - base] = 1.16;
  valueToStatMultiplier[33 - base] = 1.20;
  valueToStatMultiplier[34 - base] = 1.24;
  valueToStatMultiplier[35 - base] = 1.28;
  valueToStatMultiplier[36 - base] = 1.32;
  valueToStatMultiplier[37 - base] = 1.35;
  valueToStatMultiplier[38 - base] = 1.38;
  valueToStatMultiplier[39 - base] = 1.41;
  valueToStatMultiplier[40 - base] = 1.43;
  for (int i = 0; i <= 40 - base; i++) {
    valueToStatMultiplier[i] -= 1.0;
    valueToStatMultiplier[i] *= exaggerate;
    valueToStatMultiplier[i] += 1.0;

    //valueToStatMultiplier[i] -= 0.9;
  }
  currentStat *= valueToStatMultiplier[age - base];

  // even bad players have SOME skills.
  //XXcurrentStat = 0.4f + currentStat * 0.6f;
  //currentStat = 0.1f + currentStat * 0.9f;

  //currentStat = clamp(currentStat, 0.01, 0.99);

  return currentStat;
}
*/

float CalculateStat(float baseStat, float profileStat, float age, e_DevelopmentCurveType developmentCurveType) {

  float idealAge = 27;
  float ageFactor = curve( 1.0f - NormalizedClamp(fabs(age - idealAge), 0, 13) * 0.5f , 1.0f) * 2.0f - 1.0f; // 0 .. 1
  assert(ageFactor >= 0.0f && ageFactor <= 1.0f);
  //ageFactor = clamp(ageFactor, 0.0f, 1.0f);

  // this factor should roughly be around 1.0f for good players at their top age.
  float agedBaseStat = baseStat * (ageFactor * 0.5f + 0.5f) * 1.2f;

  float agedProfileStat = clamp(profileStat * 2.0f * agedBaseStat, 0.01f, 1.0f); // profile stat * 2 because average == 0.5

  return agedProfileStat;
}

/*
float GetIndividualStat(float averageStat, float profileStat, float age) {

  //float stat = averageStat * profileStat;

  //float ageSkill = 1.0 - fabs( 1.0 - ((age - 15) / (28 - 15)) ); // flip twice (1.0 - ..) to be able to use fabs for changing direction @ peak age
  //float ageFactor = clamp(ageSkill, 0.0, 1.0);

  float ageFactor = clamp( float(age - 15) / float(28 - 15) , 0.0, 1.0);

  // some test thing, i guess
  // //float result = stat / (stat + 0.3f) * 1.3f;
  // float result = tanh(2.4f * stat) * 1.06f; // hyperbolic tangent
  // //float result = stat * 1.6f;

  //float agedProfileStat = tanh(2.4f * profileStat * ageFactor) * 1.06f; // hyperbolic tangent

  // even out specific skills with age (keep average around 0.5)
  ageFactor *= 0.6f;
  float agedProfileStat = (profileStat * (1.0 - ageFactor)) + (0.5 * ageFactor);

  float result = agedProfileStat;
  //float result = averageStat * agedProfileStat * 1.8f;// <-- why was I doing that? should have commented it

  return clamp(result, 0.00000001, 0.99999999);
}

float GetAverageStatFromBaseStat(float baseStat, int age, e_DevelopmentCurveType developmentCurveType) {

  float ageFactor = pow(clamp( float(age - 15) / float(28 - 15) , 0.0, 1.0), 0.5);
  return pow(baseStat, 1.0 - ageFactor * 0.9f);

  float idealAge = 27;
  float ageFactor = curve( 1.0f - NormalizedClamp(fabs(age - idealAge), 0, 13) * 0.6f , 1.0f);

  return pow(baseStat, ageFactor);
}
*/

template <> TemporalValue<Quaternion>::TemporalValue() {
  data = QUATERNION_IDENTITY;
  time_ms = 0;
};

template <typename T> TemporalSmoother<T>::TemporalSmoother() {
  //snapshotSize = 3;
  snapshotSize =
      3 + int(std::ceil(temporalSmoother_history_ms /
                        10.0f));  // not sure how to calculate proper number?
  values = boost::circular_buffer< TemporalValue<T> >(snapshotSize);
}

template <typename T> void TemporalSmoother<T>::SetValue(const T &data, unsigned long valueTime_ms) {
  TemporalValue<T> value;
  value.data = data;
  value.time_ms = valueTime_ms;
  //printf("adding time: %lu\n", value.time_ms);
  values.push_back(value);
}

template <typename T> T TemporalSmoother<T>::GetValue(unsigned long currentTime_ms, unsigned long history_ms) const {

  if (values.size() == 0) {
    TemporalValue<T> bla; // do it like this so the struct constructor is invoked for T
    return bla.data;
  }
  if (values.size() == 1) return (*values.begin()).data; // only one value yet

  //return (values.back()).data; // disable smoother

  unsigned long now_ms = currentTime_ms;
  //unsigned long now_ms = (currentTime_ms == 0) ? EnvironmentManager::GetInstance().GetTime_ms() : currentTime_ms;
  unsigned long targetTime_ms = 0;
  if (history_ms <= now_ms) targetTime_ms = now_ms - history_ms; // this makes sure targetTime_ms won't become negative (and loop-around since it's an unsigned var)

  // find the 2 values we need

  TemporalValue<T> value1;
  TemporalValue<T> value2;

  bool debug = false;//(random(0.0f, 1.0f) > 0.99f);

  int t1index = -1;
  int t2index = -1;
  if (debug) printf("-- start --\n");
  for (unsigned int i = 0; i < values.size(); i++) {
    if (debug) printf("iteration %i\n", i);
    const TemporalValue<T> *iter = &values[i];
    if (debug) printf("%lu vs. %lu ?\n", (*iter).time_ms, targetTime_ms);
    if ((*iter).time_ms <= targetTime_ms) {
      value1.data = (*iter).data;
      value1.time_ms = (*iter).time_ms;
      t1index = i;
    }
    else if ((*iter).time_ms > targetTime_ms) {
      value2.data = (*iter).data;
      value2.time_ms = (*iter).time_ms;
      t2index = i;
      break;
    }
  }
  if (debug) printf("-- end --\n");

  // if (targetTime_ms < value1.time_ms) printf("<");
  // else if (targetTime_ms > value2.time_ms) printf(">");
  // else printf("_");

  if (debug) printf("current: %lu, target: %lu, t1: %lu, t2: %lu, t1index/t2index/size: %i/%i/%i\n", currentTime_ms, targetTime_ms, value1.time_ms, value2.time_ms, t1index, t2index, values.size());
  // if (value1.time_ms == 0) printf("1");
  // if (value2.time_ms == 0) printf("2");
  if (value1.time_ms == 0) return value2.data;
  if (value2.time_ms == 0) return value1.data;

  float bias = NormalizedClamp(targetTime_ms, value1.time_ms, std::max(value2.time_ms, value1.time_ms + 1));
  if (debug) printf("bias: %f\n", bias);
  //unsigned long time_ms = EnvironmentManager::GetInstance().GetTime_ms();
  //int diff = value2.time_ms - value1.time_ms;
  //printf("%i to %i (%i)\n", (int)(value1.time_ms - time_ms), (int)(value2.time_ms - time_ms), diff);
  //if (random(0.0f, 1.0f) > 0.8f) printf("bias: %f\n", bias);
  //if (random(0.0f, 1.0f) > 0.8f) printf("ago: %li\n", now_ms - values.at(values.size() - 1).time_ms);

  return DataMix(value1.data, value2.data, bias);
}

template <typename T> T TemporalSmoother<T>::DataMix(const T &data1, const T &data2, float bias) const {
  return data1 * (1.0f - bias) + data2 * bias;
}

template <> Quaternion TemporalSmoother<Quaternion>::DataMix(const Quaternion &data1, const Quaternion &data2, float bias) const {
  return data1.GetLerped(bias, data2);
}

template class TemporalSmoother<Vector3>;
template class TemporalSmoother<Quaternion>;
template class TemporalSmoother<float>;
template class TemporalSmoother<unsigned long>;
template class TemporalSmoother<int>;


#include <vector>
#include <list>
#include <map>
#include <set>

#include <queue>
#include <deque>
#include <stack>
#include <bitset>
#include <algorithm>
#include <functional>
#include <numeric>
#include <utility>
#include <sstream>
#include <iostream>
#include <fstream>
#include <iomanip>
#include <cstdio>
#include <cmath>
#include <cstdlib>
#include <ctime>
#include <cstring>
#include <assert.h>
#include <boost/lexical_cast.hpp>

#define INF 1023123123
#define EPS 1e-11
#define LSOne(S) (S & (-S))

#define M_PI           3.14159265358979323846  /* pi */

#define FORN(X,Y) for (int (X) = 0;(X) < (Y);++(X))
#define FORB(X,Y) for (int (X) = (Y);(X) >= 0;--(X))
#define REP(X,Y,Z) for (int (X) = (Y);(X) < (Z);++(X))
#define REPB(X,Y,Z) for (int (X) = (Y);(X) >= (Z);--(X))

#define SZ(Z) ((int)(Z).size())
#define ALL(W) (W).begin(), (W).end()
#define PB push_back

#define MP make_pair
#define A first
#define B second

#define FORIT(X,Y) for(typeof((Y).begin()) X = (Y).begin();X!=(Y).end();X++)

using namespace std;

typedef long long ll;
typedef double db;
typedef vector<int> vi;
typedef pair<int, int> ii;
typedef vector<ii> vii;

void print_case(int i)
{
   cout << "Case #" << i + 1 << ": ";
}

bool myfunction(pair<ll, pair<ll, int>> i, pair<ll, pair<ll, int>> j) { return (i>j); }

int main()
{
   cout.precision(12);
   int ntc;
   cin >> ntc;

   FORN(kk, ntc)
   {
      print_case(kk);
      int C = 0;
      int J = 1;
      int ac, aj;
      cin >> ac >> aj;
      vector<pair<int, pair<int, int>>> ranges(ac + aj);
      int used_c = 0;
      int used_j = 0;
      FORN(i, ac)
      {
         cin >> ranges[i].first >> ranges[i].second.first;
         ranges[i].second.second = C;
         used_c += ranges[i].second.first - ranges[i].first;
      }
      REP(i, ac, ac + aj)
      {
         cin >> ranges[i].first >> ranges[i].second.first;
         ranges[i].second.second = J;
         used_j += ranges[i].second.first - ranges[i].first;
      }
      sort(ranges.begin(), ranges.end());
      vector<ii> open_ranges_c;
      vector<ii> open_ranges_j;
      int clashes = 0;
      FORN(i, ranges.size())
      {
         auto cur = ranges[i];
         auto prev = i == 0 ? ranges[ranges.size() - 1] : ranges[i - 1];
         if (prev.second.second == cur.second.second)
         {
            int duration = cur.first - prev.second.first;
            if (duration < 0)
               duration += 24 * 60;
            if (prev.second.second == C)
               open_ranges_c.push_back(ii(duration, prev.second.first));
            else
               open_ranges_j.push_back(ii(duration, prev.second.first));
         }
         else
         {
            clashes++;
         }
      }
      sort(open_ranges_c.begin(), open_ranges_c.end());
      sort(open_ranges_j.begin(), open_ranges_j.end());
      int free_c = 720 - used_c;
      int free_j = 720 - used_j;

      FORN(i, open_ranges_c.size())
      {
         auto pp = open_ranges_c[i];
         if (pp.first <= free_c)
         {
            free_c -= pp.first;
         }
         else
         {
            clashes += 2;
         }
      }
      FORN(i, open_ranges_j.size())
      {
         auto pp = open_ranges_j[i];
         if (pp.first <= free_j)
         {
            free_j -= pp.first;
         }
         else
         {
            clashes += 2;
         }
      }
      cout << clashes << endl;
   }
}


/**********************************************************************************
 *  The MIT License (MIT)                                                        *
 *                                                                                *
 *  Copyright (c) 2014 Carnegie Mellon University                                 *
 *                                                                                *
 *  Permission is hereby granted, free of charge, to any person obtaining a copy  *
 *  of this software and associated documentation files (the "Software"), to deal *
 *  in the Software without restriction, including without limitation the rights  *
 *  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell     *
 *  copies of the Software, and to permit persons to whom the Software is         *
 *  furnished to do so, subject to the following conditions:                      *
 *                                                                                *
 *  The above copyright notice and this permission notice shall be included in    *
 *  all copies or substantial portions of the Software.                           *
 *                                                                                *
 *  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR    *
 *  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,      *
 *  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE   *
 *  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER        *
 *  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, *
 *  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN     *
 *  THE SOFTWARE.                                                                 *
 **********************************************************************************/
/**
 * @file   cpdr.cpp
 * @brief  A pedestrian dead-reckoning system.  Integrates gyros and step counting
 * @author M. George
 */

#include <cmath>
#include <iostream>

#include <Eigen/Dense>

#include "cpdr.h"

using namespace BASIL;

/**************************************************************************//**
* cPDR Constructor.  Constant vectors and matrices (gravity,
* earth rate) are formed here.  PDR state is set to ALIGN initially.  PDR
* cannot be used until an alignment method is called.
* \arg \c params.  Boost::property_tree object and id of attached IMU.
******************************************************************************/
cPDR::cPDR(boost::property_tree::ptree config, unsigned int imuid) : mStepDetector(config)
{
    mImuId = imuid;

    mMessageCounter = 0;

    // Clear all the member vectors and matrices.
    mEarthRate           = CartesianVector::Zero();
    mGravity             = CartesianVector::Zero();
    mAlpha               = CartesianVector::Zero();
    mDeltaP              = CartesianVector::Zero();
    mDeltaV              = CartesianVector::Zero();
    mGravityReaction     = CartesianVector::Zero();

    mIdentity = CartesianMatrix::Identity();
    mA        = CartesianMatrix::Zero();

    mAlphaSkew     = SkewMatrix::Zero();
    mAlphaMagnitude = 0.0;

    mf1 = mf2 = mf3 = mf4 = 0.0;

    mCbn = RotationMatrix::Identity();

    mdt = 0;

    // INS initially stopped, time = 0, no messages and ready to calibrate.
    mTime = 0.0;
    mPreviousTime = 0.0;
    mPreviousEarthRateTime = 0.0;
    mAlignmentTime = config.get<double>("parameters.ins.alignmentperiod");
    mIMUCount = 0;
    mIMURate = config.get<unsigned int>("parameters.hardware.imus.xsensmti.rate");
    mEarthUpdateRate = config.get<unsigned int>("parameters.ins.earthupdaterate");

    if (mEarthUpdateRate < 0)
    {
        std::cerr << "Earth update rate must be >= 0" << std::endl;
        mApplyEarthRateCorrections = false;
        mEarthSubsample = 0;
    }
    else if (mEarthUpdateRate == 0)
    {
        mApplyEarthRateCorrections = false;
        mEarthSubsample = 0;
    }
    else
    {
        mApplyEarthRateCorrections = true;
        mEarthSubsample = mIMURate / mEarthUpdateRate;
    }

    // Get initial conditions
    mInitialHeading = config.get<double>("parameters.ins.initialheading")*BASIL::CONSTANTS::DEG_TO_RAD;
    mInitialLatitude = config.get<double>("parameters.ins.approximatelatitude")*BASIL::CONSTANTS::DEG_TO_RAD;

    // Step data
    mStepLength = config.get<double>("parameters.pdr.steplength");
    mStepIncrement = CartesianVector::Zero();

    // Clear initial INS data structure
    mInsData.Velocity    = CartesianVector::Zero();
    mInsData.Position    = Array::Zero();
    mInsData.Orientation = AngleArray::Zero();
    mInsData.InsMdl = BASIL::INS::GENERIC_INS;
    mInsData.Id = imuid;

    // Clear IMU calibration data structure
    mImuCalibrationData.AccelerationBias = CartesianVector::Zero();
    mImuCalibrationData.AccelerationScaleFactor = CartesianVector::Zero();
    mImuCalibrationData.AngularRateBias << 0.00414044772524217,0.00189662656009109,0.00726575764268722;
    mImuCalibrationData.AngularRateGBias = CartesianVector::Zero();
    mImuCalibrationData.AngularRateScaleFactor = CartesianVector::Zero();
    mImuCalibrationData.ImuMdl = BASIL::IMUS::GENERIC_IMU;
    mImuCalibrationData.Id = imuid;

    mCurrentState = ALIGNING;
}

/**************************************************************************//**
* cPDR Destructor
******************************************************************************/
cPDR::~cPDR()
{
}

/**************************************************************************//**
* Integrates the attitude forward using Savage type exact
* integration. Ref: Savage, P.  Strapdown Analytics, 2nd Ed.  Chapters 7, 19
* Steps are detected and position is dead-reckoned using heading data and
* average step length.
* \arg \c omega.  Vector of x,y,z gyro rate readings in rad/s
* \arg \c accel.  Vector of x,y,z accelerometer readings in m/s/s
* \arg \c time.   Time stamp of IMU readings
* \return \c void
******************************************************************************/
void cPDR::integratePVA(const cImuData& imudata)
{
    if (mCurrentState < cPDR::RUNNING)
    {
        std::cerr << "Error (cPDR): INS must be aligned before integrating." << std::endl;
	    return;
    }

    // For the first message we receive predict using nominal IMU rate
    if (mMessageCounter == 0)
    {
        mdt = 1.0/double(mIMURate);
    }
    else
    {
        mdt = imudata.Time.getSeconds() - mPreviousTime;
    }

    // Catch an out of order packet here
    if (mdt < 0)
    {
        //Some kind of standard error here
        std::cerr << "Error (cPDR): dt is negative in integratePVA()" << std::endl;
        return;
    }

    if (mdt > 2.0/double(mIMURate))
    {
        // Some kind of standard error here
        std::cerr << "Warning (cPDR): dt (" << mdt << "s) is large in integratePVA()" << std::endl;
        // Reset the counters so this doesn't persist.
        mInsData.Time = imudata.Time;
        mPreviousTime = imudata.Time.getSeconds();
    	mMessageCounter++;
        //  Returning here is equivalent to assuming no motion during period of missing data.
        return;
    }

    // Get rotation vector
    mAlpha = imudata.AngularRate*mdt;
    mAlphaMagnitude = mAlpha.norm();

    // Catch zero rotation vector magnitudes which happen with stationary sensors and quantization
    if (mAlphaMagnitude > 0.0)
    {
        // Skew symmetric matrix from rotation vector
        BASIL::MATH::skew(mAlpha, mAlphaSkew);

        // Integration constants
        mf1 = std::sin(mAlphaMagnitude)/mAlphaMagnitude;
        mf2 = (1.0 - std::cos(mAlphaMagnitude))/(mAlphaMagnitude*mAlphaMagnitude);
        mf3 = (1.0/(mAlphaMagnitude*mAlphaMagnitude))*(1.0-mf1);
        mf4 = (1.0/(mAlphaMagnitude*mAlphaMagnitude))*(0.5-mf2);

	    // Position update
	    if (mStepDetector.detect(imudata.Acceleration(BASIL::IMUZ)))
	    {
	        mStepIncrement(BASIL::NORTH) = mStepLength * std::cos(mInsData.Orientation(BASIL::YAW));
	        mStepIncrement(BASIL::EAST) = mStepLength * std::sin(mInsData.Orientation(BASIL::YAW));
	        mStepIncrement(BASIL::DOWN) = 0.0;
            mInsData.Position.noalias() += mStepIncrement;
            //std::cout << mMessageCounter << std::endl;
        }

        // Orientation update
        mA.noalias() = mIdentity + mf1*mAlphaSkew + mf2*mAlphaSkew*mAlphaSkew;
        mCbn *= mA;
        cbn2euler(mCbn,mInsData.Orientation);
    }
    else
    {
        // Position update
	    if (mStepDetector.detect(imudata.Acceleration(BASIL::IMUZ)))
	    {
	        mStepIncrement(BASIL::NORTH) = mStepLength * std::cos(mInsData.Orientation(BASIL::YAW));
	        mStepIncrement(BASIL::EAST) = mStepLength * std::sin(mInsData.Orientation(BASIL::YAW));
	        mStepIncrement(BASIL::DOWN) = 0.0;
            mInsData.Position.noalias() += mStepIncrement;
            //std::cout << mMessageCounter << std::endl;
        }
    }

    // Apply an earth rate correction at a lower frequency
    if ( mApplyEarthRateCorrections && (mMessageCounter % mEarthSubsample == 0) )
    {
        cPDR::earthRateCorrection(imudata.Time.getSeconds());
    }

    // Times are set here so that a user can bypass position and velocity integration
    // if desired, effectively using this class as a AHRS system with gyros only.
    mInsData.Time = imudata.Time;
    mPreviousTime = imudata.Time.getSeconds();

    mMessageCounter++;
}

/**************************************************************************//**
* Called at sub-intervals of the integreatePVA() function.  Adds correction
* to current attitude to account for Earth rotation effects.  Position and velocity
* corrections are taken into account in integratePVA() and as a natural consequence
* of this update.  Not used if no latitude input is used in alignment call.
* \arg \c void
* \return \c void
******************************************************************************/
void cPDR::earthRateCorrection(const double time)
{
    // First time in we use nominal earth correction rate for the dt.
    if (mPreviousEarthRateTime == 0.0)
    {
        mdt = 1.0 / (double)mEarthSubsample;
    }
    else
    {
        mdt = time - mPreviousEarthRateTime;
    }

    // Catch negative time increments
    if (mdt < 0)
    {
        //Some kind of standard error here
        std::cerr << "Error (cPDR): dt is negative in earthRateCorrection()" << std::endl;
        return;
    }

    mAlpha = -1.0*mdt*mEarthRate;
    mAlphaMagnitude = mAlpha.norm();
    mf1 = std::sin(mAlphaMagnitude)/mAlphaMagnitude;
    mf2 = (1.0 - std::cos(mAlphaMagnitude))/(mAlphaMagnitude*mAlphaMagnitude);
    BASIL::MATH::skew(mAlpha, mAlphaSkew);

    mA.noalias() = mIdentity + mf1*mAlphaSkew + mf2*mAlphaSkew*mAlphaSkew;
    mCbn = mA*mCbn;
    cbn2euler(mCbn,mInsData.Orientation);

    mPreviousEarthRateTime = time;
}

/**************************************************************************//**
* Local initial alignment with velocity and position in local navigation coordinates.
* Roll and pitch angles are coarsely aligned from an input average acceleration vector
* (2-3s recommended) with gravity.  Initial heading (North), velocity and position (Local)
* are user inputs.  User must also input an approximate latitude.  The global heading and
* approximate latitude allow reasonable gravity models and earth rate corrections to be fixed.
*\arg \c Pointer to 3x1 array of time averaged x,y,z accelerometer signals.
* 		 Suggested averaging time 2-3s.  IMU must be stationary during this time!
* \return \c void
******************************************************************************/
void cPDR::localAlign(const CartesianVector& gravityReactionForce,  const double heading, const CartesianVector& initialUTMPosition,
                      const CartesianVector& initialUTMVelocity, const double approximateLatitude)
{
    gravityAlignRollPitch(gravityReactionForce, mInsData.Orientation);
    mInsData.Orientation(BASIL::YAW) = heading;

    cPDR::euler2cbn(mInsData.Orientation,mCbn);

    mInsData.Velocity = initialUTMVelocity;
    mInsData.Position = initialUTMPosition;

    // Earth rate:
    getEarthRate(approximateLatitude, mEarthRate);

    // Gravity
    mGravity(BASIL::DOWN) = getGravityMagnitude(approximateLatitude);

    mMode = cPDR::LOCAL;
    // Earth rate corrections will be applied unless already turned off in constructor
    mApplyEarthRateCorrections = mApplyEarthRateCorrections && true;

    // Zero counters in case we need to re-align at some point
    mMessageCounter = 0;
    mdt = 0;
    mInsData.Time.setTime(TIME::FREE_RUNNING,0,0.0);
    mPreviousTime = 0.0;
    mPreviousEarthRateTime = 0.0;

    // We are now running and ready to call integrate*() functions
    mCurrentState = cPDR::RUNNING;
}

/**************************************************************************//**
* Local initial alignment with of roll and pitch angles only.
* Roll and pitch angles are coarsely aligned from an input average acceleration vector
* (2-3s recommended) with gravity.  Initial heading, velocity and position are all set to 0.
* User must also input an approximate latitude to allow a coarse estimate of gravity.
*\arg \c Pointer to 3x1 array of time averaged x,y,z accelerometer signals.
* 		 Suggested averaging time 2-3s.  IMU must be stationary during this time!
* \return \c void
******************************************************************************/
void cPDR::localAlign(const CartesianVector& gravityReactionForce, const double approximateLatitude)
{
    gravityAlignRollPitch(gravityReactionForce, mInsData.Orientation);

    euler2cbn(mInsData.Orientation,mCbn);

    // Earth rate:
    getEarthRate(approximateLatitude, mEarthRate);

    // Gravity
    mGravity(BASIL::DOWN) = getGravityMagnitude(approximateLatitude);

    mMode = cPDR::LOCAL;
    // Earth rate corrections will be applied unless already turned off in constructor
    mApplyEarthRateCorrections = mApplyEarthRateCorrections && true;

    // Zero counters in case we need to re-align at some point
    mMessageCounter = 0;
    mdt = 0;
    mInsData.Time.setTime(TIME::FREE_RUNNING,0,0.0);
    mPreviousTime = 0.0;
    mPreviousEarthRateTime = 0.0;

    // We are now running and ready to call integrate*() functions
    mCurrentState = cPDR::RUNNING;
}

/**************************************************************************//**
* Get the magnitude of gravity at our current latitude using a Defense Mapping
* Agency model.  Reference:  Jay A. Farrell.  Aided Inertial Navigation, p.33.
* \arg \c latitude.  Reference to latitude data (radians)
* \arg \c gravity.  Reference to gravity output.
* \return \c void
******************************************************************************/
double cPDR::getGravityMagnitude(const double latitude)
{
    double gravity = BASIL::WGS84::EQUATORIAL_GRAVITY *
                    ( (          1.0 + 0.0019318513530*std::sin(latitude)*std::sin(latitude)) /
                      (std::sqrt(1.0 - 0.0066943800229*std::sin(latitude)*std::sin(latitude))) );
    return gravity;
}

/**************************************************************************//**
* Correct the current position, velocity and orientation with errors.
* \arg \c position.  Position.
* \arg \c velocity.  Velocity.
* \arg \c eulers.  Euler angles.
* \return \c void
******************************************************************************/
void cPDR::correctPVA(const CartesianVector& positionerror,
                      const CartesianVector& velocityerror,
                      const AngleArray& skewangles)
{
    mInsData.Velocity -= velocityerror;
    mInsData.Position -= positionerror;

    BASIL::MATH::skew(skewangles, mAlphaSkew);
    mA = mIdentity - mAlphaSkew;
    mCbn = mA.inverse() * mCbn;

    cbn2euler(mCbn, mInsData.Orientation);
}

/**************************************************************************//**
* Get the components of earth rate at our current latitude.
* \arg \c latitude.  Reference to latitude data (radians)
* \arg \c gravity.  Pointer to earthRate vector for output.
* \return \c void
******************************************************************************/
void cPDR::getEarthRate(const double latitude, CartesianVector& earthRate)
{
    earthRate(NORTH) = BASIL::WGS84::EARTH_RATE * std::cos(latitude);
    earthRate(EAST) = 0;
    earthRate(DOWN) = -1.0 * BASIL::WGS84::EARTH_RATE*std::sin(latitude);
}

/**************************************************************************//**
* Get roll and pitch angles from a gravity vector.  Input vector should be averaged
* over a period of 2-3 seconds with a stationary IMU.  Aligns IMU x,y coordinates
* to Down gravity vector.  1 mg of accelerometer bias -> 1 mrad of roll, pitch error.
* Calibrate your accelerometer before averaging if possible.
* \arg \c latitude.  Reference to latitude data (radians)
* \arg \c gravity.  Pointer to earthRate vector for output.
* \return \c void
******************************************************************************/
void cPDR::gravityAlignRollPitch(const CartesianVector& gravityReactionForce, AngleArray& angles)
{
    angles(ROLL) = std::atan2(-gravityReactionForce(BODYY),
                              -gravityReactionForce(BODYZ));
    angles(PITCH) = std::atan2(gravityReactionForce(BASIL::BODYX),
                                           std::sqrt((gravityReactionForce(BODYY) * gravityReactionForce(BODYY))
                                                    +(gravityReactionForce(BODYZ) * gravityReactionForce(BODYZ))));
}

/**************************************************************************//**
* Calculate a direction cosine matrix, Cbm, relating body frame to global North,
* East, Down frame from an Euler angle vector.  Cbm is propagated directly by
* integrate so this function is most useful at alignment time for initial Cbm.
* \arg \c Cbm.  Empty Cbm matrix to be filled.
* \arg \c type.  Euler angle vector.
* \return \c void
******************************************************************************/
void cPDR::euler2cbn(const AngleArray& eulers, CartesianMatrix& Cbn)
{
    double roll = eulers(0);
    double pitch = eulers(1);
    double yaw = eulers(2);

    Cbn << std::cos(yaw)*std::cos(pitch),
         std::cos(yaw)*std::sin(pitch)*std::sin(roll) - std::sin(yaw)*std::cos(roll),
         std::cos(yaw)*std::sin(pitch)*std::cos(roll) + std::sin(roll)*std::sin(yaw),
         std::cos(pitch)*std::sin(yaw),
         std::cos(roll)*std::cos(yaw) + std::sin(roll)*std::sin(pitch)*std::sin(yaw),
         std::cos(roll)*std::sin(pitch)*std::sin(yaw) - std::sin(roll)*std::cos(yaw),
         -std::sin(pitch),
         std::sin(roll)*std::cos(pitch),
         std::cos(roll)*std::cos(pitch);
}

/**************************************************************************//**
* Set the Euler angles from the current Cbm value
* \arg \c Cbm.  Current Cbm matrix.
* \return \c void
******************************************************************************/
void cPDR::cbn2euler(const RotationMatrix& Cbn, AngleArray& eulers)
{
    eulers(0) = std::atan2(Cbn(2,1),Cbn(2,2));
    eulers(1) = std::asin(-Cbn(2,0));
    eulers(2) = std::atan2(Cbn(1,0),Cbn(0,0));
}

/**************************************************************************//**
* Copy the current Cbm into the input reference.
* \arg \c out.  Empty Cbm matrix to be filled.
* \return \c void
******************************************************************************/
void cPDR::getCbn(RotationMatrix& out)
{
    out = mCbn;
}

/**************************************************************************//**
* Copy the current euler angles into the input reference.
* \arg \c out.  Empty position vector to be filled.
* \return \c void
******************************************************************************/
void cPDR::getEulers(AngleArray& out)
{
    cbn2euler(mCbn,out);
}

/**************************************************************************//**
* Copy the current velocity into the input reference.
* \arg \c out.  Empty velocity vector to be filled.
* \return \c void
******************************************************************************/
void cPDR::getVelocity(CartesianVector& out)
{
    out = mInsData.Velocity;
}

/**************************************************************************//**
* Copy the current position into the input reference.
* \arg \c out.  Empty position vector to be filled.
* \return \c void
******************************************************************************/
void cPDR::getPosition(CartesianVector& out)
{
    out = mInsData.Position;
}

/**************************************************************************//**
* Copy the current gravity vector into the input reference.
* \arg \c out.  Empty gravity vector to be filled.
* \return \c void
******************************************************************************/
void cPDR::getGravity(CartesianVector& out)
{
    out = mGravity;
}

/**************************************************************************//**
* Copy the current earth rate vector into the input reference.
* \arg \c out.  Empty earth rate vector to be filled.
* \return \c void
******************************************************************************/
void cPDR::getEarthRate(CartesianVector& out)
{
    out = mEarthRate;
}

/**************************************************************************//**
* Copy the current position, velocity and euler angles out.
* \arg \c position.  Empty position vector to be filled.
* \arg \c velocity.  Empty velocity vector to be filled.
* \arg \c eulers.  Empty euler angles vector to be filled.
* \return \c void
******************************************************************************/
void cPDR::getPVA(CartesianVector& position, CartesianVector& velocity, CartesianVector& eulers)
{
    position = mInsData.Position;
    velocity = mInsData.Velocity;
    cbn2euler(mCbn,eulers);
}

/**************************************************************************//**
* Set the current Cbm from the input field.
* \arg \c out.  Cbm matrix input.
* \return \c void
******************************************************************************/
void cPDR::setCbn(const RotationMatrix& in)
{
    mCbn = in;
    cbn2euler(mCbn, mInsData.Orientation);
}

/**************************************************************************//**
* Set the current euler angles from the input field.
* \arg \c out.  Euler angles (r,p,y) input.
* \return \c void
******************************************************************************/
void cPDR::setEulers(const AngleArray& in)
{
    mInsData.Orientation = in;
    euler2cbn(mInsData.Orientation, mCbn);
}

/**************************************************************************//**
* Set the current velocity from the input field.
* \arg \c out.  Velocity vector input.
* \return \c void
******************************************************************************/
void cPDR::setVelocity(const CartesianVector& in)
{
    mInsData.Velocity = in;
}

/**************************************************************************//**
* Set the current position from the input field.
* \arg \c out.  Position vector input.
* \return \c void
******************************************************************************/
void cPDR::setPosition(const CartesianVector& in)
{
    mInsData.Position = in;
}

/**************************************************************************//**
* Set the current position, velocity and euler angles.
* \arg \c position.  Position.
* \arg \c velocity.  Velocity.
* \arg \c eulers.  Euler angles.
* \return \c void
******************************************************************************/
void cPDR::setPVA(const CartesianVector& position,
                  const CartesianVector& velocity,
                  const AngleArray& eulers)
{
    mInsData.Position = position;
    mInsData.Velocity = velocity;
    mInsData.Orientation = eulers;
    euler2cbn(mInsData.Orientation, mCbn);
}

/**************************************************************************//**
* Set the current gravity vector
* \arg \c in.  Gravity in NED.
* \return \c void
******************************************************************************/
void cPDR::setGravity(const CartesianVector& in)
{
    mGravity = in;
}

/**************************************************************************//**
* Reset INS Counters so that a break in data does not cause a spike in the solution
* \arg \c void
* \return \c void
******************************************************************************/
void cPDR::resetCounters()
{
    // Zero counters in case we need to re-align at some point
    mMessageCounter = 0;
    mdt = 0;
    mTime = 0.0;
    mPreviousTime = 0.0;
    mPreviousEarthRateTime = 0.0;
}