/**
 * @file serialization_test.cpp
 * @author Ryan Curtin
 *
 * Test serialization of mlpack objects.
 */
#include <mlpack/core.hpp>

#include <boost/test/unit_test.hpp>
#include "old_boost_test_definitions.hpp"
#include "serialization.hpp"

#include <mlpack/core/dists/regression_distribution.hpp>
#include <mlpack/core/tree/ballbound.hpp>
#include <mlpack/core/tree/hrectbound.hpp>
#include <mlpack/core/metrics/mahalanobis_distance.hpp>
#include <mlpack/core/tree/binary_space_tree.hpp>
#include <mlpack/methods/hoeffding_trees/hoeffding_tree.hpp>
#include <mlpack/core/tree/cover_tree.hpp>
#include <mlpack/core/tree/rectangle_tree.hpp>

#include <mlpack/methods/perceptron/perceptron.hpp>
#include <mlpack/methods/logistic_regression/logistic_regression.hpp>
#include <mlpack/methods/neighbor_search/neighbor_search.hpp>
#include <mlpack/methods/softmax_regression/softmax_regression.hpp>
#include <mlpack/methods/det/dtree.hpp>
#include <mlpack/methods/naive_bayes/naive_bayes_classifier.hpp>
#include <mlpack/methods/rann/ra_search.hpp>
#include <mlpack/methods/lsh/lsh_search.hpp>
#include <mlpack/methods/decision_stump/decision_stump.hpp>
#include <mlpack/methods/lars/lars.hpp>

using namespace mlpack;
using namespace mlpack::distribution;
using namespace mlpack::regression;
using namespace mlpack::bound;
using namespace mlpack::metric;
using namespace mlpack::tree;
using namespace mlpack::perceptron;
using namespace mlpack::regression;
using namespace mlpack::naive_bayes;
using namespace mlpack::neighbor;
using namespace mlpack::decision_stump;

using namespace arma;
using namespace boost;
using namespace boost::archive;
using namespace boost::serialization;
using namespace std;

BOOST_AUTO_TEST_SUITE(SerializationTest);

/**
 * Serialize a random cube.
 */
BOOST_AUTO_TEST_CASE(CubeSerializeTest)
{
  arma::cube m;
  m.randu(2, 50, 50);
  TestAllArmadilloSerialization(m);
}

/**
 * Serialize an empty cube.
 */
BOOST_AUTO_TEST_CASE(EmptyCubeSerializeTest)
{
  arma::cube c;
  TestAllArmadilloSerialization(c);
}

/**
 * Can we load and save an Armadillo matrix?
 */
BOOST_AUTO_TEST_CASE(MatrixSerializeXMLTest)
{
  arma::mat m;
  m.randu(50, 50);
  TestAllArmadilloSerialization(m);
}

/**
 * How about columns?
 */
BOOST_AUTO_TEST_CASE(ColSerializeTest)
{
  arma::vec m;
  m.randu(50, 1);
  TestAllArmadilloSerialization(m);
}

/**
 * How about rows?
 */
BOOST_AUTO_TEST_CASE(RowSerializeTest)
{
  arma::rowvec m;
  m.randu(1, 50);
  TestAllArmadilloSerialization(m);
}

// A quick test with an empty matrix.
BOOST_AUTO_TEST_CASE(EmptyMatrixSerializeTest)
{
  arma::mat m;
  TestAllArmadilloSerialization(m);
}

/**
 * Can we load and save a sparse Armadillo matrix?
 */
BOOST_AUTO_TEST_CASE(SparseMatrixSerializeTest)
{
  arma::sp_mat m;
  m.sprandu(50, 50, 0.3);
  TestAllArmadilloSerialization(m);
}

/**
 * How about columns?
 */
BOOST_AUTO_TEST_CASE(SparseColSerializeTest)
{
  arma::sp_vec m;
  m.sprandu(50, 1, 0.3);
  TestAllArmadilloSerialization(m);
}

/**
 * How about rows?
 */
BOOST_AUTO_TEST_CASE(SparseRowSerializeTest)
{
  arma::sp_rowvec m;
  m.sprandu(1, 50, 0.3);
  TestAllArmadilloSerialization(m);
}

// A quick test with an empty matrix.
BOOST_AUTO_TEST_CASE(EmptySparseMatrixSerializeTest)
{
  arma::sp_mat m;
  TestAllArmadilloSerialization(m);
}

// Now, test mlpack objects.
BOOST_AUTO_TEST_CASE(DiscreteDistributionTest)
{
  // I assume that I am properly saving vectors, so, this should be
  // straightforward.
  vec prob;
  prob.randu(12);
  DiscreteDistribution t(prob);

  DiscreteDistribution xmlT, textT, binaryT;

  // Load and save with all serializers.
  SerializeObjectAll(t, xmlT, textT, binaryT);

  for (size_t i = 0; i < 12; ++i)
  {
    vec obs(1);
    obs[0] = i;
    const double prob = t.Probability(obs);
    if (prob == 0.0)
    {
      BOOST_REQUIRE_SMALL(xmlT.Probability(obs), 1e-8);
      BOOST_REQUIRE_SMALL(textT.Probability(obs), 1e-8);
      BOOST_REQUIRE_SMALL(binaryT.Probability(obs), 1e-8);
    }
    else
    {
      BOOST_REQUIRE_CLOSE(prob, xmlT.Probability(obs), 1e-8);
      BOOST_REQUIRE_CLOSE(prob, textT.Probability(obs), 1e-8);
      BOOST_REQUIRE_CLOSE(prob, binaryT.Probability(obs), 1e-8);
    }
  }
}

BOOST_AUTO_TEST_CASE(GaussianDistributionTest)
{
  vec mean(10);
  mean.randu();
  // Generate a covariance matrix.
  mat cov;
  cov.randu(10, 10);
  cov = (cov * cov.t());

  GaussianDistribution g(mean, cov);
  GaussianDistribution xmlG, textG, binaryG;

  SerializeObjectAll(g, xmlG, textG, binaryG);

  BOOST_REQUIRE_EQUAL(g.Dimensionality(), xmlG.Dimensionality());
  BOOST_REQUIRE_EQUAL(g.Dimensionality(), textG.Dimensionality());
  BOOST_REQUIRE_EQUAL(g.Dimensionality(), binaryG.Dimensionality());

  // First, check the means.
  CheckMatrices(g.Mean(), xmlG.Mean(), textG.Mean(), binaryG.Mean());

  // Now, check the covariance.
  CheckMatrices(g.Covariance(), xmlG.Covariance(), textG.Covariance(),
      binaryG.Covariance());

  // Lastly, run some observations through and make sure the probability is the
  // same.  This should test anything cached internally.
  arma::mat randomObs;
  randomObs.randu(10, 500);

  for (size_t i = 0; i < 500; ++i)
  {
    const double prob = g.Probability(randomObs.unsafe_col(i));

    if (prob == 0.0)
    {
      BOOST_REQUIRE_SMALL(xmlG.Probability(randomObs.unsafe_col(i)), 1e-8);
      BOOST_REQUIRE_SMALL(textG.Probability(randomObs.unsafe_col(i)), 1e-8);
      BOOST_REQUIRE_SMALL(binaryG.Probability(randomObs.unsafe_col(i)), 1e-8);
    }
    else
    {
      BOOST_REQUIRE_CLOSE(prob, xmlG.Probability(randomObs.unsafe_col(i)),
          1e-8);
      BOOST_REQUIRE_CLOSE(prob, textG.Probability(randomObs.unsafe_col(i)),
          1e-8);
      BOOST_REQUIRE_CLOSE(prob, binaryG.Probability(randomObs.unsafe_col(i)),
          1e-8);
    }
  }
}

BOOST_AUTO_TEST_CASE(LaplaceDistributionTest)
{
  vec mean(20);
  mean.randu();

  LaplaceDistribution l(mean, 2.5);
  LaplaceDistribution xmlL, textL, binaryL;

  SerializeObjectAll(l, xmlL, textL, binaryL);

  BOOST_REQUIRE_CLOSE(l.Scale(), xmlL.Scale(), 1e-8);
  BOOST_REQUIRE_CLOSE(l.Scale(), textL.Scale(), 1e-8);
  BOOST_REQUIRE_CLOSE(l.Scale(), binaryL.Scale(), 1e-8);

  CheckMatrices(l.Mean(), xmlL.Mean(), textL.Mean(), binaryL.Mean());
}

BOOST_AUTO_TEST_CASE(MahalanobisDistanceTest)
{
  MahalanobisDistance<> d;
  d.Covariance().randu(50, 50);

  MahalanobisDistance<> xmlD, textD, binaryD;

  SerializeObjectAll(d, xmlD, textD, binaryD);

  // Check the covariance matrices.
  CheckMatrices(d.Covariance(),
                xmlD.Covariance(),
                textD.Covariance(),
                binaryD.Covariance());
}

BOOST_AUTO_TEST_CASE(LinearRegressionTest)
{
  // Generate some random data.
  mat data;
  data.randn(15, 800);
  vec responses;
  responses.randn(800, 1);

  LinearRegression lr(data, responses, 0.05); // Train the model.
  LinearRegression xmlLr, textLr, binaryLr;

  SerializeObjectAll(lr, xmlLr, textLr, binaryLr);

  BOOST_REQUIRE_CLOSE(lr.Lambda(), xmlLr.Lambda(), 1e-8);
  BOOST_REQUIRE_CLOSE(lr.Lambda(), textLr.Lambda(), 1e-8);
  BOOST_REQUIRE_CLOSE(lr.Lambda(), binaryLr.Lambda(), 1e-8);

  CheckMatrices(lr.Parameters(), xmlLr.Parameters(), textLr.Parameters(),
      binaryLr.Parameters());
}

BOOST_AUTO_TEST_CASE(RegressionDistributionTest)
{
  // Generate some random data.
  mat data;
  data.randn(15, 800);
  vec responses;
  responses.randn(800, 1);

  RegressionDistribution rd(data, responses);
  RegressionDistribution xmlRd, textRd, binaryRd;

  // Okay, now save it and load it.
  SerializeObjectAll(rd, xmlRd, textRd, binaryRd);

  // Check the gaussian distribution.
  CheckMatrices(rd.Err().Mean(),
                xmlRd.Err().Mean(),
                textRd.Err().Mean(),
                binaryRd.Err().Mean());
  CheckMatrices(rd.Err().Covariance(),
                xmlRd.Err().Covariance(),
                textRd.Err().Covariance(),
                binaryRd.Err().Covariance());

  // Check the regression function.
  if (rd.Rf().Lambda() == 0.0)
  {
    BOOST_REQUIRE_SMALL(xmlRd.Rf().Lambda(), 1e-8);
    BOOST_REQUIRE_SMALL(textRd.Rf().Lambda(), 1e-8);
    BOOST_REQUIRE_SMALL(binaryRd.Rf().Lambda(), 1e-8);
  }
  else
  {
    BOOST_REQUIRE_CLOSE(rd.Rf().Lambda(), xmlRd.Rf().Lambda(), 1e-8);
    BOOST_REQUIRE_CLOSE(rd.Rf().Lambda(), textRd.Rf().Lambda(), 1e-8);
    BOOST_REQUIRE_CLOSE(rd.Rf().Lambda(), binaryRd.Rf().Lambda(), 1e-8);
  }

  CheckMatrices(rd.Rf().Parameters(),
                xmlRd.Rf().Parameters(),
                textRd.Rf().Parameters(),
                binaryRd.Rf().Parameters());
}

BOOST_AUTO_TEST_CASE(BallBoundTest)
{
  BallBound<> b(100);
  b.Center().randu();
  b.Radius() = 14.0;

  BallBound<> xmlB, textB, binaryB;

  SerializeObjectAll(b, xmlB, textB, binaryB);

  // Check the dimensionality.
  BOOST_REQUIRE_EQUAL(b.Dim(), xmlB.Dim());
  BOOST_REQUIRE_EQUAL(b.Dim(), textB.Dim());
  BOOST_REQUIRE_EQUAL(b.Dim(), binaryB.Dim());

  // Check the radius.
  BOOST_REQUIRE_CLOSE(b.Radius(), xmlB.Radius(), 1e-8);
  BOOST_REQUIRE_CLOSE(b.Radius(), textB.Radius(), 1e-8);
  BOOST_REQUIRE_CLOSE(b.Radius(), binaryB.Radius(), 1e-8);

  // Now check the vectors.
  CheckMatrices(b.Center(), xmlB.Center(), textB.Center(), binaryB.Center());
}

BOOST_AUTO_TEST_CASE(MahalanobisBallBoundTest)
{
  BallBound<arma::vec, MahalanobisDistance<>> b(100);
  b.Center().randu();
  b.Radius() = 14.0;
  b.Metric().Covariance().randu(100, 100);

  BallBound<arma::vec, MahalanobisDistance<>> xmlB, textB, binaryB;

  SerializeObjectAll(b, xmlB, textB, binaryB);

  // Check the radius.
  BOOST_REQUIRE_CLOSE(b.Radius(), xmlB.Radius(), 1e-8);
  BOOST_REQUIRE_CLOSE(b.Radius(), textB.Radius(), 1e-8);
  BOOST_REQUIRE_CLOSE(b.Radius(), binaryB.Radius(), 1e-8);

  // Check the vectors.
  CheckMatrices(b.Center(), xmlB.Center(), textB.Center(), binaryB.Center());
  CheckMatrices(b.Metric().Covariance(),
                xmlB.Metric().Covariance(),
                textB.Metric().Covariance(),
                binaryB.Metric().Covariance());
}

BOOST_AUTO_TEST_CASE(HRectBoundTest)
{
  HRectBound<> b(2);

  arma::mat points("0.0, 1.1; 5.0, 2.2");
  points = points.t();
  b |= points; // [0.0, 5.0]; [1.1, 2.2];

  HRectBound<> xmlB, textB, binaryB;

  SerializeObjectAll(b, xmlB, textB, binaryB);

  // Check the dimensionality.
  BOOST_REQUIRE_EQUAL(b.Dim(), xmlB.Dim());
  BOOST_REQUIRE_EQUAL(b.Dim(), textB.Dim());
  BOOST_REQUIRE_EQUAL(b.Dim(), binaryB.Dim());

  // Check the bounds.
  for (size_t i = 0; i < b.Dim(); ++i)
  {
    BOOST_REQUIRE_CLOSE(b[i].Lo(), xmlB[i].Lo(), 1e-8);
    BOOST_REQUIRE_CLOSE(b[i].Hi(), xmlB[i].Hi(), 1e-8);
    BOOST_REQUIRE_CLOSE(b[i].Lo(), textB[i].Lo(), 1e-8);
    BOOST_REQUIRE_CLOSE(b[i].Hi(), textB[i].Hi(), 1e-8);
    BOOST_REQUIRE_CLOSE(b[i].Lo(), binaryB[i].Lo(), 1e-8);
    BOOST_REQUIRE_CLOSE(b[i].Hi(), binaryB[i].Hi(), 1e-8);
  }

  // Check the minimum width.
  BOOST_REQUIRE_CLOSE(b.MinWidth(), xmlB.MinWidth(), 1e-8);
  BOOST_REQUIRE_CLOSE(b.MinWidth(), textB.MinWidth(), 1e-8);
  BOOST_REQUIRE_CLOSE(b.MinWidth(), binaryB.MinWidth(), 1e-8);
}

template<typename TreeType>
void CheckTrees(TreeType& tree,
                TreeType& xmlTree,
                TreeType& textTree,
                TreeType& binaryTree)
{
  const typename TreeType::Mat* dataset = &tree.Dataset();

  // Make sure that the data matrices are the same.
  if (tree.Parent() == NULL)
  {
    CheckMatrices(*dataset,
                  xmlTree.Dataset(),
                  textTree.Dataset(),
                  binaryTree.Dataset());

    // Also ensure that the other parents are null too.
    BOOST_REQUIRE_EQUAL(xmlTree.Parent(), (TreeType*) NULL);
    BOOST_REQUIRE_EQUAL(textTree.Parent(), (TreeType*) NULL);
    BOOST_REQUIRE_EQUAL(binaryTree.Parent(), (TreeType*) NULL);
  }

  // Make sure the number of children is the same.
  BOOST_REQUIRE_EQUAL(tree.NumChildren(), xmlTree.NumChildren());
  BOOST_REQUIRE_EQUAL(tree.NumChildren(), textTree.NumChildren());
  BOOST_REQUIRE_EQUAL(tree.NumChildren(), binaryTree.NumChildren());

  // Make sure the number of descendants is the same.
  BOOST_REQUIRE_EQUAL(tree.NumDescendants(), xmlTree.NumDescendants());
  BOOST_REQUIRE_EQUAL(tree.NumDescendants(), textTree.NumDescendants());
  BOOST_REQUIRE_EQUAL(tree.NumDescendants(), binaryTree.NumDescendants());

  // Make sure the number of points is the same.
  BOOST_REQUIRE_EQUAL(tree.NumPoints(), xmlTree.NumPoints());
  BOOST_REQUIRE_EQUAL(tree.NumPoints(), textTree.NumPoints());
  BOOST_REQUIRE_EQUAL(tree.NumPoints(), binaryTree.NumPoints());

  // Check that each point is the same.
  for (size_t i = 0; i < tree.NumPoints(); ++i)
  {
    BOOST_REQUIRE_EQUAL(tree.Point(i), xmlTree.Point(i));
    BOOST_REQUIRE_EQUAL(tree.Point(i), textTree.Point(i));
    BOOST_REQUIRE_EQUAL(tree.Point(i), binaryTree.Point(i));
  }

  // Check that the parent distance is the same.
  BOOST_REQUIRE_CLOSE(tree.ParentDistance(), xmlTree.ParentDistance(), 1e-8);
  BOOST_REQUIRE_CLOSE(tree.ParentDistance(), textTree.ParentDistance(), 1e-8);
  BOOST_REQUIRE_CLOSE(tree.ParentDistance(), binaryTree.ParentDistance(), 1e-8);

  // Check that the furthest descendant distance is the same.
  BOOST_REQUIRE_CLOSE(tree.FurthestDescendantDistance(),
      xmlTree.FurthestDescendantDistance(), 1e-8);
  BOOST_REQUIRE_CLOSE(tree.FurthestDescendantDistance(),
      textTree.FurthestDescendantDistance(), 1e-8);
  BOOST_REQUIRE_CLOSE(tree.FurthestDescendantDistance(),
      binaryTree.FurthestDescendantDistance(), 1e-8);

  // Check that the minimum bound distance is the same.
  BOOST_REQUIRE_CLOSE(tree.MinimumBoundDistance(),
      xmlTree.MinimumBoundDistance(), 1e-8);
  BOOST_REQUIRE_CLOSE(tree.MinimumBoundDistance(),
      textTree.MinimumBoundDistance(), 1e-8);
  BOOST_REQUIRE_CLOSE(tree.MinimumBoundDistance(),
      binaryTree.MinimumBoundDistance(), 1e-8);

  // Recurse into the children.
  for (size_t i = 0; i < tree.NumChildren(); ++i)
  {
    // Check that the child dataset is the same.
    BOOST_REQUIRE_EQUAL(&xmlTree.Dataset(), &xmlTree.Child(i).Dataset());
    BOOST_REQUIRE_EQUAL(&textTree.Dataset(), &textTree.Child(i).Dataset());
    BOOST_REQUIRE_EQUAL(&binaryTree.Dataset(), &binaryTree.Child(i).Dataset());

    // Make sure the parent link is right.
    BOOST_REQUIRE_EQUAL(xmlTree.Child(i).Parent(), &xmlTree);
    BOOST_REQUIRE_EQUAL(textTree.Child(i).Parent(), &textTree);
    BOOST_REQUIRE_EQUAL(binaryTree.Child(i).Parent(), &binaryTree);

    CheckTrees(tree.Child(i), xmlTree.Child(i), textTree.Child(i),
        binaryTree.Child(i));
  }
}

BOOST_AUTO_TEST_CASE(BinarySpaceTreeTest)
{
  arma::mat data;
  data.randu(3, 100);
  typedef KDTree<EuclideanDistance, EmptyStatistic, arma::mat> TreeType;
  TreeType tree(data);

  TreeType* xmlTree;
  TreeType* textTree;
  TreeType* binaryTree;

  SerializePointerObjectAll(&tree, xmlTree, textTree, binaryTree);

  CheckTrees(tree, *xmlTree, *textTree, *binaryTree);

  delete xmlTree;
  delete textTree;
  delete binaryTree;
}

BOOST_AUTO_TEST_CASE(BinarySpaceTreeOverwriteTest)
{
  arma::mat data;
  data.randu(3, 100);
  typedef KDTree<EuclideanDistance, EmptyStatistic, arma::mat> TreeType;
  TreeType tree(data);

  arma::mat otherData;
  otherData.randu(5, 50);
  TreeType xmlTree(otherData);
  TreeType textTree(xmlTree);
  TreeType binaryTree(xmlTree);

  SerializeObjectAll(tree, xmlTree, textTree, binaryTree);

  CheckTrees(tree, xmlTree, textTree, binaryTree);
}

BOOST_AUTO_TEST_CASE(CoverTreeTest)
{
  arma::mat data;
  data.randu(3, 100);
  typedef StandardCoverTree<EuclideanDistance, EmptyStatistic, arma::mat>
      TreeType;
  TreeType tree(data);

  TreeType* xmlTree;
  TreeType* textTree;
  TreeType* binaryTree;

  SerializePointerObjectAll(&tree, xmlTree, textTree, binaryTree);

  CheckTrees(tree, *xmlTree, *textTree, *binaryTree);

  // Also check a few other things.
  std::stack<TreeType*> stack, xmlStack, textStack, binaryStack;
  stack.push(&tree);
  xmlStack.push(xmlTree);
  textStack.push(textTree);
  binaryStack.push(binaryTree);
  while (!stack.empty())
  {
    TreeType* node = stack.top();
    TreeType* xmlNode = xmlStack.top();
    TreeType* textNode = textStack.top();
    TreeType* binaryNode = binaryStack.top();
    stack.pop();
    xmlStack.pop();
    textStack.pop();
    binaryStack.pop();

    BOOST_REQUIRE_EQUAL(node->Scale(), xmlNode->Scale());
    BOOST_REQUIRE_EQUAL(node->Scale(), textNode->Scale());
    BOOST_REQUIRE_EQUAL(node->Scale(), binaryNode->Scale());

    BOOST_REQUIRE_CLOSE(node->Base(), xmlNode->Base(), 1e-5);
    BOOST_REQUIRE_CLOSE(node->Base(), textNode->Base(), 1e-5);
    BOOST_REQUIRE_CLOSE(node->Base(), binaryNode->Base(), 1e-5);

    for (size_t i = 0; i < node->NumChildren(); ++i)
    {
      stack.push(&node->Child(i));
      xmlStack.push(&xmlNode->Child(i));
      textStack.push(&textNode->Child(i));
      binaryStack.push(&binaryNode->Child(i));
    }
  }

  delete xmlTree;
  delete textTree;
  delete binaryTree;
}

BOOST_AUTO_TEST_CASE(CoverTreeOverwriteTest)
{
  arma::mat data;
  data.randu(3, 100);
  typedef StandardCoverTree<EuclideanDistance, EmptyStatistic, arma::mat>
      TreeType;
  TreeType tree(data);

  arma::mat otherData;
  otherData.randu(5, 50);
  TreeType xmlTree(otherData);
  TreeType textTree(xmlTree);
  TreeType binaryTree(xmlTree);

  SerializeObjectAll(tree, xmlTree, textTree, binaryTree);

  CheckTrees(tree, xmlTree, textTree, binaryTree);

  // Also check a few other things.
  std::stack<TreeType*> stack, xmlStack, textStack, binaryStack;
  stack.push(&tree);
  xmlStack.push(&xmlTree);
  textStack.push(&textTree);
  binaryStack.push(&binaryTree);
  while (!stack.empty())
  {
    TreeType* node = stack.top();
    TreeType* xmlNode = xmlStack.top();
    TreeType* textNode = textStack.top();
    TreeType* binaryNode = binaryStack.top();
    stack.pop();
    xmlStack.pop();
    textStack.pop();
    binaryStack.pop();

    BOOST_REQUIRE_EQUAL(node->Scale(), xmlNode->Scale());
    BOOST_REQUIRE_EQUAL(node->Scale(), textNode->Scale());
    BOOST_REQUIRE_EQUAL(node->Scale(), binaryNode->Scale());

    BOOST_REQUIRE_CLOSE(node->Base(), xmlNode->Base(), 1e-5);
    BOOST_REQUIRE_CLOSE(node->Base(), textNode->Base(), 1e-5);
    BOOST_REQUIRE_CLOSE(node->Base(), binaryNode->Base(), 1e-5);

    for (size_t i = 0; i < node->NumChildren(); ++i)
    {
      stack.push(&node->Child(i));
      xmlStack.push(&xmlNode->Child(i));
      textStack.push(&textNode->Child(i));
      binaryStack.push(&binaryNode->Child(i));
    }
  }
}

BOOST_AUTO_TEST_CASE(RectangleTreeTest)
{
  arma::mat data;
  data.randu(3, 1000);
  typedef RTree<EuclideanDistance, EmptyStatistic, arma::mat> TreeType;
  TreeType tree(data);

  TreeType* xmlTree;
  TreeType* textTree;
  TreeType* binaryTree;

  SerializePointerObjectAll(&tree, xmlTree, textTree, binaryTree);

  CheckTrees(tree, *xmlTree, *textTree, *binaryTree);

  // Check a few other things too.
  std::stack<TreeType*> stack, xmlStack, textStack, binaryStack;
  stack.push(&tree);
  xmlStack.push(xmlTree);
  textStack.push(textTree);
  binaryStack.push(binaryTree);
  while (!stack.empty())
  {
    // Check more things...
    TreeType* node = stack.top();
    TreeType* xmlNode = xmlStack.top();
    TreeType* textNode = textStack.top();
    TreeType* binaryNode = binaryStack.top();
    stack.pop();
    xmlStack.pop();
    textStack.pop();
    binaryStack.pop();

    BOOST_REQUIRE_EQUAL(node->MaxLeafSize(), xmlNode->MaxLeafSize());
    BOOST_REQUIRE_EQUAL(node->MaxLeafSize(), textNode->MaxLeafSize());
    BOOST_REQUIRE_EQUAL(node->MaxLeafSize(), binaryNode->MaxLeafSize());

    BOOST_REQUIRE_EQUAL(node->MinLeafSize(), xmlNode->MinLeafSize());
    BOOST_REQUIRE_EQUAL(node->MinLeafSize(), textNode->MinLeafSize());
    BOOST_REQUIRE_EQUAL(node->MinLeafSize(), binaryNode->MinLeafSize());

    BOOST_REQUIRE_EQUAL(node->MaxNumChildren(), xmlNode->MaxNumChildren());
    BOOST_REQUIRE_EQUAL(node->MaxNumChildren(), textNode->MaxNumChildren());
    BOOST_REQUIRE_EQUAL(node->MaxNumChildren(), binaryNode->MaxNumChildren());

    BOOST_REQUIRE_EQUAL(node->MinNumChildren(), xmlNode->MinNumChildren());
    BOOST_REQUIRE_EQUAL(node->MinNumChildren(), textNode->MinNumChildren());
    BOOST_REQUIRE_EQUAL(node->MinNumChildren(), binaryNode->MinNumChildren());
  }

  delete xmlTree;
  delete textTree;
  delete binaryTree;
}

BOOST_AUTO_TEST_CASE(RectangleTreeOverwriteTest)
{
  arma::mat data;
  data.randu(3, 1000);
  typedef RTree<EuclideanDistance, EmptyStatistic, arma::mat> TreeType;
  TreeType tree(data);

  arma::mat otherData;
  otherData.randu(5, 50);
  TreeType xmlTree(otherData);
  TreeType textTree(otherData);
  TreeType binaryTree(textTree);

  SerializeObjectAll(tree, xmlTree, textTree, binaryTree);

  CheckTrees(tree, xmlTree, textTree, binaryTree);

  // Check a few other things too.
  std::stack<TreeType*> stack, xmlStack, textStack, binaryStack;
  stack.push(&tree);
  xmlStack.push(&xmlTree);
  textStack.push(&textTree);
  binaryStack.push(&binaryTree);
  while (!stack.empty())
  {
    // Check more things...
    TreeType* node = stack.top();
    TreeType* xmlNode = xmlStack.top();
    TreeType* textNode = textStack.top();
    TreeType* binaryNode = binaryStack.top();
    stack.pop();
    xmlStack.pop();
    textStack.pop();
    binaryStack.pop();

    BOOST_REQUIRE_EQUAL(node->MaxLeafSize(), xmlNode->MaxLeafSize());
    BOOST_REQUIRE_EQUAL(node->MaxLeafSize(), textNode->MaxLeafSize());
    BOOST_REQUIRE_EQUAL(node->MaxLeafSize(), binaryNode->MaxLeafSize());

    BOOST_REQUIRE_EQUAL(node->MinLeafSize(), xmlNode->MinLeafSize());
    BOOST_REQUIRE_EQUAL(node->MinLeafSize(), textNode->MinLeafSize());
    BOOST_REQUIRE_EQUAL(node->MinLeafSize(), binaryNode->MinLeafSize());

    BOOST_REQUIRE_EQUAL(node->MaxNumChildren(), xmlNode->MaxNumChildren());
    BOOST_REQUIRE_EQUAL(node->MaxNumChildren(), textNode->MaxNumChildren());
    BOOST_REQUIRE_EQUAL(node->MaxNumChildren(), binaryNode->MaxNumChildren());

    BOOST_REQUIRE_EQUAL(node->MinNumChildren(), xmlNode->MinNumChildren());
    BOOST_REQUIRE_EQUAL(node->MinNumChildren(), textNode->MinNumChildren());
    BOOST_REQUIRE_EQUAL(node->MinNumChildren(), binaryNode->MinNumChildren());
  }
}

BOOST_AUTO_TEST_CASE(PerceptronTest)
{
  // Create a perceptron.  Train it randomly.  Then check that it hasn't
  // changed.
  arma::mat data;
  data.randu(3, 100);
  arma::Row<size_t> labels(100);
  for (size_t i = 0; i < labels.n_elem; ++i)
  {
    if (data(1, i) > 0.5)
      labels[i] = 0;
    else
      labels[i] = 1;
  }

  Perceptron<> p(data, labels, 2, 15);

  Perceptron<> pXml(2, 3), pText(2, 3), pBinary(2, 3);
  SerializeObjectAll(p, pXml, pText, pBinary);

  // Now check that things are the same.
  CheckMatrices(p.Weights(), pXml.Weights(), pText.Weights(),
      pBinary.Weights());
  CheckMatrices(p.Biases(), pXml.Biases(), pText.Biases(), pBinary.Biases());

  BOOST_REQUIRE_EQUAL(p.MaxIterations(), pXml.MaxIterations());
  BOOST_REQUIRE_EQUAL(p.MaxIterations(), pText.MaxIterations());
  BOOST_REQUIRE_EQUAL(p.MaxIterations(), pBinary.MaxIterations());
}

BOOST_AUTO_TEST_CASE(LogisticRegressionTest)
{
  arma::mat data;
  data.randu(3, 100);
  arma::Row<size_t> responses;
  responses.randu(100);

  LogisticRegression<> lr(data, responses, 0.5);

  LogisticRegression<> lrXml(data, responses + 3, 0.3);
  LogisticRegression<> lrText(data, responses + 1);
  LogisticRegression<> lrBinary(3, 0.0);

  SerializeObjectAll(lr, lrXml, lrText, lrBinary);

  CheckMatrices(lr.Parameters(), lrXml.Parameters(), lrText.Parameters(),
      lrBinary.Parameters());

  BOOST_REQUIRE_CLOSE(lr.Lambda(), lrXml.Lambda(), 1e-5);
  BOOST_REQUIRE_CLOSE(lr.Lambda(), lrText.Lambda(), 1e-5);
  BOOST_REQUIRE_CLOSE(lr.Lambda(), lrBinary.Lambda(), 1e-5);
}

BOOST_AUTO_TEST_CASE(AllkNNTest)
{
  using neighbor::AllkNN;
  arma::mat dataset = arma::randu<arma::mat>(5, 2000);

  AllkNN allknn(dataset, false, false);

  AllkNN knnXml, knnText, knnBinary;

  SerializeObjectAll(allknn, knnXml, knnText, knnBinary);

  // Now run nearest neighbor and make sure the results are the same.
  arma::mat querySet = arma::randu<arma::mat>(5, 1000);

  arma::mat distances, xmlDistances, textDistances, binaryDistances;
  arma::Mat<size_t> neighbors, xmlNeighbors, textNeighbors, binaryNeighbors;

  allknn.Search(querySet, 5, neighbors, distances);
  knnXml.Search(querySet, 5, xmlNeighbors, xmlDistances);
  knnText.Search(querySet, 5, textNeighbors, textDistances);
  knnBinary.Search(querySet, 5, binaryNeighbors, binaryDistances);

  CheckMatrices(distances, xmlDistances, textDistances, binaryDistances);
  CheckMatrices(neighbors, xmlNeighbors, textNeighbors, binaryNeighbors);
}

BOOST_AUTO_TEST_CASE(SoftmaxRegressionTest)
{
  using regression::SoftmaxRegression;

  arma::mat dataset = arma::randu<arma::mat>(5, 1000);
  arma::Row<size_t> labels(1000);
  for (size_t i = 0; i < 500; ++i)
    labels[i] = 0;
  for (size_t i = 500; i < 1000; ++i)
    labels[i] = 1;

  SoftmaxRegression<> sr(dataset, labels, 2);

  SoftmaxRegression<> srXml(dataset.n_rows, 2);
  SoftmaxRegression<> srText(dataset.n_rows, 2);
  SoftmaxRegression<> srBinary(dataset.n_rows, 2);

  SerializeObjectAll(sr, srXml, srText, srBinary);

  CheckMatrices(sr.Parameters(), srXml.Parameters(), srText.Parameters(),
      srBinary.Parameters());
}

BOOST_AUTO_TEST_CASE(DETTest)
{
  using det::DTree;

  // Create a density estimation tree on a random dataset.
  arma::mat dataset = arma::randu<arma::mat>(25, 5000);

  DTree tree(dataset);

  arma::mat otherDataset = arma::randu<arma::mat>(5, 100);
  DTree xmlTree, binaryTree, textTree(otherDataset);

  SerializeObjectAll(tree, xmlTree, binaryTree, textTree);

  std::stack<DTree*> stack, xmlStack, binaryStack, textStack;
  stack.push(&tree);
  xmlStack.push(&xmlTree);
  binaryStack.push(&binaryTree);
  textStack.push(&textTree);

  while (!stack.empty())
  {
    // Get the top node from the stack.
    DTree* node = stack.top();
    DTree* xmlNode = xmlStack.top();
    DTree* binaryNode = binaryStack.top();
    DTree* textNode = textStack.top();

    stack.pop();
    xmlStack.pop();
    binaryStack.pop();
    textStack.pop();

    // Check that all the members are the same.
    BOOST_REQUIRE_EQUAL(node->Start(), xmlNode->Start());
    BOOST_REQUIRE_EQUAL(node->Start(), binaryNode->Start());
    BOOST_REQUIRE_EQUAL(node->Start(), textNode->Start());

    BOOST_REQUIRE_EQUAL(node->End(), xmlNode->End());
    BOOST_REQUIRE_EQUAL(node->End(), binaryNode->End());
    BOOST_REQUIRE_EQUAL(node->End(), textNode->End());

    BOOST_REQUIRE_EQUAL(node->SplitDim(), xmlNode->SplitDim());
    BOOST_REQUIRE_EQUAL(node->SplitDim(), binaryNode->SplitDim());
    BOOST_REQUIRE_EQUAL(node->SplitDim(), textNode->SplitDim());

    if (std::abs(node->SplitValue()) < 1e-5)
    {
      BOOST_REQUIRE_SMALL(xmlNode->SplitValue(), 1e-5);
      BOOST_REQUIRE_SMALL(binaryNode->SplitValue(), 1e-5);
      BOOST_REQUIRE_SMALL(textNode->SplitValue(), 1e-5);
    }
    else
    {
      BOOST_REQUIRE_CLOSE(node->SplitValue(), xmlNode->SplitValue(), 1e-5);
      BOOST_REQUIRE_CLOSE(node->SplitValue(), binaryNode->SplitValue(), 1e-5);
      BOOST_REQUIRE_CLOSE(node->SplitValue(), textNode->SplitValue(), 1e-5);
    }

    if (std::abs(node->LogNegError()) < 1e-5)
    {
      BOOST_REQUIRE_SMALL(xmlNode->LogNegError(), 1e-5);
      BOOST_REQUIRE_SMALL(binaryNode->LogNegError(), 1e-5);
      BOOST_REQUIRE_SMALL(textNode->LogNegError(), 1e-5);
    }
    else
    {
      BOOST_REQUIRE_CLOSE(node->LogNegError(), xmlNode->LogNegError(), 1e-5);
      BOOST_REQUIRE_CLOSE(node->LogNegError(), binaryNode->LogNegError(), 1e-5);
      BOOST_REQUIRE_CLOSE(node->LogNegError(), textNode->LogNegError(), 1e-5);
    }

    if (std::abs(node->SubtreeLeavesLogNegError()) < 1e-5)
    {
      BOOST_REQUIRE_SMALL(xmlNode->SubtreeLeavesLogNegError(), 1e-5);
      BOOST_REQUIRE_SMALL(binaryNode->SubtreeLeavesLogNegError(), 1e-5);
      BOOST_REQUIRE_SMALL(textNode->SubtreeLeavesLogNegError(), 1e-5);
    }
    else
    {
      BOOST_REQUIRE_CLOSE(node->SubtreeLeavesLogNegError(),
          xmlNode->SubtreeLeavesLogNegError(), 1e-5);
      BOOST_REQUIRE_CLOSE(node->SubtreeLeavesLogNegError(),
          binaryNode->SubtreeLeavesLogNegError(), 1e-5);
      BOOST_REQUIRE_CLOSE(node->SubtreeLeavesLogNegError(),
          textNode->SubtreeLeavesLogNegError(), 1e-5);
    }

    BOOST_REQUIRE_EQUAL(node->SubtreeLeaves(), xmlNode->SubtreeLeaves());
    BOOST_REQUIRE_EQUAL(node->SubtreeLeaves(), binaryNode->SubtreeLeaves());
    BOOST_REQUIRE_EQUAL(node->SubtreeLeaves(), textNode->SubtreeLeaves());

    if (std::abs(node->Ratio()) < 1e-5)
    {
      BOOST_REQUIRE_SMALL(xmlNode->Ratio(), 1e-5);
      BOOST_REQUIRE_SMALL(binaryNode->Ratio(), 1e-5);
      BOOST_REQUIRE_SMALL(textNode->Ratio(), 1e-5);
    }
    else
    {
      BOOST_REQUIRE_CLOSE(node->Ratio(), xmlNode->Ratio(), 1e-5);
      BOOST_REQUIRE_CLOSE(node->Ratio(), binaryNode->Ratio(), 1e-5);
      BOOST_REQUIRE_CLOSE(node->Ratio(), textNode->Ratio(), 1e-5);
    }

    if (std::abs(node->LogVolume()) < 1e-5)
    {
      BOOST_REQUIRE_SMALL(xmlNode->LogVolume(), 1e-5);
      BOOST_REQUIRE_SMALL(binaryNode->LogVolume(), 1e-5);
      BOOST_REQUIRE_SMALL(textNode->LogVolume(), 1e-5);
    }
    else
    {
      BOOST_REQUIRE_CLOSE(node->LogVolume(), xmlNode->LogVolume(), 1e-5);
      BOOST_REQUIRE_CLOSE(node->LogVolume(), binaryNode->LogVolume(), 1e-5);
      BOOST_REQUIRE_CLOSE(node->LogVolume(), textNode->LogVolume(), 1e-5);
    }

    if (node->Left() == NULL)
    {
      BOOST_REQUIRE(xmlNode->Left() == NULL);
      BOOST_REQUIRE(binaryNode->Left() == NULL);
      BOOST_REQUIRE(textNode->Left() == NULL);
    }
    else
    {
      BOOST_REQUIRE(xmlNode->Left() != NULL);
      BOOST_REQUIRE(binaryNode->Left() != NULL);
      BOOST_REQUIRE(textNode->Left() != NULL);

      // Push children onto stack.
      stack.push(node->Left());
      xmlStack.push(xmlNode->Left());
      binaryStack.push(binaryNode->Left());
      textStack.push(textNode->Left());
    }

    if (node->Right() == NULL)
    {
      BOOST_REQUIRE(xmlNode->Right() == NULL);
      BOOST_REQUIRE(binaryNode->Right() == NULL);
      BOOST_REQUIRE(textNode->Right() == NULL);
    }
    else
    {
      BOOST_REQUIRE(xmlNode->Right() != NULL);
      BOOST_REQUIRE(binaryNode->Right() != NULL);
      BOOST_REQUIRE(textNode->Right() != NULL);

      // Push children onto stack.
      stack.push(node->Right());
      xmlStack.push(xmlNode->Right());
      binaryStack.push(binaryNode->Right());
      textStack.push(textNode->Right());
    }

    BOOST_REQUIRE_EQUAL(node->Root(), xmlNode->Root());
    BOOST_REQUIRE_EQUAL(node->Root(), binaryNode->Root());
    BOOST_REQUIRE_EQUAL(node->Root(), textNode->Root());

    if (std::abs(node->AlphaUpper()) < 1e-5)
    {
      BOOST_REQUIRE_SMALL(xmlNode->AlphaUpper(), 1e-5);
      BOOST_REQUIRE_SMALL(binaryNode->AlphaUpper(), 1e-5);
      BOOST_REQUIRE_SMALL(textNode->AlphaUpper(), 1e-5);
    }
    else
    {
      BOOST_REQUIRE_CLOSE(node->AlphaUpper(), xmlNode->AlphaUpper(), 1e-5);
      BOOST_REQUIRE_CLOSE(node->AlphaUpper(), binaryNode->AlphaUpper(), 1e-5);
      BOOST_REQUIRE_CLOSE(node->AlphaUpper(), textNode->AlphaUpper(), 1e-5);
    }

    BOOST_REQUIRE_EQUAL(node->MaxVals().n_elem, xmlNode->MaxVals().n_elem);
    BOOST_REQUIRE_EQUAL(node->MaxVals().n_elem, binaryNode->MaxVals().n_elem);
    BOOST_REQUIRE_EQUAL(node->MaxVals().n_elem, textNode->MaxVals().n_elem);
    for (size_t i = 0; i < node->MaxVals().n_elem; ++i)
    {
      if (std::abs(node->MaxVals()[i]) < 1e-5)
      {
        BOOST_REQUIRE_SMALL(xmlNode->MaxVals()[i], 1e-5);
        BOOST_REQUIRE_SMALL(binaryNode->MaxVals()[i], 1e-5);
        BOOST_REQUIRE_SMALL(textNode->MaxVals()[i], 1e-5);
      }
      else
      {
        BOOST_REQUIRE_CLOSE(node->MaxVals()[i], xmlNode->MaxVals()[i], 1e-5);
        BOOST_REQUIRE_CLOSE(node->MaxVals()[i], binaryNode->MaxVals()[i], 1e-5);
        BOOST_REQUIRE_CLOSE(node->MaxVals()[i], textNode->MaxVals()[i], 1e-5);
      }
    }

    BOOST_REQUIRE_EQUAL(node->MinVals().n_elem, xmlNode->MinVals().n_elem);
    BOOST_REQUIRE_EQUAL(node->MinVals().n_elem, binaryNode->MinVals().n_elem);
    BOOST_REQUIRE_EQUAL(node->MinVals().n_elem, textNode->MinVals().n_elem);
    for (size_t i = 0; i < node->MinVals().n_elem; ++i)
    {
      if (std::abs(node->MinVals()[i]) < 1e-5)
      {
        BOOST_REQUIRE_SMALL(xmlNode->MinVals()[i], 1e-5);
        BOOST_REQUIRE_SMALL(binaryNode->MinVals()[i], 1e-5);
        BOOST_REQUIRE_SMALL(textNode->MinVals()[i], 1e-5);
      }
      else
      {
        BOOST_REQUIRE_CLOSE(node->MinVals()[i], xmlNode->MinVals()[i], 1e-5);
        BOOST_REQUIRE_CLOSE(node->MinVals()[i], binaryNode->MinVals()[i], 1e-5);
        BOOST_REQUIRE_CLOSE(node->MinVals()[i], textNode->MinVals()[i], 1e-5);
      }
    }
  }
}

BOOST_AUTO_TEST_CASE(NaiveBayesSerializationTest)
{
  // Train NBC randomly.  Make sure the model is the same after serializing and
  // re-loading.
  arma::mat dataset;
  dataset.randu(10, 500);
  arma::Row<size_t> labels(500);
  for (size_t i = 0; i < 500; ++i)
  {
    if (dataset(0, i) > 0.5)
      labels[i] = 0;
    else
      labels[i] = 1;
  }

  NaiveBayesClassifier<> nbc(dataset, labels, 2);

  // Initialize some empty Naive Bayes classifiers.
  NaiveBayesClassifier<> xmlNbc(0, 0), textNbc(0, 0), binaryNbc(0, 0);
  SerializeObjectAll(nbc, xmlNbc, textNbc, binaryNbc);

  BOOST_REQUIRE_EQUAL(nbc.Means().n_elem, xmlNbc.Means().n_elem);
  BOOST_REQUIRE_EQUAL(nbc.Means().n_elem, textNbc.Means().n_elem);
  BOOST_REQUIRE_EQUAL(nbc.Means().n_elem, binaryNbc.Means().n_elem);
  for (size_t i = 0; i < nbc.Means().n_elem; ++i)
  {
    BOOST_REQUIRE_CLOSE(nbc.Means()[i], xmlNbc.Means()[i], 1e-5);
    BOOST_REQUIRE_CLOSE(nbc.Means()[i], textNbc.Means()[i], 1e-5);
    BOOST_REQUIRE_CLOSE(nbc.Means()[i], binaryNbc.Means()[i], 1e-5);
  }

  BOOST_REQUIRE_EQUAL(nbc.Variances().n_elem, xmlNbc.Variances().n_elem);
  BOOST_REQUIRE_EQUAL(nbc.Variances().n_elem, textNbc.Variances().n_elem);
  BOOST_REQUIRE_EQUAL(nbc.Variances().n_elem, binaryNbc.Variances().n_elem);
  for (size_t i = 0; i < nbc.Variances().n_elem; ++i)
  {
    BOOST_REQUIRE_CLOSE(nbc.Variances()[i], xmlNbc.Variances()[i], 1e-5);
    BOOST_REQUIRE_CLOSE(nbc.Variances()[i], textNbc.Variances()[i], 1e-5);
    BOOST_REQUIRE_CLOSE(nbc.Variances()[i], binaryNbc.Variances()[i], 1e-5);
  }

  BOOST_REQUIRE_EQUAL(nbc.Probabilities().n_elem,
      xmlNbc.Probabilities().n_elem);
  BOOST_REQUIRE_EQUAL(nbc.Probabilities().n_elem,
      textNbc.Probabilities().n_elem);
  BOOST_REQUIRE_EQUAL(nbc.Probabilities().n_elem,
      binaryNbc.Probabilities().n_elem);
  for (size_t i = 0; i < nbc.Probabilities().n_elem; ++i)
  {
    BOOST_REQUIRE_CLOSE(nbc.Probabilities()[i], xmlNbc.Probabilities()[i],
        1e-5);
    BOOST_REQUIRE_CLOSE(nbc.Probabilities()[i], textNbc.Probabilities()[i],
        1e-5);
    BOOST_REQUIRE_CLOSE(nbc.Probabilities()[i], binaryNbc.Probabilities()[i],
        1e-5);
  }
}

BOOST_AUTO_TEST_CASE(RASearchTest)
{
  using neighbor::AllkRANN;
  using neighbor::AllkNN;
  arma::mat dataset = arma::randu<arma::mat>(5, 200);
  arma::mat otherDataset = arma::randu<arma::mat>(5, 100);

  // Find nearest neighbors in the top 10, with accuracy 0.95.  So 95% of the
  // results we get (at least) should fall into the top 10 of the true nearest
  // neighbors.
  AllkRANN allkrann(dataset, false, false, 5, 0.95);

  AllkRANN krannXml(otherDataset, false, false);
  AllkRANN krannText(otherDataset, true, false);
  AllkRANN krannBinary(otherDataset, true, true);

  SerializeObjectAll(allkrann, krannXml, krannText, krannBinary);

  // Now run nearest neighbor and make sure the results are the same.
  arma::mat querySet = arma::randu<arma::mat>(5, 100);

  arma::mat distances, xmlDistances, textDistances, binaryDistances;
  arma::Mat<size_t> neighbors, xmlNeighbors, textNeighbors, binaryNeighbors;

  AllkNN allknn(dataset); // Exact search.
  allknn.Search(querySet, 10, neighbors, distances);
  krannXml.Search(querySet, 5, xmlNeighbors, xmlDistances);
  krannText.Search(querySet, 5, textNeighbors, textDistances);
  krannBinary.Search(querySet, 5, binaryNeighbors, binaryDistances);

  BOOST_REQUIRE_EQUAL(xmlNeighbors.n_rows, 5);
  BOOST_REQUIRE_EQUAL(xmlNeighbors.n_cols, 100);
  BOOST_REQUIRE_EQUAL(textNeighbors.n_rows, 5);
  BOOST_REQUIRE_EQUAL(textNeighbors.n_cols, 100);
  BOOST_REQUIRE_EQUAL(binaryNeighbors.n_rows, 5);
  BOOST_REQUIRE_EQUAL(binaryNeighbors.n_cols, 100);

  size_t xmlCorrect = 0;
  size_t textCorrect = 0;
  size_t binaryCorrect = 0;
  for (size_t i = 0; i < xmlNeighbors.n_cols; ++i)
  {
    // See how many are in the top 10.
    for (size_t j = 0; j < xmlNeighbors.n_rows; ++j)
    {
      for (size_t k = 0; k < neighbors.n_rows; ++k)
      {
        if (neighbors(k, i) == xmlNeighbors(j, i))
          xmlCorrect++;
        if (neighbors(k, i) == textNeighbors(j, i))
          textCorrect++;
        if (neighbors(k, i) == binaryNeighbors(j, i))
          binaryCorrect++;
      }
    }
  }

  // We need 95% of these to be correct.
  BOOST_REQUIRE_GT(xmlCorrect, 95 * 5);
  BOOST_REQUIRE_GT(binaryCorrect, 95 * 5);
  BOOST_REQUIRE_GT(textCorrect, 95 * 5);
}

/**
 * Test that an LSH model can be serialized and deserialized.
 */
BOOST_AUTO_TEST_CASE(LSHTest)
{
  // Since we still don't have good tests for LSH, basically what we're going to
  // do is serialize an LSH model, and make sure we can deserialize it and that
  // we still get results when we call Search().
  arma::mat referenceData = arma::randu<arma::mat>(10, 100);

  LSHSearch<> lsh(referenceData, 5, 10); // Arbitrary chosen parameters.

  LSHSearch<> xmlLsh;
  arma::mat textData = arma::randu<arma::mat>(5, 50);
  LSHSearch<> textLsh(textData, 4, 5);
  LSHSearch<> binaryLsh(referenceData, 15, 2);

  // Now serialize.
  SerializeObjectAll(lsh, xmlLsh, textLsh, binaryLsh);

  // Check what we can about the serialized objects.
  BOOST_REQUIRE_EQUAL(lsh.NumProjections(), xmlLsh.NumProjections());
  BOOST_REQUIRE_EQUAL(lsh.NumProjections(), textLsh.NumProjections());
  BOOST_REQUIRE_EQUAL(lsh.NumProjections(), binaryLsh.NumProjections());
  for (size_t i = 0; i < lsh.NumProjections(); ++i)
  {
    CheckMatrices(lsh.Projection(i), xmlLsh.Projection(i),
        textLsh.Projection(i), binaryLsh.Projection(i));
  }

  CheckMatrices(lsh.ReferenceSet(), xmlLsh.ReferenceSet(),
      textLsh.ReferenceSet(), binaryLsh.ReferenceSet());
  CheckMatrices(lsh.Offsets(), xmlLsh.Offsets(), textLsh.Offsets(),
      binaryLsh.Offsets());
  CheckMatrices(lsh.SecondHashWeights(), xmlLsh.SecondHashWeights(),
      textLsh.SecondHashWeights(), binaryLsh.SecondHashWeights());

  BOOST_REQUIRE_EQUAL(lsh.BucketSize(), xmlLsh.BucketSize());
  BOOST_REQUIRE_EQUAL(lsh.BucketSize(), textLsh.BucketSize());
  BOOST_REQUIRE_EQUAL(lsh.BucketSize(), binaryLsh.BucketSize());

  CheckMatrices(lsh.SecondHashTable(), xmlLsh.SecondHashTable(),
      textLsh.SecondHashTable(), binaryLsh.SecondHashTable());
}

// Make sure serialization works for the decision stump.
BOOST_AUTO_TEST_CASE(DecisionStumpTest)
{
  // Generate dataset.
  arma::mat trainingData = arma::randu<arma::mat>(4, 100);
  arma::Row<size_t> labels(100);
  for (size_t i = 0; i < 25; ++i)
    labels[i] = 0;
  for (size_t i = 25; i < 50; ++i)
    labels[i] = 3;
  for (size_t i = 50; i < 75; ++i)
    labels[i] = 1;
  for (size_t i = 75; i < 100; ++i)
    labels[i] = 2;

  DecisionStump<> ds(trainingData, labels, 4, 3);

  arma::mat otherData = arma::randu<arma::mat>(3, 100);
  arma::Row<size_t> otherLabels = arma::randu<arma::Row<size_t>>(100);
  DecisionStump<> xmlDs(otherData, otherLabels, 2, 3);

  DecisionStump<> textDs;
  DecisionStump<> binaryDs(trainingData, labels, 4, 10);

  SerializeObjectAll(ds, xmlDs, textDs, binaryDs);

  // Make sure that everything is the same about the new decision stumps.
  BOOST_REQUIRE_EQUAL(ds.SplitDimension(), xmlDs.SplitDimension());
  BOOST_REQUIRE_EQUAL(ds.SplitDimension(), textDs.SplitDimension());
  BOOST_REQUIRE_EQUAL(ds.SplitDimension(), binaryDs.SplitDimension());

  CheckMatrices(ds.Split(), xmlDs.Split(), textDs.Split(), binaryDs.Split());
  CheckMatrices(ds.BinLabels(), xmlDs.BinLabels(), textDs.BinLabels(),
      binaryDs.BinLabels());
}

// Make sure serialization works for LARS.
BOOST_AUTO_TEST_CASE(LARSTest)
{
  using namespace mlpack::regression;

  // Create a dataset.
  arma::mat X = arma::randn(75, 250);
  arma::vec beta = arma::randn(75, 1);
  arma::vec y = trans(X) * beta;

  LARS lars(true, 0.1, 0.1);
  arma::vec betaOpt;
  lars.Train(X, y, betaOpt);

  // Now, serialize.
  LARS xmlLars(false, 0.5, 0.0), binaryLars(true, 1.0, 0.0),
      textLars(false, 0.1, 0.1);

  // Train textLars.
  arma::mat textX = arma::randn(25, 150);
  arma::vec textBeta = arma::randn(25, 1);
  arma::vec textY = trans(textX) * textBeta;
  arma::vec textBetaOpt;
  textLars.Train(textX, textY, textBetaOpt);

  SerializeObjectAll(lars, xmlLars, binaryLars, textLars);

  // Now, check that predictions are the same.
  arma::vec pred, xmlPred, textPred, binaryPred;
  lars.Predict(X, pred);
  xmlLars.Predict(X, xmlPred);
  textLars.Predict(X, textPred);
  binaryLars.Predict(X, binaryPred);

  CheckMatrices(pred, xmlPred, textPred, binaryPred);
}

/**
 * Test serialization of the HoeffdingNumericSplit object after binning has
 * occured.
 */
BOOST_AUTO_TEST_CASE(HoeffdingNumericSplitTest)
{
  using namespace mlpack::tree;

  HoeffdingNumericSplit<GiniImpurity> split(3);
  // Train until it bins.
  for (size_t i = 0; i < 200; ++i)
    split.Train(mlpack::math::Random(), mlpack::math::RandInt(3));

  HoeffdingNumericSplit<GiniImpurity> xmlSplit(5);
  HoeffdingNumericSplit<GiniImpurity> textSplit(7);
  for (size_t i = 0; i < 200; ++i)
    textSplit.Train(mlpack::math::Random() + 3, 0);
  HoeffdingNumericSplit<GiniImpurity> binarySplit(2);

  SerializeObjectAll(split, xmlSplit, textSplit, binarySplit);

  // Ensure that everything is the same.
  BOOST_REQUIRE_EQUAL(split.Bins(), xmlSplit.Bins());
  BOOST_REQUIRE_EQUAL(split.Bins(), textSplit.Bins());
  BOOST_REQUIRE_EQUAL(split.Bins(), binarySplit.Bins());

  double bestSplit, secondBestSplit;
  double baseBestSplit, baseSecondBestSplit;
  split.EvaluateFitnessFunction(baseBestSplit, baseSecondBestSplit);
  xmlSplit.EvaluateFitnessFunction(bestSplit, secondBestSplit);
  BOOST_REQUIRE_CLOSE(bestSplit, baseBestSplit, 1e-5);
  BOOST_REQUIRE_SMALL(secondBestSplit, 1e-10);

  textSplit.EvaluateFitnessFunction(bestSplit, secondBestSplit);
  BOOST_REQUIRE_CLOSE(bestSplit, baseBestSplit, 1e-5);
  BOOST_REQUIRE_SMALL(secondBestSplit, 1e-10);

  binarySplit.EvaluateFitnessFunction(bestSplit, secondBestSplit);
  BOOST_REQUIRE_CLOSE(bestSplit, baseBestSplit, 1e-5);
  BOOST_REQUIRE_SMALL(secondBestSplit, 1e-10);

  arma::Col<size_t> children, xmlChildren, textChildren, binaryChildren;
  NumericSplitInfo<double> splitInfo, xmlSplitInfo, textSplitInfo,
      binarySplitInfo;

  split.Split(children, splitInfo);
  xmlSplit.Split(xmlChildren, xmlSplitInfo);
  binarySplit.Split(binaryChildren, binarySplitInfo);
  textSplit.Split(textChildren, textSplitInfo);

  BOOST_REQUIRE_EQUAL(children.size(), xmlChildren.size());
  BOOST_REQUIRE_EQUAL(children.size(), textChildren.size());
  BOOST_REQUIRE_EQUAL(children.size(), binaryChildren.size());
  for (size_t i = 0; i < children.size(); ++i)
  {
    BOOST_REQUIRE_EQUAL(children[i], xmlChildren[i]);
    BOOST_REQUIRE_EQUAL(children[i], textChildren[i]);
    BOOST_REQUIRE_EQUAL(children[i], binaryChildren[i]);
  }

  // Random checks.
  for (size_t i = 0; i < 200; ++i)
  {
    const double random = mlpack::math::Random() * 1.5;
    BOOST_REQUIRE_EQUAL(splitInfo.CalculateDirection(random),
                        xmlSplitInfo.CalculateDirection(random));
    BOOST_REQUIRE_EQUAL(splitInfo.CalculateDirection(random),
                        textSplitInfo.CalculateDirection(random));
    BOOST_REQUIRE_EQUAL(splitInfo.CalculateDirection(random),
                        binarySplitInfo.CalculateDirection(random));
  }
}

/**
 * Make sure serialization of the HoeffdingNumericSplit object before binning
 * occurs is successful.
 */
BOOST_AUTO_TEST_CASE(HoeffdingNumericSplitBeforeBinningTest)
{
  using namespace mlpack::tree;

  HoeffdingNumericSplit<GiniImpurity> split(3);
  // Train but not until it bins.
  for (size_t i = 0; i < 50; ++i)
    split.Train(mlpack::math::Random(), mlpack::math::RandInt(3));

  HoeffdingNumericSplit<GiniImpurity> xmlSplit(5);
  HoeffdingNumericSplit<GiniImpurity> textSplit(7);
  for (size_t i = 0; i < 200; ++i)
    textSplit.Train(mlpack::math::Random() + 3, 0);
  HoeffdingNumericSplit<GiniImpurity> binarySplit(2);

  SerializeObjectAll(split, xmlSplit, textSplit, binarySplit);

  // Ensure that everything is the same.
  BOOST_REQUIRE_EQUAL(split.Bins(), xmlSplit.Bins());
  BOOST_REQUIRE_EQUAL(split.Bins(), textSplit.Bins());
  BOOST_REQUIRE_EQUAL(split.Bins(), binarySplit.Bins());

  double baseBestSplit, baseSecondBestSplit;
  double bestSplit, secondBestSplit;
  split.EvaluateFitnessFunction(baseBestSplit, baseSecondBestSplit);
  textSplit.EvaluateFitnessFunction(bestSplit, secondBestSplit);

  BOOST_REQUIRE_SMALL(baseBestSplit, 1e-5);
  BOOST_REQUIRE_SMALL(baseSecondBestSplit, 1e-5);

  BOOST_REQUIRE_SMALL(bestSplit, 1e-5);
  BOOST_REQUIRE_SMALL(secondBestSplit, 1e-5);

  xmlSplit.EvaluateFitnessFunction(bestSplit, secondBestSplit);
  BOOST_REQUIRE_SMALL(bestSplit, 1e-5);
  BOOST_REQUIRE_SMALL(secondBestSplit, 1e-5);

  binarySplit.EvaluateFitnessFunction(bestSplit, secondBestSplit);
  BOOST_REQUIRE_SMALL(bestSplit, 1e-5);
  BOOST_REQUIRE_SMALL(secondBestSplit, 1e-5);
}

/**
 * Make sure the HoeffdingCategoricalSplit object serializes correctly.
 */
BOOST_AUTO_TEST_CASE(HoeffdingCategoricalSplitTest)
{
  using namespace mlpack::tree;

  HoeffdingCategoricalSplit<GiniImpurity> split(10, 3);
  for (size_t i = 0; i < 50; ++i)
    split.Train(mlpack::math::RandInt(10), mlpack::math::RandInt(3));

  HoeffdingCategoricalSplit<GiniImpurity> xmlSplit(3, 7);
  HoeffdingCategoricalSplit<GiniImpurity> binarySplit(4, 11);
  HoeffdingCategoricalSplit<GiniImpurity> textSplit(2, 2);
  for (size_t i = 0; i < 10; ++i)
    textSplit.Train(mlpack::math::RandInt(2), mlpack::math::RandInt(2));

  SerializeObjectAll(split, xmlSplit, textSplit, binarySplit);

  BOOST_REQUIRE_EQUAL(split.MajorityClass(), xmlSplit.MajorityClass());
  BOOST_REQUIRE_EQUAL(split.MajorityClass(), textSplit.MajorityClass());
  BOOST_REQUIRE_EQUAL(split.MajorityClass(), binarySplit.MajorityClass());

  double bestSplit, secondBestSplit;
  double baseBestSplit, baseSecondBestSplit;
  split.EvaluateFitnessFunction(baseBestSplit, baseSecondBestSplit);
  xmlSplit.EvaluateFitnessFunction(bestSplit, secondBestSplit);

  BOOST_REQUIRE_CLOSE(bestSplit, baseBestSplit, 1e-5);
  BOOST_REQUIRE_SMALL(secondBestSplit, 1e-10);

  textSplit.EvaluateFitnessFunction(bestSplit, secondBestSplit);
  BOOST_REQUIRE_CLOSE(bestSplit, baseBestSplit, 1e-5);
  BOOST_REQUIRE_SMALL(secondBestSplit, 1e-10);

  binarySplit.EvaluateFitnessFunction(bestSplit, secondBestSplit);
  BOOST_REQUIRE_CLOSE(bestSplit, baseBestSplit, 1e-5);
  BOOST_REQUIRE_SMALL(secondBestSplit, 1e-10);

  arma::Col<size_t> children, xmlChildren, textChildren, binaryChildren;
  CategoricalSplitInfo splitInfo(1); // I don't care about this.

  split.Split(children, splitInfo);
  xmlSplit.Split(xmlChildren, splitInfo);
  binarySplit.Split(binaryChildren, splitInfo);
  textSplit.Split(textChildren, splitInfo);

  BOOST_REQUIRE_EQUAL(children.size(), xmlChildren.size());
  BOOST_REQUIRE_EQUAL(children.size(), textChildren.size());
  BOOST_REQUIRE_EQUAL(children.size(), binaryChildren.size());
  for (size_t i = 0; i < children.size(); ++i)
  {
    BOOST_REQUIRE_EQUAL(children[i], xmlChildren[i]);
    BOOST_REQUIRE_EQUAL(children[i], textChildren[i]);
    BOOST_REQUIRE_EQUAL(children[i], binaryChildren[i]);
  }
}

/**
 * Make sure the HoeffdingTree object serializes correctly before a split has
 * occured.
 */
BOOST_AUTO_TEST_CASE(HoeffdingTreeBeforeSplitTest)
{
  data::DatasetInfo info(5);
  info.MapString("0", 2); // Dimension 1 is categorical.
  info.MapString("1", 2);
  HoeffdingTree<> split(info, 2, 0.99, 15000, 1);

  // Train for 2 samples.
  split.Train(arma::vec("0.3 0.4 1 0.6 0.7"), 0);
  split.Train(arma::vec("-0.3 0.0 0 0.7 0.8"), 1);

  data::DatasetInfo wrongInfo(3);
  wrongInfo.MapString("1", 1);
  HoeffdingTree<> xmlSplit(wrongInfo, 7, 0.1, 10, 1);

  // Force the binarySplit to split.
  data::DatasetInfo binaryInfo(2);
  binaryInfo.MapString("cat0", 0);
  binaryInfo.MapString("cat1", 0);
  binaryInfo.MapString("cat0", 1);

  HoeffdingTree<> binarySplit(info, 2, 0.95, 5000, 1);

  // Feed samples from each class.
  for (size_t i = 0; i < 500; ++i)
  {
    binarySplit.Train(arma::Col<size_t>("0 0"), 0);
    binarySplit.Train(arma::Col<size_t>("1 0"), 1);
  }

  HoeffdingTree<> textSplit(wrongInfo, 11, 0.75, 1000, 1);

  SerializeObjectAll(split, xmlSplit, textSplit, binarySplit);

  BOOST_REQUIRE_EQUAL(split.SplitDimension(), xmlSplit.SplitDimension());
  BOOST_REQUIRE_EQUAL(split.SplitDimension(), binarySplit.SplitDimension());
  BOOST_REQUIRE_EQUAL(split.SplitDimension(), textSplit.SplitDimension());

  BOOST_REQUIRE_EQUAL(split.MajorityClass(), xmlSplit.MajorityClass());
  BOOST_REQUIRE_EQUAL(split.MajorityClass(), binarySplit.MajorityClass());
  BOOST_REQUIRE_EQUAL(split.MajorityClass(), textSplit.MajorityClass());

  BOOST_REQUIRE_EQUAL(split.SplitCheck(), xmlSplit.SplitCheck());
  BOOST_REQUIRE_EQUAL(split.SplitCheck(), binarySplit.SplitCheck());
  BOOST_REQUIRE_EQUAL(split.SplitCheck(), textSplit.SplitCheck());
}

/**
 * Make sure the HoeffdingTree object serializes correctly after a split has
 * occurred.
 */
BOOST_AUTO_TEST_CASE(HoeffdingTreeAfterSplitTest)
{
  // Force the split to split.
  data::DatasetInfo info(2);
  info.MapString("cat0", 0);
  info.MapString("cat1", 0);
  info.MapString("cat0", 1);

  HoeffdingTree<> split(info, 2, 0.95, 5000, 1);

  // Feed samples from each class.
  for (size_t i = 0; i < 500; ++i)
  {
    split.Train(arma::Col<size_t>("0 0"), 0);
    split.Train(arma::Col<size_t>("1 0"), 1);
  }
  // Ensure a split has happened.
  BOOST_REQUIRE_NE(split.SplitDimension(), size_t(-1));

  data::DatasetInfo wrongInfo(3);
  wrongInfo.MapString("1", 1);
  HoeffdingTree<> xmlSplit(wrongInfo, 7, 0.1, 10, 1);

  data::DatasetInfo binaryInfo(5);
  binaryInfo.MapString("0", 2); // Dimension 2 is categorical.
  binaryInfo.MapString("1", 2);
  HoeffdingTree<> binarySplit(binaryInfo, 2, 0.99, 15000, 1);

  // Train for 2 samples.
  binarySplit.Train(arma::vec("0.3 0.4 1 0.6 0.7"), 0);
  binarySplit.Train(arma::vec("-0.3 0.0 0 0.7 0.8"), 1);

  HoeffdingTree<> textSplit(wrongInfo, 11, 0.75, 1000, 1);

  SerializeObjectAll(split, xmlSplit, textSplit, binarySplit);

  BOOST_REQUIRE_EQUAL(split.SplitDimension(), xmlSplit.SplitDimension());
  BOOST_REQUIRE_EQUAL(split.SplitDimension(), binarySplit.SplitDimension());
  BOOST_REQUIRE_EQUAL(split.SplitDimension(), textSplit.SplitDimension());

  // If splitting has already happened, then SplitCheck() should return 0.
  BOOST_REQUIRE_EQUAL(split.SplitCheck(), 0);
  BOOST_REQUIRE_EQUAL(split.SplitCheck(), xmlSplit.SplitCheck());
  BOOST_REQUIRE_EQUAL(split.SplitCheck(), binarySplit.SplitCheck());
  BOOST_REQUIRE_EQUAL(split.SplitCheck(), textSplit.SplitCheck());

  BOOST_REQUIRE_EQUAL(split.MajorityClass(), xmlSplit.MajorityClass());
  BOOST_REQUIRE_EQUAL(split.MajorityClass(), binarySplit.MajorityClass());
  BOOST_REQUIRE_EQUAL(split.MajorityClass(), textSplit.MajorityClass());

  BOOST_REQUIRE_EQUAL(split.CalculateDirection(arma::vec("0.3 0.4 1 0.6 0.7")),
      xmlSplit.CalculateDirection(arma::vec("0.3 0.4 1 0.6 0.7")));
  BOOST_REQUIRE_EQUAL(split.CalculateDirection(arma::vec("0.3 0.4 1 0.6 0.7")),
      binarySplit.CalculateDirection(arma::vec("0.3 0.4 1 0.6 0.7")));
  BOOST_REQUIRE_EQUAL(split.CalculateDirection(arma::vec("0.3 0.4 1 0.6 0.7")),
      textSplit.CalculateDirection(arma::vec("0.3 0.4 1 0.6 0.7")));
}

BOOST_AUTO_TEST_CASE(EmptyHoeffdingTreeTest)
{
  using namespace mlpack::tree;

  data::DatasetInfo info(6);
  HoeffdingTree<> tree(info, 2);
  HoeffdingTree<> xmlTree(info, 3);
  HoeffdingTree<> binaryTree(info, 4);
  HoeffdingTree<> textTree(info, 5);

  SerializeObjectAll(tree, xmlTree, binaryTree, textTree);

  BOOST_REQUIRE_EQUAL(tree.NumChildren(), 0);
  BOOST_REQUIRE_EQUAL(xmlTree.NumChildren(), 0);
  BOOST_REQUIRE_EQUAL(binaryTree.NumChildren(), 0);
  BOOST_REQUIRE_EQUAL(textTree.NumChildren(), 0);
}

/**
 * Build a Hoeffding tree, then save it and make sure other trees can classify
 * as effectively.
 */
BOOST_AUTO_TEST_CASE(HoeffdingTreeTest)
{
  using namespace mlpack::tree;

  arma::mat dataset(2, 400);
  arma::Row<size_t> labels(400);
  for (size_t i = 0; i < 200; ++i)
  {
    dataset(0, 2 * i) = mlpack::math::RandInt(4);
    dataset(1, 2 * i) = mlpack::math::RandInt(2);
    dataset(0, 2 * i + 1) = mlpack::math::RandInt(4);
    dataset(1, 2 * i + 1) = mlpack::math::RandInt(2) + 2;
    labels[2 * i] = 0;
    labels[2 * i + 1] = 1;
  }
  // Make the features categorical.
  data::DatasetInfo info(2);
  info.MapString("a", 0);
  info.MapString("b", 0);
  info.MapString("c", 0);
  info.MapString("d", 0);
  info.MapString("a", 1);
  info.MapString("b", 1);
  info.MapString("c", 1);
  info.MapString("d", 1);

  HoeffdingTree<> tree(dataset, info, labels, 2, false /* no batch mode */);

  data::DatasetInfo xmlInfo(1);
  HoeffdingTree<> xmlTree(xmlInfo, 1);
  data::DatasetInfo binaryInfo(5);
  HoeffdingTree<> binaryTree(binaryInfo, 6);
  data::DatasetInfo textInfo(7);
  HoeffdingTree<> textTree(textInfo, 100);

  SerializeObjectAll(tree, xmlTree, textTree, binaryTree);

  BOOST_REQUIRE_EQUAL(tree.NumChildren(), xmlTree.NumChildren());
  BOOST_REQUIRE_EQUAL(tree.NumChildren(), textTree.NumChildren());
  BOOST_REQUIRE_EQUAL(tree.NumChildren(), binaryTree.NumChildren());

  BOOST_REQUIRE_EQUAL(tree.SplitDimension(), xmlTree.SplitDimension());
  BOOST_REQUIRE_EQUAL(tree.SplitDimension(), textTree.SplitDimension());
  BOOST_REQUIRE_EQUAL(tree.SplitDimension(), binaryTree.SplitDimension());

  for (size_t i = 0; i < tree.NumChildren(); ++i)
  {
    BOOST_REQUIRE_EQUAL(tree.Child(i).NumChildren(), 0);
    BOOST_REQUIRE_EQUAL(xmlTree.Child(i).NumChildren(), 0);
    BOOST_REQUIRE_EQUAL(binaryTree.Child(i).NumChildren(), 0);
    BOOST_REQUIRE_EQUAL(textTree.Child(i).NumChildren(), 0);

    BOOST_REQUIRE_EQUAL(tree.Child(i).SplitDimension(),
        xmlTree.Child(i).SplitDimension());
    BOOST_REQUIRE_EQUAL(tree.Child(i).SplitDimension(),
        textTree.Child(i).SplitDimension());
    BOOST_REQUIRE_EQUAL(tree.Child(i).SplitDimension(),
        binaryTree.Child(i).SplitDimension());
  }
}

BOOST_AUTO_TEST_SUITE_END();


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

  @copyright 2016 NumScale SAS
  @copyright 2016 J.T. Lapreste

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

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

 /*!

    @ingroup group-exponential
    Function object implementing exprecnegc capabilities

    Computes the  function: \f$1-e^{-\frac1x}\f$

    @par Semantic:

    For every parameter of floating type T

    @code
    T r = exprecnegc(x);
    @endcode

    is equivalent to
    @code
    T r = oneminus(exp(-rec((x))));
    @endcode

    @see exp, exprecneg

  **/
  const boost::dispatch::functor<tag::exprecnegc_> exprecnegc = {};
} }
#endif

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

#endif


/*!
 * @file
 * An example of logistic regression training and testing.
 * The data is taken from:
 *
 * command to run:
 * ./bin/trajectory "data/trajectory/traj.txt"
 *
 * For running on some different data-set specify the columns etc. in `fromFile`
 * if needed and give the file(s) as command line argument.
 * */
#include <array>
#include <iostream>
#include <stdexcept>
#include <string>
#include <tuple>
#include <vector>
#include <boost/mpi.hpp>

#include <ezl.hpp>
#include <ezl/algorithms/reduces.hpp>
#include <ezl/algorithms/fromFile.hpp>

using namespace std;

auto difference(const array<float, 3>& prev, const array<float, 3>& next) {
  array<float, 3> diff;
  for(auto i : {0, 1, 2}) {
    diff[i] = next[i] - prev[i];
  }
  return diff;
}

auto crossProd(const array<float, 3>& v1, const array<float, 3>& v2) {
  array<float, 3> prod;
  prod[0] = v1[1]*v2[2] - v1[2]*v2[1];
  prod[1] = v1[2]*v2[0] - v1[0]*v2[2];
  prod[2] = v1[0]*v2[1] - v1[1]*v2[0];
  return prod;
}

void trajectory(int argc, char* argv[]) {
  const auto epsilon = 0.00001F;
  const string outFile = "data/output/traj.txt";
  std::string inFile = "data/trajectory/traj.txt";
  if (argc > 1) inFile = std::string(argv[1]);

  ezl::rise(ezl::fromFile<array<float, 3>>(inFile)
                .cols({1, 2, 3})
                .colSeparator(" \t"))
      .reduceAll([](const vector<array<float, 3>> & v) {
        return difference(v[0], v[1]);
      }).adjacent(2)
      .reduceAll([](const vector<array<float, 3>> & v) {
        return crossProd(v[0], v[1]);
      }).adjacent(2)
      .map([&epsilon](array<float, 3> prod) {
        array<int, 3> res;
        for (auto i : {0, 1, 2}) {
          res[i] = (fabs(prod[i]) > epsilon) ? (prod[i] / fabs(prod[i])) : 0;
        }
        return res;
      })
      .reduce<2>(ezl::count(), 0).inprocess()
      .reduceAll([](vector<tuple<array<int, 3>, int>> a) {
        sort(a.begin(), a.end());
        return a;
      }).dump(outFile)
      .run(1); // Always runs with single process
}

int main(int argc, char *argv[]) {
  boost::mpi::environment env(argc, argv, false);
  try {
    trajectory(argc, argv);
  } catch (const exception& ex) {
    cerr<<"error: "<<ex.what()<<'\n';
    env.abort(1);
  } catch (...) {
    cerr<<"unknown exception\n";
    env.abort(2);
  }
  return 0;
}


#include <stan/math/mix/scal.hpp>
#include <gtest/gtest.h>
#include <boost/math/special_functions/gamma.hpp>
#include <test/unit/math/rev/scal/fun/util.hpp>
#include <test/unit/math/mix/scal/fun/nan_util.hpp>

TEST(AgradFwdGammaQ, FvarVar_FvarVar_1stDeriv) {
  using stan::math::fvar;
  using stan::math::var;

  fvar<var> x(0.5,1.0);
  fvar<var> z(1.0,1.0);
  fvar<var> a = stan::math::gamma_q(x,z);

  EXPECT_FLOAT_EQ(boost::math::gamma_q(0.5,1.0), a.val_.val());
  EXPECT_FLOAT_EQ(0.18228334, a.d_.val());

  AVEC y = createAVEC(x.val_,z.val_);
  VEC g;
  a.val_.grad(y,g);
  EXPECT_FLOAT_EQ(0.38983709,g[0]);
  EXPECT_FLOAT_EQ(-boost::math::gamma_p_derivative(0.5,1.0),g[1]);
}
TEST(AgradFwdGammaQ, Double_FvarVar_1stDeriv) {
  using stan::math::fvar;
  using stan::math::var;

  double x(0.5);
  fvar<var> z(1.0,1.0);
  fvar<var> a = stan::math::gamma_q(x,z);

  EXPECT_FLOAT_EQ(boost::math::gamma_q(0.5,1.0), a.val_.val());
  EXPECT_FLOAT_EQ(-boost::math::gamma_p_derivative(0.5,1.0), a.d_.val());

  AVEC y = createAVEC(z.val_);
  VEC g;
  a.val_.grad(y,g);
  EXPECT_FLOAT_EQ(-boost::math::gamma_p_derivative(0.5,1.0),g[0]);
}
TEST(AgradFwdGammaQ, FvarVar_Double_1stDeriv) {
  using stan::math::fvar;
  using stan::math::var;

  fvar<var> x(0.5,1.0);
  double z(1.0);
  fvar<var> a = stan::math::gamma_q(x,z);

  EXPECT_FLOAT_EQ(boost::math::gamma_q(0.5,1.0), a.val_.val());
  EXPECT_FLOAT_EQ(0.38983709, a.d_.val());

  AVEC y = createAVEC(x.val_);
  VEC g;
  a.val_.grad(y,g);
  EXPECT_FLOAT_EQ(0.38983709,g[0]);
}
TEST(AgradFwdGammaQ, FvarVar_FvarVar_2ndDeriv) {
  using stan::math::fvar;
  using stan::math::var;

  fvar<var> x(0.5,1.0);
  fvar<var> z(1.0,1.0);
  fvar<var> a = stan::math::gamma_q(x,z);

  EXPECT_FLOAT_EQ(boost::math::gamma_q(0.5,1.0), a.val_.val());
  EXPECT_FLOAT_EQ(0.18228334, a.d_.val());

  AVEC y = createAVEC(x.val_,z.val_);
  VEC g;
  a.d_.grad(y,g);
  EXPECT_FLOAT_EQ(-0.19403456,g[0]);
  EXPECT_FLOAT_EQ(-0.096204743,g[1]);
}
TEST(AgradFwdGammaQ, Double_FvarVar_2ndDeriv) {
  using stan::math::fvar;
  using stan::math::var;

  double x(0.5);
  fvar<var> z(1.0,1.0);
  fvar<var> a = stan::math::gamma_q(x,z);

  EXPECT_FLOAT_EQ(boost::math::gamma_q(0.5,1.0), a.val_.val());
  EXPECT_FLOAT_EQ(-boost::math::gamma_p_derivative(0.5,1.0), a.d_.val());

  AVEC y = createAVEC(z.val_);
  VEC g;
  a.d_.grad(y,g);
  EXPECT_FLOAT_EQ(0.31133062,g[0]);
}
TEST(AgradFwdGammaQ, FvarVar_Double_2ndDeriv) {
  using stan::math::fvar;
  using stan::math::var;

  fvar<var> x(0.5,1.0);
  double z(1.0);
  fvar<var> a = stan::math::gamma_q(x,z);

  EXPECT_FLOAT_EQ(boost::math::gamma_q(0.5,1.0), a.val_.val());
  EXPECT_FLOAT_EQ(0.38983709, a.d_.val());

  AVEC y = createAVEC(x.val_);
  VEC g;
  a.d_.grad(y,g);
  EXPECT_FLOAT_EQ(0.21349931,g[0]);
}

TEST(AgradFwdGammaQ, FvarFvarVar_FvarFvarVar_1stDeriv) {
  using stan::math::fvar;
  using stan::math::var;
  using boost::math::gamma_q;

  fvar<fvar<var> > x;
  x.val_.val_ = 0.5;
  x.val_.d_ = 1.0;

  fvar<fvar<var> > y;
  y.val_.val_ = 1.0;
  y.d_.val_ = 1.0;

  fvar<fvar<var> > a = gamma_q(x,y);

  EXPECT_FLOAT_EQ(gamma_q(0.5,1.0), a.val_.val_.val());
  EXPECT_FLOAT_EQ(0.38983709, a.val_.d_.val());
  EXPECT_FLOAT_EQ(-boost::math::gamma_p_derivative(0.5,1.0), a.d_.val_.val());
  EXPECT_FLOAT_EQ(-0.40753385, a.d_.d_.val());

  AVEC p = createAVEC(x.val_.val_,y.val_.val_);
  VEC g;
  a.val_.val_.grad(p,g);
  EXPECT_FLOAT_EQ(0.38983709, g[0]);
  EXPECT_FLOAT_EQ(-boost::math::gamma_p_derivative(0.5,1.0), g[1]);
}
TEST(AgradFwdGammaQ, Double_FvarFvarVar_1stDeriv) {
  using stan::math::fvar;
  using stan::math::var;
  using boost::math::gamma_q;

  double x(0.5);

  fvar<fvar<var> > y;
  y.val_.val_ = 1.0;
  y.d_.val_ = 1.0;

  fvar<fvar<var> > a = gamma_q(x,y);

  EXPECT_FLOAT_EQ(gamma_q(0.5,1.0), a.val_.val_.val());
  EXPECT_FLOAT_EQ(0, a.val_.d_.val());
  EXPECT_FLOAT_EQ(-boost::math::gamma_p_derivative(0.5,1.0), a.d_.val_.val());
  EXPECT_FLOAT_EQ(0, a.d_.d_.val());

  AVEC p = createAVEC(y.val_.val_);
  VEC g;
  a.val_.val_.grad(p,g);
  EXPECT_FLOAT_EQ(-boost::math::gamma_p_derivative(0.5,1.0), g[0]);
}
TEST(AgradFwdGammaQ, FvarFvarVar_Double_1stDeriv) {
  using stan::math::fvar;
  using stan::math::var;
  using boost::math::gamma_q;

  fvar<fvar<var> > x;
  x.val_.val_ = 0.5;
  x.val_.d_ = 1.0;

  double y(1.0);

  fvar<fvar<var> > a = gamma_q(x,y);

  EXPECT_FLOAT_EQ(gamma_q(0.5,1.0), a.val_.val_.val());
  EXPECT_FLOAT_EQ(0.38983709, a.val_.d_.val());
  EXPECT_FLOAT_EQ(0, a.d_.val_.val());
  EXPECT_FLOAT_EQ(0, a.d_.d_.val());

  AVEC p = createAVEC(x.val_.val_);
  VEC g;
  a.val_.val_.grad(p,g);
  EXPECT_FLOAT_EQ(0.38983709, g[0]);
}

TEST(AgradFwdGammaQ, FvarFvarVar_FvarFvarVar_2ndDeriv_x) {
  using stan::math::fvar;
  using stan::math::var;
  using boost::math::gamma_q;

  fvar<fvar<var> > x;
  x.val_.val_ = 0.5;
  x.val_.d_ = 1.0;

  fvar<fvar<var> > y;
  y.val_.val_ = 1.0;
  y.d_.val_ = 1.0;

  fvar<fvar<var> > a = gamma_q(x,y);

  EXPECT_FLOAT_EQ(gamma_q(0.5,1.0), a.val_.val_.val());
  EXPECT_FLOAT_EQ(0.38983709, a.val_.d_.val());
  EXPECT_FLOAT_EQ(-boost::math::gamma_p_derivative(0.5,1.0), a.d_.val_.val());
  EXPECT_FLOAT_EQ(-0.40753385, a.d_.d_.val());

  AVEC p = createAVEC(x.val_.val_,y.val_.val_);
  VEC g;
  a.val_.d_.grad(p,g);
  EXPECT_FLOAT_EQ(0.21349931, g[0]);
  EXPECT_FLOAT_EQ(-0.40753537, g[1]);
}
TEST(AgradFwdGammaQ, FvarFvarVar_FvarFvarVar_2ndDeriv_y) {
  using stan::math::fvar;
  using stan::math::var;
  using boost::math::gamma_q;

  fvar<fvar<var> > x;
  x.val_.val_ = 0.5;
  x.val_.d_ = 1.0;

  fvar<fvar<var> > y;
  y.val_.val_ = 1.0;
  y.d_.val_ = 1.0;

  fvar<fvar<var> > a = gamma_q(x,y);

  EXPECT_FLOAT_EQ(gamma_q(0.5,1.0), a.val_.val_.val());
  EXPECT_FLOAT_EQ(0.38983709, a.val_.d_.val());
  EXPECT_FLOAT_EQ(-boost::math::gamma_p_derivative(0.5,1.0), a.d_.val_.val());
  EXPECT_FLOAT_EQ(-0.40753385, a.d_.d_.val());

  AVEC p = createAVEC(x.val_.val_,y.val_.val_);
  VEC g;
  a.d_.val_.grad(p,g);
  EXPECT_FLOAT_EQ(-0.40753385, g[0]);
  EXPECT_FLOAT_EQ(0.31133062, g[1]);
}
TEST(AgradFwdGammaQ, Double_FvarFvarVar_2ndDeriv) {
  using stan::math::fvar;
  using stan::math::var;
  using boost::math::gamma_q;

  double x(0.5);

  fvar<fvar<var> > y;
  y.val_.val_ = 1.0;
  y.d_.val_ = 1.0;

  fvar<fvar<var> > a = gamma_q(x,y);

  EXPECT_FLOAT_EQ(gamma_q(0.5,1.0), a.val_.val_.val());
  EXPECT_FLOAT_EQ(0, a.val_.d_.val());
  EXPECT_FLOAT_EQ(-boost::math::gamma_p_derivative(0.5,1.0), a.d_.val_.val());
  EXPECT_FLOAT_EQ(0, a.d_.d_.val());

  AVEC p = createAVEC(y.val_.val_);
  VEC g;
  a.d_.val_.grad(p,g);
  EXPECT_FLOAT_EQ(0.31133062, g[0]);
}
TEST(AgradFwdGammaQ, FvarFvarVar_Double_2ndDeriv) {
  using stan::math::fvar;
  using stan::math::var;
  using boost::math::gamma_q;

  fvar<fvar<var> > x;
  x.val_.val_ = 0.5;
  x.val_.d_ = 1.0;

  double y(1.0);

  fvar<fvar<var> > a = gamma_q(x,y);

  EXPECT_FLOAT_EQ(gamma_q(0.5,1.0), a.val_.val_.val());
  EXPECT_FLOAT_EQ(0.38983709, a.val_.d_.val());
  EXPECT_FLOAT_EQ(0, a.d_.val_.val());
  EXPECT_FLOAT_EQ(0, a.d_.d_.val());

  AVEC p = createAVEC(x.val_.val_);
  VEC g;
  a.val_.d_.grad(p,g);
  EXPECT_FLOAT_EQ(0.21349931, g[0]);
}

TEST(AgradFwdGammaQ, FvarFvarVar_FvarFvarVar_3rdDeriv) {
  using stan::math::fvar;
  using stan::math::var;
  using boost::math::gamma_q;

  fvar<fvar<var> > x;
  x.val_.val_ = 0.5;
  x.val_.d_ = 1.0;

  fvar<fvar<var> > y;
  y.val_.val_ = 1.0;
  y.d_.val_ = 1.0;

  fvar<fvar<var> > a = gamma_q(x,y);

  EXPECT_FLOAT_EQ(gamma_q(0.5,1.0), a.val_.val_.val());
  EXPECT_FLOAT_EQ(0.38983709, a.val_.d_.val());
  EXPECT_FLOAT_EQ(-boost::math::gamma_p_derivative(0.5,1.0), a.d_.val_.val());
  EXPECT_FLOAT_EQ(-0.40753385, a.d_.d_.val());

  AVEC p = createAVEC(x.val_.val_,y.val_.val_);
  VEC g;
  a.d_.d_.grad(p,g);
  EXPECT_FLOAT_EQ(0.22403987, g[0]);
  EXPECT_FLOAT_EQ(0.40374705, g[1]);
}
TEST(AgradFwdGammaQ, Double_FvarFvarVar_3rdDeriv) {
  using stan::math::fvar;
  using stan::math::var;
  using boost::math::gamma_q;

  double x(0.5);

  fvar<fvar<var> > y;
  y.val_.val_ = 1.0;
  y.d_.val_ = 1.0;
  y.val_.d_ = 1.0;

  fvar<fvar<var> > a = gamma_q(x,y);

  AVEC p = createAVEC(y.val_.val_);
  VEC g;
  a.d_.d_.grad(p,g);
  EXPECT_FLOAT_EQ(-0.57077283, g[0]);
}
TEST(AgradFwdGammaQ, FvarFvarVar_Double_3rdDeriv) {
  using stan::math::fvar;
  using stan::math::var;
  using boost::math::gamma_q;

  fvar<fvar<var> > x;
  x.val_.val_ = 0.5;
  x.val_.d_ = 1.0;
  x.d_.val_ = 1.0;

  double y(1.0);

  fvar<fvar<var> > a = gamma_q(x,y);

  AVEC p = createAVEC(x.val_.val_);
  VEC g;
  a.d_.d_.grad(p,g);
  EXPECT_FLOAT_EQ(-0.5462361, g[0]);
}

struct gamma_q_fun {
  template <typename T0, typename T1>
  inline
  typename boost::math::tools::promote_args<T0,T1>::type
  operator()(const T0 arg1,
             const T1 arg2) const {
    return gamma_q(arg1,arg2);
  }
};

TEST(AgradFwdGammaQ, nan) {
  gamma_q_fun gamma_q_;
  test_nan_mix(gamma_q_,3.0,5.0,false);
}


#include <iostream>
#include <stdexcept>
// Boost random generator
#include <boost/random/mersenne_twister.hpp>
#include <boost/random/normal_distribution.hpp>
// Eigen dense matrices
#include <Eigen/Dense>

#include "factory.h"
#include "normal_multivar.h"

namespace numeric_utils {

NormalMultiVar::NormalMultiVar()
  : RandomGenerator()
{
  generator_ = boost::random::mt19937(seed_);
  distribution_ = boost::random::normal_distribution<double>();
}

NormalMultiVar::NormalMultiVar(int seed)
  : RandomGenerator()
{
  seed_ = seed;
  generator_ = boost::random::mt19937(seed_);
  distribution_ = boost::random::normal_distribution<double>();
}

bool NormalMultiVar::generate(
    Eigen::Matrix<double, Eigen::Dynamic, Eigen::Dynamic>& random_numbers,
    const Eigen::VectorXd& means, const Eigen::MatrixXd& cov,
    unsigned int cases) {

  bool success = true;
  Eigen::Matrix<double, Eigen::Dynamic, Eigen::Dynamic> lower_cholesky;

  try {
    auto llt = cov.llt();
    lower_cholesky = llt.matrixL();

    if (llt.info() == Eigen::NumericalIssue) {
      throw std::runtime_error(
          "\nERROR: In NormalMultivar::generate method: Input covariance matrix is not "
          "positive semi-definite\n");
    }
  } catch (const std::exception& e) {
    std::cerr << "\nERROR: In normal multivariate random number generation: "
              << e.what() << std::endl;
    success = false;
  }

  random_numbers.resize(cov.rows(), cases);

  // Generate random numbers based on distribution and generator type for
  // requested number of cases
  for (unsigned int i = 0; i < random_numbers.cols(); ++i) {
    for (unsigned int j = 0; j < random_numbers.rows(); ++j) {
      random_numbers(j, i) = distribution_(generator_);
    }
  }

  // Transform from unit normal distribution based on covariance and mean values
  for (unsigned int i = 0; i < random_numbers.cols(); ++i) {
    random_numbers.col(i) = lower_cholesky * random_numbers.col(i) + means;
  }

  return success;
}

std::string NormalMultiVar::name() const {
  return "NormalMultiVar";
}
}  // namespace numeric_utils


//  (C) Copyright 2006 Eric Niebler, Olivier Gygi.
//  Use, modification and distribution are subject to the
//  Boost Software License, Version 1.0. (See accompanying file
//  LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)

// Test case for tail_quantile.hpp

#include <boost/random.hpp>
#include <boost/test/unit_test.hpp>
#include <boost/test/floating_point_comparison.hpp>
#include <boost/accumulators/numeric/functional/vector.hpp>
#include <boost/accumulators/numeric/functional/complex.hpp>
#include <boost/accumulators/numeric/functional/valarray.hpp>
#include <boost/accumulators/accumulators.hpp>
#include <boost/accumulators/statistics/stats.hpp>
#include <boost/accumulators/statistics/tail_quantile.hpp>

using namespace boost;
using namespace unit_test;
using namespace boost::accumulators;

///////////////////////////////////////////////////////////////////////////////
// test_stat
//
void test_stat()
{
    // tolerance in %
    double epsilon = 1;

    std::size_t n = 100000; // number of MC steps
    std::size_t c =  10000; // cache size

    typedef accumulator_set<double, stats<tag::tail_quantile<right> > > accumulator_t_right;
    typedef accumulator_set<double, stats<tag::tail_quantile<left> > > accumulator_t_left;

    accumulator_t_right acc0( right_tail_cache_size = c );
    accumulator_t_right acc1( right_tail_cache_size = c );
    accumulator_t_left  acc2( left_tail_cache_size = c );
    accumulator_t_left  acc3( left_tail_cache_size = c );

    // two random number generators
    boost::lagged_fibonacci607 rng;
    boost::normal_distribution<> mean_sigma(0,1);
    boost::variate_generator<boost::lagged_fibonacci607&, boost::normal_distribution<> > normal(rng, mean_sigma);

    for (std::size_t i = 0; i < n; ++i)
    {
        double sample1 = rng();
        double sample2 = normal();
        acc0(sample1);
        acc1(sample2);
        acc2(sample1);
        acc3(sample2);
    }

    // check uniform distribution
    BOOST_CHECK_CLOSE( quantile(acc0, quantile_probability = 0.95 ), 0.95,  epsilon );
    BOOST_CHECK_CLOSE( quantile(acc0, quantile_probability = 0.975), 0.975, epsilon );
    BOOST_CHECK_CLOSE( quantile(acc0, quantile_probability = 0.99 ), 0.99,  epsilon );
    BOOST_CHECK_CLOSE( quantile(acc0, quantile_probability = 0.999), 0.999, epsilon );
    BOOST_CHECK_CLOSE( quantile(acc2, quantile_probability  = 0.05 ), 0.05,  2 );
    BOOST_CHECK_CLOSE( quantile(acc2, quantile_probability  = 0.025), 0.025, 2 );
    BOOST_CHECK_CLOSE( quantile(acc2, quantile_probability  = 0.01 ), 0.01,  3 );
    BOOST_CHECK_CLOSE( quantile(acc2, quantile_probability  = 0.001), 0.001, 20 );

    // check standard normal distribution
    BOOST_CHECK_CLOSE( quantile(acc1, quantile_probability = 0.975),  1.959963, epsilon );
    BOOST_CHECK_CLOSE( quantile(acc1, quantile_probability = 0.999),  3.090232, epsilon );
    BOOST_CHECK_CLOSE( quantile(acc3, quantile_probability  = 0.025), -1.959963, epsilon );
    BOOST_CHECK_CLOSE( quantile(acc3, quantile_probability  = 0.001), -3.090232, epsilon );

}

///////////////////////////////////////////////////////////////////////////////
// init_unit_test_suite
//
test_suite* init_unit_test_suite( int argc, char* argv[] )
{
    test_suite *test = BOOST_TEST_SUITE("tail_quantile test");

    test->add(BOOST_TEST_CASE(&test_stat));

    return test;
}


#include <iostream>
#include <functional>
#include <cmath>
#include <boost/mpi.hpp>

namespace mpi = boost::mpi;

int main (int argc, char* argv[])
{
    mpi::environment env(argc, argv);
    mpi::communicator world;
    int myrank= world.rank();

    float vec[2];
    vec[0]= 2*myrank; vec[1]= vec[0]+1;

    // Local accumulation
    float local= std::abs(vec[0]) + std::abs(vec[1]);

    // Global accumulation
    float global= mpi::all_reduce(world, local, std::plus<float>());
    std::cout << "Hello, I am process " << world.rank() << " and I know too that |v|_1 is " << global << ".\n";

    return 0 ;
}


// Software License for MTL
//
// Copyright (c) 2007 The Trustees of Indiana University.
//               2008 Dresden University of Technology and the Trustees of Indiana University.
//               2010 SimuNova UG (haftungsbeschränkt), www.simunova.com.
// All rights reserved.
// Authors: Peter Gottschling and Andrew Lumsdaine
//
// This file is part of the Matrix Template Library
//
// See also license.mtl.txt in the distribution.

#include <iostream>
#include <boost/numeric/mtl/mtl.hpp>

using namespace std;

template <typename Matrix, typename Indirect>
void check(const Indirect& I, const char* error)
{
    Matrix C(3, 2);
    C= 4, 2,
       2, 0,
       5, 3;

    C-= I;
    if (one_norm(C) > 0.01) throw error;
}

template <typename Matrix>
void test(Matrix& A, const char* name)
{
    hessian_setup(A, 1.0);
    std::cout << "\n" << name << "\nA is: \n" << A;

    mtl::iset rows, cols;
    rows= 2, 0, 3;
    cols= 2, 0;

    cout << "rows = " << rows << ", cols = " << cols << "\n";
    cout << "A[rows][cols] is: \n" << A[rows][cols] << "\n";

    mtl::mat::indirect<Matrix> B(A[rows][cols]);
    cout << "B is\n" << B;
    check<Matrix>(B, "Wrong value after copy constructor");

    Matrix D(3, 2), E;
    D= B;
    check<Matrix>(D, "Wrong value after assignment");

    E= D + B;
    E/= 2;
    check<Matrix>(D, "Wrong value after addition");
}

int main(int, char**)
{
    using namespace mtl;
    const unsigned size= 5;

    dense2D<double>                                  dc(size, size-2);
    dense2D<double, mat::parameters<col_major> >  dcc(size, size-2);
    dense2D<float>                                   fc(size, size-2);
    morton_dense<double,  morton_mask>               mdc(size, size-2);
    morton_dense<double, doppled_32_col_mask>        mcc(size, size-2);
    compressed2D<double>                             cc(size, size-2);
    compressed2D<double, mat::parameters<col_major> >  ccc(size, size-2);

    test(dc, "dense2D");
    test(dcc, "dense2D col-major");
    test(fc, "dense2D float");

    test(mdc, "pure Morton");
    test(mcc, "Hybrid col-major");
    test(cc, "Compressed");
    test(ccc, "Compresse col-majord");

    return 0;
}


#include <vector>
#include <random>

#include <algorithm>
#include <numeric>
#include <execution>

#define BOOST_TEST_MODULE TestNORM2
#include <boost/test/unit_test.hpp>
#include <boost/test/tools/floating_point_comparison.hpp>

#include <generate_randoms.hpp>
#include <norm2.hpp>

BOOST_AUTO_TEST_CASE( NORM2_FLOAT_SEQ )
{
    std::vector<float> lv{ 2.0, 3.0, 4.0, 5.0 };

    BOOST_CHECK_EQUAL(norm_2<int>(std::execution::seq, lv.begin(), lv.end()), 7);
    BOOST_CHECK_CLOSE(norm_2(std::execution::seq, lv.begin(), lv.end()), 7.348469, 0.001);
}

BOOST_AUTO_TEST_CASE( NORM2_FLOAT_PAR_10K )
{
    std::vector<float> A(10000);

    generate_randoms(std::execution::seq, A.begin(), 10000);

    BOOST_CHECK_CLOSE(
        norm_2<float>(std::execution::seq, A.begin(), A.begin() + 10000)
    ,   norm_2<float>(std::execution::par, A.begin(), A.begin() + 10000)
    ,   0.00001);
}


#define BOOST_TEST_MODULE shperical harmonic transforms
#define BOOST_TEST_DYN_LINK
#include <cmath>
#include <random>
#include <vector>
#include <chrono>
#include <iostream>
#include <boost/test/unit_test.hpp>
#include <blackpearl/core/sht.hpp>

template<typename real_scalar_type>
void test_sht(){
    using namespace blackpearl::core;
    size_t l_max(6143);
    size_t m_max(l_max);
    size_t n_side(2048);
    size_t num_pixels = 12*n_side*n_side;
    size_t num_fields(6);
    sht<real_scalar_type> sht_test(l_max,m_max,num_pixels,num_fields);
}

BOOST_AUTO_TEST_CASE(shp_data){
    test_sht<float>();
    test_sht<double>();
}


//
// $Id$
//
//
// Original author: Witold Wolski <wewolski@gmail.com>
//
// Copyright : ETH Zurich
//
// 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.
//
#ifndef DETERMINEBINWIDTH_H
#define DETERMINEBINWIDTH_H

#include <cmath>
#include <boost/assert.hpp>
#include <boost/range/algorithm_ext.hpp>
#include <boost/bind.hpp>
#include <algorithm>

#include "pwiz/utility/findmf/base/base/diff.hpp"
#include "pwiz/utility/findmf/base/resample/utilities/determinebinwidth.hpp"

namespace ralab
{
	namespace base
	{
		namespace resample
		{
			template<typename TReal>
			struct SquareRoot{
				TReal operator()(TReal x) const{
					return(sqrt(x));
				}
			};

			struct SamplingWith{
				std::vector<double> diff_;
				std::vector<double> summ_;
				std::vector<double> am_;

				//expects a sorted sequence
				template<typename TRealI>
				double operator()(TRealI begin, TRealI end)
				{
					//BOOST_ASSERT(!boost::range::is_sorted(begin,end));
					typedef typename std::iterator_traits<TRealI>::value_type TReal;
					std::size_t N = std::distance(begin,end);
					double am;
					if(N > 1){
						diff_.resize(N-1);
						summ_.resize(N-1);
						am_.resize(N-1);
						ralab::base::base::diff(begin,end,diff_.begin(),1);

						utilities::summ( begin , end, summ_.begin(),1);
						//square the sum
						//std::transform(summ_.begin(),summ_.end(),summ_.begin(),boost::bind(sqrt,_1));
						std::transform(summ_.begin(),summ_.end(),summ_.begin(),SquareRoot<TReal>());
						std::transform(diff_.begin(),diff_.end(),summ_.begin(),am_.begin(),std::divides<double>());
						std::sort(am_.begin(),am_.end());
						am = utilities::determine(am_.begin(),am_.end());
					}else{
						am = 0.;
					}
					return am;
				}
			};

		}
	}
}

#endif // DETERMINEBINWIDTH_H


#include <gtest/gtest.h>

#include "scheme/kinematics/Scene_io.hh"
#include "scheme/actor/ActorConcept_io.hh"
#include "scheme/objective/ObjectiveFunction.hh"
#include "scheme/objective/ObjectiveVisitor.hh"
#include "scheme/numeric/X1dim.hh"

#include "scheme/io/dump_pdb_atom.hh"

#include <boost/foreach.hpp>

#include <stdint.h>
#include <fstream>

#include <Eigen/Geometry>

namespace scheme { namespace kinematics { namespace test_eigen {

using std::cout;
using std::endl;
using boost::tie;

typedef Eigen::AngleAxis<double> AA;
typedef Eigen::Vector3d Vec;

Vec UX(1,0,0);
Vec UY(0,1,0);
Vec UZ(0,0,1);

struct Xform : Eigen::Transform<double,3,Eigen::AffineCompact>{
	typedef Eigen::Transform<double,3,Eigen::AffineCompact> BASE;
	Xform(){}
	Xform(Vec t, double a, Vec ax) : BASE(AA(a,ax)) { translation() = t; }
	template<class T> Xform(T const & t) : BASE(t) {}
};

// Xform inverse(Xform const & x){ return x.inverse(); }
std::ostream & operator<<(std::ostream & out,Xform const & x){
	return out << "t("<<x.translation().transpose()<<")";
}

TEST(Scene_eigen,eigen_transform){
	// Xform x(Xform::Identity());
	AA aa(0,Vec(1,0,0));
	Xform x(aa);
	//cout << x << endl;
	Eigen::Vector3d v(0,0,0);
	x*v;
	Eigen::RowVector3d rv(0,0,0);
	// x*rv; // not allowed
	Eigen::Vector3f vf(0,0,0);
	// x*vf // not allowed
	x*vf.cast<double>();
	Eigen::RowVector4d v4(0,0,0,0);
	// x*v4; // not allowed
}

typedef size_t Index;
typedef std::pair<size_t,size_t> Index2;
typedef std::pair<Index2,Index2> Index4;

struct Xactor : actor::ActorConcept<Xform,int> {
	Xactor() : actor::ActorConcept<Xform,int>() {}
	Xactor(Position const & p, int d) : actor::ActorConcept<Xform,int>(p,d) {}
	Xactor(Xactor const & a,Position const & moveby){ position_ = moveby*a.position(); data_ = a.data_; }
};

////////////// test scores ////////////////////////

	struct ScoreX {
		typedef double Result;
		typedef Xactor Interaction;
		static std::string name(){ return "ScoreADI"; }
		template<class Config>
		Result operator()(Interaction const & a, Config const& ) const {
			return a.data_;
		}
	};
	std::ostream & operator<<(std::ostream & out,ScoreX const& s){ return out << s.name(); }

	double distance( Xform const & a, Xform const & b ){
		return (a.translation()-b.translation()).norm();
	}

	struct ScoreXX {
		static size_t ncalls;
		typedef double Result;
		typedef Xactor Actor1;
		typedef Xactor Actor2;
		typedef std::pair<Xactor,Xactor> Interaction;
		static std::string name(){ return "ScoreADIADI"; }
		template<class Config>
		Result operator()(Actor1 const & a1, Actor2 const & a2, Config const& ) const {
			++ncalls;
			// cout << a.first << " " << a.second << endl;
			return distance(a1.position(),a2.position());
		}
		template<class Config>
		Result operator()(Interaction const & i, Config const& c) const {
			return this->template operator()<Config>(i.first,i.second,c);
		}
	};
	size_t ScoreXX::ncalls = 0;
	std::ostream & operator<<(std::ostream & out,ScoreXX const& s){ return out << s.name(); }

	struct Config {};

/////////////////// tests //////////////////////////

Xform rel(Xform a,Xform b){ return a.inverse()*b; }
Xform rel2(Xform a,Xform b){ return b*a.inverse(); }

TEST(Scene_eigen,relative_relations_preserved){
	typedef	objective::ObjectiveFunction<
		m::vector<
			ScoreX,
			ScoreXX
		>,
		Config
	> ObjFun;
	typedef ObjFun::Results Results;
	ObjFun score;

	typedef m::vector< Xactor > Actors;
	typedef Scene<impl::Conformation<Actors>,Xform> Scene;

	Scene scene(2);
	scene.set_position(0,Xform(Vec(10, 0, 0),2,UX));
	scene.set_position(1,Xform(Vec( 0,10, 0),2,UY));

	scene.mutable_conformation_asym(0).add_actor( Xactor(Xform(Vec(1,3,1),1,UY),7) );

	scene.mutable_conformation_asym(1).add_actor( Xactor(Xform(Vec(3,4,7),2,UZ),7) );

	Xactor x1,x2,a1,a2,r1,r2;

	x1 = scene.get_actor<Xactor>(0,0);
	x2 = scene.get_actor<Xactor>(1,0);

	tie(a1,a2) = scene.get_interaction_absolute<std::pair<Xactor,Xactor> >(
		std::make_pair(std::make_pair(0,1),std::make_pair(0,0)));
	tie(r1,r2) = scene.get_interaction<std::pair<Xactor,Xactor> >(
		std::make_pair(std::make_pair(0,1),std::make_pair(0,0)));

	ASSERT_TRUE( x1.position().isApprox(a1.position()) );
	ASSERT_TRUE( x2.position().isApprox(a2.position()) );
	// cout << x1 << " " << x2 << endl;
	// cout << a1 << " " << a2 << endl;
	// cout << r1 << " " << r2 << endl;

	Xform xa = a1.position().inverse() * a2.position();
	Xform xr = r1.position().inverse() * r2.position();
	ASSERT_TRUE( xa.isApprox(xr) );

	/// why ~a*b captures the relation of a to b
	/// rather than b * ~a?  b * ~a  * a  == b
	/// ~(c*a)*c*b = ~a*~c*c*b = ~a*b
	/// ~(a*c)*b*c = ~c*~a*b*c
	/// b*c*~(a*c) = b*c*~c*a = b*~a
	Xform a = Xform(Vec(10, 0, 0),2,UX);
	Xform b = Xform(Vec( 0,10, 0),2,UY);
	Xform c = Xform(Vec( 0,10,10),1,UZ);
	ASSERT_TRUE ( rel (a,b).isApprox( rel (c*a  ,c*b  ) )  );
	ASSERT_FALSE( rel (a,b).isApprox( rel (  a*c,  b*c) )  );
	ASSERT_FALSE( rel2(a,b).isApprox( rel2(c*a  ,c*b  ) )  );
	ASSERT_TRUE ( rel2(a,b).isApprox( rel2(  a*c,  b*c) )  );

	// xa = a1.position() * a2.position().inverse();
	// xr = r1.position() * r2.position().inverse();
	// ASSERT_TRUE( xa.isApprox(xr) );
}

// TEST(Scene_eigen,symmetry){
// 	typedef	objective::ObjectiveFunction<
// 		m::vector<
// 			ScoreADI,
// 			ScoreADC,
// 			ScoreADIADI,
// 			ScoreADCADI
// 		>,
// 		Config
// 	> ObjFun;
// 	typedef ObjFun::Results Results;

// 	ObjFun score;

// 	typedef m::vector< ADI, ADC > Actors;
// 	typedef Conformation<Actors> Conformation;
// 	typedef Scene<Conformation,X1dim,size_t> Scene;

// 	Scene scene(2);
// 	scene.add_symframe(10);

// 	ASSERT_EQ( score(scene).sum(), 0 );

// 	scene.mutable_conformation_asym(0).add_actor( ADI(0,1) );
// 	scene.mutable_conformation_asym(1).add_actor( ADI(1,2) );
// 	// check that symmetric interactiosn are downweighted by 0.5: 40/2 = 20
// 	ASSERT_EQ( score(scene), Results(3,0,1+20,0) );

// 	scene.mutable_conformation_asym(1).add_actor( ADI(0,1) );
// 	scene.mutable_conformation_asym(1).add_actor( ADI(1,2) );
// 	// check that symmetric interactiosn are downweighted by 0.5: 160/2 = 80
// 	ASSERT_EQ( score(scene), Results(6,0,2+80,0) );

// 	// scene.mutable_conformation_asym(1).add_actor( ADC(0,'1') );
// 	// scene.mutable_conformation_asym(1).add_actor( ADC(0,'2') );
// 	// ASSERT_EQ( score(scene), Results(20,3,16,80) );

// }

// TEST(Scene_eigen, performance){
// // TEST(Scene_eigen,performance){
// 	// TODO: speed up SceneIter iteration
// 	//       iteration seems to take about 100 cycles per score call overhead
// 	//       much of this is probably all the conditions for symmetry checks
// 	//       could template out these and have both sym and asym scenes?
// 	// FIX with visitation pattern, seems at least 10x faster

// 	std::cout << "This test performs 301.934M score calls, should
	// take about a second when compiled with optimizations." << std::endl;

// 	typedef	objective::ObjectiveFunction<
// 		m::vector<
// 			ScoreADI,
// 			ScoreADC,
// 			ScoreADIADI,
// 			ScoreADCADI
// 		>,
// 		Config
// 	> ObjFun;
// 	typedef ObjFun::Results Results;

// 	ObjFun score;

// 	typedef m::vector< ADI, ADC > Actors;
// 	typedef Conformation<Actors> Conformation;
// 	typedef Scene<Conformation,X1dim,uint32_t> Scene;

// 	ScoreADIADI obj;
// 	Config c;
// 	objective::ObjectiveVisitor<ScoreADIADI,Config> visitor(obj,c);

// 	Scene scene; {
// 		Scene::Index const NBOD = 10;
// 		Scene::Index const NSYM = 20;
// 		Scene::Index const NACT = 400;
// 		for(Scene::Index i = 0; i < NBOD; ++i) scene.add_body();
// 		for(Scene::Index i = 0; i < NSYM-1; ++i) scene.add_symframe(i+1);
// 		for(Scene::Index i = 0; i < NBOD; ++i){
// 			for(Scene::Index j = 0; j < NACT; ++j){
// 				scene.mutable_conformation_asym(i).add_actor( ADI(j,i) );
// 			}
// 		}
// 	}

// 	cout << score(scene).get<ScoreADIADI>() << " " << (double)ScoreADIADI::ncalls/1000000.0 << "M" << endl;
// 	ScoreADIADI::ncalls = 0;
// 	return;

// 	if(false)
// 	{
// 			// typedef ADI Actor1;
// 			// typedef ADI Actor2;
// 			// typedef Scene::Position Position;
// 			// Scene::Index const NBOD = scene.bodies_.size();
// 			// Scene::Index const NSYM = scene.symframes_.size()+1;
// 			// for(Scene::Index i1 = 0; i1 < NBOD*NSYM; ++i1){
// 			// 	Conformation const & c1 = scene.conformation(i1);
// 			// 	Position     const & p1 =     scene.position(i1);
// 			// 	Scene::Index const NACT1 = c1.get<Actor1>().size();
// 			// 	for(Scene::Index i2 = 0; i2 < NBOD*NSYM; ++i2){
// 			// 		if( i1 >= NBOD && i2 >= NBOD ) continue;
// 			// 		if( i2 <= i1 ) continue;
// 			// 		Conformation const & c2 = scene.conformation(i2);
// 			// 		Position     const & p2 =     scene.position(i2);
// 			// 		Scene::Index const NACT2 = c2.get<Actor2>().size();
// 			// 		for(Scene::Index j1 = 0; j1 < NACT1; ++j1){
// 			// 			Actor1 a1( c1.get<Actor1>()[j1], p1 );
// 			// 			for(Scene::Index j2 = 0; j2 < NACT2; ++j2){
// 			// 				Actor2 a2( c2.get<Actor2>()[j2], p2 );
// 			// 				visitor( a1, a2, i1<NBOD&&i2<NBOD?1.0:0.5 );
// 			// 			}
// 			// 		}
// 			// 	}
// 			// }
// 			// cout << visitor.result_ << " " << (double)ScoreADIADI::ncalls/1000000.0 << endl;
// 			// ScoreADIADI::ncalls = 0;

// 	}

// 	if(false)
// 		performance_test_helper(scene,visitor);
// 	ScoreADIADI::ncalls = 0;

// 	scene.visit(visitor);
// 	cout << visitor.result_ << " " << (double)ScoreADIADI::ncalls/1000000.0 << "M" << endl;
// 	ScoreADIADI::ncalls = 0;

// }

}
}
}


#ifndef BACKTRACKING_LINE_SEARCH_HPP
#define BACKTRACKING_LINE_SEARCH_HPP

#include <functional>
#include <Eigen/Core>

namespace mathtoolbox
{
    namespace optimization
    {
        // Procedure 3.1: Backtracking Line Search
        //
        // This algoritmh tries to find an appropriate step size that satisfies
        // the Armijo condition (i.e., the safficient decreasing condition).
        // This algorithm runs faster than the line search algorithm for the
        // strong Wolfe conditions, but it does not guarantee the curvature
        // condition, which is required to stabilize the overall optimization.
        inline double RunBacktrackingLineSearch(const std::function<double(const Eigen::VectorXd&)>& f,
                                                const Eigen::VectorXd& grad,
                                                const Eigen::VectorXd& x,
                                                const Eigen::VectorXd& p,
                                                const double alpha_init,
                                                const double rho,
                                                const double c)
        {
            constexpr unsigned int num_max_iterations = 50;

            unsigned counter = 0;
            double alpha = alpha_init;
            while (true)
            {
                // Equation 3.6a
                const bool armijo_condition = f(x + alpha * p) <= f(x) + c * alpha * grad.transpose() * p;

                if (armijo_condition || counter == num_max_iterations) { break; }

                alpha *= rho;

                ++ counter;
            }
            return alpha;
        }
    }
}

#endif /* BACKTRACKING_LINE_SEARCH_HPP */


//  ================================================================
//  Created by Gregory Kramida on 10/23/18.
//  Copyright (c) 2018 Gregory Kramida
//  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.
//  ================================================================
#pragma once

#include <Eigen/Eigen>

namespace eig = Eigen;

namespace math{

	/**
	 * @param tsdf_value_a
	 * @param tsdf_value_b
	 * @param tolerance
	 * @return whether both SDF values is within tolerance of the truncation thresholds -1.0f and 1.0f
	 */
    inline bool are_both_SDF_values_truncated_tolerance(float tsdf_value_a, float tsdf_value_b, float tolerance = 10e-6f){
        return (1.0f - std::abs(tsdf_value_a) < tolerance && 1.0f - std::abs(tsdf_value_b) < tolerance);
    }
    /**
     * @param tsdf_value_a
     * @param tsdf_value_b
     * @return whether both SDF values have absolute value of 1.0f
     */
    inline bool are_both_SDF_values_truncated(float tsdf_value_a, float tsdf_value_b){
    	return std::abs(tsdf_value_a) == 1.0 && std::abs(tsdf_value_b) == 1.0;
    }
}// namespace math


//==============================================================================
//         Copyright 2003 - 2011   LASMEA UMR 6602 CNRS/Univ. Clermont II
//         Copyright 2009 - 2011   LRI    UMR 8623 CNRS/Univ Paris Sud XI
//
//          Distributed under the Boost Software License, Version 1.0.
//                 See accompanying file LICENSE.txt or copy at
//                     http://www.boost.org/LICENSE_1_0.txt
//==============================================================================
#ifndef NT2_PREDICATES_FUNCTIONS_TABLE_ISMATRIX_HPP_INCLUDED
#define NT2_PREDICATES_FUNCTIONS_TABLE_ISMATRIX_HPP_INCLUDED

#include <nt2/predicates/functions/ismatrix.hpp>
#include <nt2/include/functions/numel.hpp>
#include <nt2/include/functions/extent.hpp>
#include <boost/fusion/include/at.hpp>

namespace nt2 { namespace ext
{
  BOOST_DISPATCH_IMPLEMENT  ( ismatrix_, tag::cpu_
                            , (A0)
                            , (unspecified_<A0>)
                            )
  {
    typedef bool result_type;

    BOOST_FORCEINLINE
    result_type operator()(const A0& a0) const
    {
      typename meta::call<tag::extent_(A0 const&)>::type ex = nt2::extent(a0);
      std::size_t nz = nt2::numel(ex);

      // 4x0x1x1 is matrix but I do not know if 4x0x4x1 is ?!
      return (nz == 0) ||((nz > 0)
                          &&  (boost::fusion::at_c<0>(ex)*boost::fusion::at_c<1>(ex) == nz));
    }
  };
} }

#endif