<commit_before>// Ignore unused parameter warnings coming from cppunit headers
#include <libmesh/ignore_warnings.h>
#include <cppunit/extensions/HelperMacros.h>
#include <cppunit/TestCase.h>
#include <libmesh/restore_warnings.h>

// libmesh includes
#include <libmesh/dense_matrix.h>
#include <libmesh/dense_vector.h>

#ifdef LIBMESH_HAVE_PETSC
#include "libmesh/petsc_macro.h"
#endif

// THE CPPUNIT_TEST_SUITE_END macro expands to code that involves
// std::auto_ptr, which in turn produces -Wdeprecated-declarations
// warnings.  These can be ignored in GCC as long as we wrap the
// offending code in appropriate pragmas.  We can't get away with a
// single ignore_warnings.h inclusion at the beginning of this file,
// since the libmesh headers pull in a restore_warnings.h at some
// point.  We also don't bother restoring warnings at the end of this
// file since it's not a header.
#include <libmesh/ignore_warnings.h>

using namespace libMesh;

class DenseMatrixTest : public CppUnit::TestCase
{
public:
  void setUp() {}

  void tearDown() {}

  CPPUNIT_TEST_SUITE(DenseMatrixTest);

  CPPUNIT_TEST(testSVD);
  CPPUNIT_TEST(testEVDreal);
  CPPUNIT_TEST(testEVDcomplex);
  CPPUNIT_TEST(testComplexSVD);

  CPPUNIT_TEST_SUITE_END();

private:

  void testSVD()
  {
    DenseMatrix<Number> U, VT;
    DenseVector<Real> sigma;
    DenseMatrix<Number> A;

    A.resize(3, 2);
    A(0,0) = 1.0; A(0,1) = 2.0;
    A(1,0) = 3.0; A(1,1) = 4.0;
    A(2,0) = 5.0; A(2,1) = 6.0;

    A.svd(sigma, U, VT);

    // Solution for this case is (verified with numpy)
    DenseMatrix<Number> true_U(3,2), true_VT(2,2);
    DenseVector<Real> true_sigma(2);
    true_U(0,0) = -2.298476964000715e-01; true_U(0,1) = 8.834610176985250e-01;
    true_U(1,0) = -5.247448187602936e-01; true_U(1,1) = 2.407824921325463e-01;
    true_U(2,0) = -8.196419411205157e-01; true_U(2,1) = -4.018960334334318e-01;

    true_VT(0,0) = -6.196294838293402e-01; true_VT(0,1) = -7.848944532670524e-01;
    true_VT(1,0) = -7.848944532670524e-01; true_VT(1,1) = 6.196294838293400e-01;

    true_sigma(0) = 9.525518091565109e+00;
    true_sigma(1) = 5.143005806586446e-01;

    for (unsigned i=0; i<U.m(); ++i)
      for (unsigned j=0; j<U.n(); ++j)
        CPPUNIT_ASSERT_DOUBLES_EQUAL( libmesh_real(U(i,j)), libmesh_real(true_U(i,j)), TOLERANCE*TOLERANCE);

    for (unsigned i=0; i<VT.m(); ++i)
      for (unsigned j=0; j<VT.n(); ++j)
        CPPUNIT_ASSERT_DOUBLES_EQUAL( libmesh_real(VT(i,j)), libmesh_real(true_VT(i,j)), TOLERANCE*TOLERANCE);

    for (unsigned i=0; i<sigma.size(); ++i)
      CPPUNIT_ASSERT_DOUBLES_EQUAL(sigma(i), true_sigma(i), TOLERANCE*TOLERANCE);
  }

  // This function is called by testEVD for different matrices.  The
  // Lapack results are compared to known eigenvalue real and
  // imaginary parts for the matrix in question, which must also be
  // passed in by non-const value, since this routine will sort them
  // in-place.
  void testEVD_helper(DenseMatrix<Real> & A,
                      std::vector<Real> true_lambda_real,
                      std::vector<Real> true_lambda_imag)
  {
    // Note: see bottom of this file, we only do this test if PETSc is
    // available, but this function currently only exists if we're
    // using real numbers.
#ifdef LIBMESH_USE_REAL_NUMBERS
    DenseVector<Real> lambda_real, lambda_imag;
    A.evd(lambda_real, lambda_imag);

    // Sort the results from Lapack *individually*.
    std::sort(lambda_real.get_values().begin(), lambda_real.get_values().end());
    std::sort(lambda_imag.get_values().begin(), lambda_imag.get_values().end());

    // Sort the true eigenvalues *individually*.
    std::sort(true_lambda_real.begin(), true_lambda_real.end());
    std::sort(true_lambda_imag.begin(), true_lambda_imag.end());

    // Compare the individually-sorted values.
    for (unsigned i=0; i<lambda_real.size(); ++i)
      {
        CPPUNIT_ASSERT_DOUBLES_EQUAL(lambda_real(i), true_lambda_real[i], TOLERANCE*TOLERANCE);
        CPPUNIT_ASSERT_DOUBLES_EQUAL(lambda_imag(i), true_lambda_imag[i], TOLERANCE*TOLERANCE);
      }
#endif
  }

  void testEVDreal()
  {
    // This is an example from Matlab's gallery(3) which is a
    // non-symmetric 3x3 matrix with eigen values lambda = 1, 2, 3.
    DenseMatrix<Real> A(3, 3);
    A(0,0) = -149; A(0,1) = -50; A(0,2) = -154;
    A(1,0) =  537; A(1,1) = 180; A(1,2) =  546;
    A(2,0) =  -27; A(2,1) =  -9; A(2,2) =  -25;

    std::vector<Real> true_lambda_real(3);
    true_lambda_real[0] = 1.;
    true_lambda_real[1] = 2.;
    true_lambda_real[2] = 3.;
    std::vector<Real> true_lambda_imag(3); // all zero

    // call helper function to compute and verify results
    testEVD_helper(A, true_lambda_real, true_lambda_imag);
  }

  void testEVDcomplex()
  {
    // This test is also from a Matlab example, and has complex eigenvalues.
    // http://www.mathworks.com/help/matlab/math/eigenvalues.html?s_tid=gn_loc_drop
    DenseMatrix<Real> A(3, 3);
    A(0,0) =  0; A(0,1) = -6; A(0,2) =  -1;
    A(1,0) =  6; A(1,1) =  2; A(1,2) = -16;
    A(2,0) = -5; A(2,1) = 20; A(2,2) = -10;

    std::vector<Real> true_lambda_real(3);
    true_lambda_real[0] = -3.070950351248293;
    true_lambda_real[1] = -2.464524824375853;
    true_lambda_real[2] = -2.464524824375853;
    std::vector<Real> true_lambda_imag(3);
    true_lambda_imag[0] = 0.;
    true_lambda_imag[1] = 17.60083096447099;
    true_lambda_imag[2] = -17.60083096447099;

    // call helper function to compute and verify results
    testEVD_helper(A, true_lambda_real, true_lambda_imag);
  }

  void testComplexSVD()
  {
#ifdef LIBMESH_USE_COMPLEX_NUMBERS
    DenseMatrix<Complex> A(3,3);

    A(0,0) = Complex(2.18904,4.44523e-18); A(0,1) = Complex(-3.20491,-0.136699);   A(0,2) = Complex(0.716316,-0.964802);
    A(1,0) = Complex(-3.20491,0.136699);   A(1,1) = Complex(4.70076,-3.25261e-18); A(1,2) = Complex(-0.98849,1.45727);
    A(2,0) = Complex(0.716316,0.964802);   A(2,1) = Complex(-0.98849,-1.45727);    A(2,2) = Complex(0.659629,-4.01155e-18);

    DenseVector<Real> sigma;
    A.svd(sigma);

    DenseVector<Real> true_sigma(3);
    true_sigma(0) = 7.54942516052;
    true_sigma(1) = 3.17479511368e-06;
    true_sigma(2) = 6.64680908281e-07;

    for (unsigned i=0; i<sigma.size(); ++i)
      CPPUNIT_ASSERT_DOUBLES_EQUAL(sigma(i), true_sigma(i), 1.e-10);
#endif
  }

};

// Only run the test if we expect it can actually work!
#ifdef LIBMESH_HAVE_PETSC
#if !PETSC_VERSION_LESS_THAN(3,1,0)
  CPPUNIT_TEST_SUITE_REGISTRATION(DenseMatrixTest);
#endif
#endif
<commit_msg>Add unit test verifying left and right eigenvector calculation.<commit_after>// Ignore unused parameter warnings coming from cppunit headers
#include <libmesh/ignore_warnings.h>
#include <cppunit/extensions/HelperMacros.h>
#include <cppunit/TestCase.h>
#include <libmesh/restore_warnings.h>

// libmesh includes
#include <libmesh/dense_matrix.h>
#include <libmesh/dense_vector.h>

#ifdef LIBMESH_HAVE_PETSC
#include "libmesh/petsc_macro.h"
#endif

// THE CPPUNIT_TEST_SUITE_END macro expands to code that involves
// std::auto_ptr, which in turn produces -Wdeprecated-declarations
// warnings.  These can be ignored in GCC as long as we wrap the
// offending code in appropriate pragmas.  We can't get away with a
// single ignore_warnings.h inclusion at the beginning of this file,
// since the libmesh headers pull in a restore_warnings.h at some
// point.  We also don't bother restoring warnings at the end of this
// file since it's not a header.
#include <libmesh/ignore_warnings.h>

using namespace libMesh;

class DenseMatrixTest : public CppUnit::TestCase
{
public:
  void setUp() {}

  void tearDown() {}

  CPPUNIT_TEST_SUITE(DenseMatrixTest);

  CPPUNIT_TEST(testSVD);
  CPPUNIT_TEST(testEVDreal);
  CPPUNIT_TEST(testEVDcomplex);
  CPPUNIT_TEST(testComplexSVD);

  CPPUNIT_TEST_SUITE_END();

private:

  void testSVD()
  {
    DenseMatrix<Number> U, VT;
    DenseVector<Real> sigma;
    DenseMatrix<Number> A;

    A.resize(3, 2);
    A(0,0) = 1.0; A(0,1) = 2.0;
    A(1,0) = 3.0; A(1,1) = 4.0;
    A(2,0) = 5.0; A(2,1) = 6.0;

    A.svd(sigma, U, VT);

    // Solution for this case is (verified with numpy)
    DenseMatrix<Number> true_U(3,2), true_VT(2,2);
    DenseVector<Real> true_sigma(2);
    true_U(0,0) = -2.298476964000715e-01; true_U(0,1) = 8.834610176985250e-01;
    true_U(1,0) = -5.247448187602936e-01; true_U(1,1) = 2.407824921325463e-01;
    true_U(2,0) = -8.196419411205157e-01; true_U(2,1) = -4.018960334334318e-01;

    true_VT(0,0) = -6.196294838293402e-01; true_VT(0,1) = -7.848944532670524e-01;
    true_VT(1,0) = -7.848944532670524e-01; true_VT(1,1) = 6.196294838293400e-01;

    true_sigma(0) = 9.525518091565109e+00;
    true_sigma(1) = 5.143005806586446e-01;

    for (unsigned i=0; i<U.m(); ++i)
      for (unsigned j=0; j<U.n(); ++j)
        CPPUNIT_ASSERT_DOUBLES_EQUAL( libmesh_real(U(i,j)), libmesh_real(true_U(i,j)), TOLERANCE*TOLERANCE);

    for (unsigned i=0; i<VT.m(); ++i)
      for (unsigned j=0; j<VT.n(); ++j)
        CPPUNIT_ASSERT_DOUBLES_EQUAL( libmesh_real(VT(i,j)), libmesh_real(true_VT(i,j)), TOLERANCE*TOLERANCE);

    for (unsigned i=0; i<sigma.size(); ++i)
      CPPUNIT_ASSERT_DOUBLES_EQUAL(sigma(i), true_sigma(i), TOLERANCE*TOLERANCE);
  }

  // This function is called by testEVD for different matrices.  The
  // Lapack results are compared to known eigenvalue real and
  // imaginary parts for the matrix in question, which must also be
  // passed in by non-const value, since this routine will sort them
  // in-place.
  void testEVD_helper(DenseMatrix<Real> & A,
                      std::vector<Real> true_lambda_real,
                      std::vector<Real> true_lambda_imag)
  {
    // Note: see bottom of this file, we only do this test if PETSc is
    // available, but this function currently only exists if we're
    // using real numbers.
#ifdef LIBMESH_USE_REAL_NUMBERS
    // Let's compute the eigenvalues on a copy of A, so that we can
    // use the original to check the computation.
    DenseMatrix<Real> A_copy = A;

    DenseVector<Real> lambda_real, lambda_imag;
    DenseMatrix<Real> VR; // right eigenvectors
    DenseMatrix<Real> VL; // left eigenvectors
    A_copy.evd_left_and_right(lambda_real, lambda_imag, VL, VR);

    // The matrix is square and of size N x N.
    const unsigned N = A.m();

    // Verify left eigen-values.
    // Test that the right eigenvalues are self-consistent by computing
    // u_j**H * A = lambda_j * u_j**H
    // Note that we have to handle real and complex eigenvalues
    // differently, since complex eigenvectors share their storage.
    for (unsigned eigenval=0; eigenval<N; ++eigenval)
      {
        // Only check real eigenvalues
        if (std::abs(lambda_imag(eigenval)) < TOLERANCE*TOLERANCE)
          {
            // remove print libMesh::out << "Checking eigenvalue: " << eigenval << std::endl;
            DenseVector<Real> lhs(N), rhs(N);
            for (unsigned i=0; i<N; ++i)
              {
                rhs(i) = lambda_real(eigenval) * VL(i, eigenval);
                for (unsigned j=0; j<N; ++j)
                  lhs(i) += A(j, i) * VL(j, eigenval); // Note: A(j,i)
              }

            // Subtract and assert that the norm of the difference is
            // below some tolerance.
            lhs -= rhs;
            CPPUNIT_ASSERT_DOUBLES_EQUAL(/*expected=*/0., /*actual=*/lhs.l2_norm(), std::sqrt(TOLERANCE)*TOLERANCE);
          }
        else
          {
            // This is a complex eigenvalue, so:
            // a.) It occurs in a complex-conjugate pair
            // b.) the real part of the eigenvector is stored is VL(:,eigenval)
            // c.) the imag part of the eigenvector is stored in VL(:,eigenval+1)
            //
            // Equating the real and imaginary parts of Ax=lambda*x leads to two sets
            // of relations that must hold:
            // 1.) A^T x_r =  lambda_r*x_r + lambda_i*x_i
            // 2.) A^T x_i = -lambda_i*x_r + lambda_r*x_i
            // which we can verify.

            // 1.)
            DenseVector<Real> lhs(N), rhs(N);
            for (unsigned i=0; i<N; ++i)
              {
                rhs(i) = lambda_real(eigenval) * VL(i, eigenval) + lambda_imag(eigenval) * VL(i, eigenval+1);
                for (unsigned j=0; j<N; ++j)
                  lhs(i) += A(j, i) * VL(j, eigenval); // Note: A(j,i)
              }

            lhs -= rhs;
            CPPUNIT_ASSERT_DOUBLES_EQUAL(/*expected=*/0., /*actual=*/lhs.l2_norm(), std::sqrt(TOLERANCE)*TOLERANCE);

            // libMesh::out << "lhs=" << std::endl;
            // lhs.print_scientific(libMesh::out, /*precision=*/15);
            //
            // libMesh::out << "rhs=" << std::endl;
            // rhs.print_scientific(libMesh::out, /*precision=*/15);

            // 2.)
            lhs.zero();
            rhs.zero();
            for (unsigned i=0; i<N; ++i)
              {
                rhs(i) = -lambda_imag(eigenval) * VL(i, eigenval) + lambda_real(eigenval) * VL(i, eigenval+1);
                for (unsigned j=0; j<N; ++j)
                  lhs(i) += A(j, i) * VL(j, eigenval+1); // Note: A(j,i)
              }

            lhs -= rhs;
            CPPUNIT_ASSERT_DOUBLES_EQUAL(/*expected=*/0., /*actual=*/lhs.l2_norm(), std::sqrt(TOLERANCE)*TOLERANCE);

            // libMesh::out << "lhs=" << std::endl;
            // lhs.print_scientific(libMesh::out, /*precision=*/15);
            //
            // libMesh::out << "rhs=" << std::endl;
            // rhs.print_scientific(libMesh::out, /*precision=*/15);

            // We'll skip the second member of the complex conjugate
            // pair.  If the first one worked, the second one should
            // as well...
            eigenval += 1;
          }
      }

    // Verify right eigen-values.
    // Test that the right eigenvalues are self-consistent by computing
    // A * v_j - lambda_j * v_j
    // Note that we have to handle real and complex eigenvalues
    // differently, since complex eigenvectors share their storage.
    for (unsigned eigenval=0; eigenval<N; ++eigenval)
      {
        // Only check real eigenvalues
        if (std::abs(lambda_imag(eigenval)) < TOLERANCE*TOLERANCE)
          {
            // remove print libMesh::out << "Checking eigenvalue: " << eigenval << std::endl;
            DenseVector<Real> lhs(N), rhs(N);
            for (unsigned i=0; i<N; ++i)
              {
                rhs(i) = lambda_real(eigenval) * VR(i, eigenval);
                for (unsigned j=0; j<N; ++j)
                  lhs(i) += A(i, j) * VR(j, eigenval);
              }

            lhs -= rhs;
            CPPUNIT_ASSERT_DOUBLES_EQUAL(/*expected=*/0., /*actual=*/lhs.l2_norm(), std::sqrt(TOLERANCE)*TOLERANCE);
          }
        else
          {
            // This is a complex eigenvalue, so:
            // a.) It occurs in a complex-conjugate pair
            // b.) the real part of the eigenvector is stored is VR(:,eigenval)
            // c.) the imag part of the eigenvector is stored in VR(:,eigenval+1)
            //
            // Equating the real and imaginary parts of Ax=lambda*x leads to two sets
            // of relations that must hold:
            // 1.) Ax_r = lambda_r*x_r - lambda_i*x_i
            // 2.) Ax_i = lambda_i*x_r + lambda_r*x_i
            // which we can verify.

            // 1.)
            DenseVector<Real> lhs(N), rhs(N);
            for (unsigned i=0; i<N; ++i)
              {
                rhs(i) = lambda_real(eigenval) * VR(i, eigenval) - lambda_imag(eigenval) * VR(i, eigenval+1);
                for (unsigned j=0; j<N; ++j)
                  lhs(i) += A(i, j) * VR(j, eigenval);
              }

            lhs -= rhs;
            CPPUNIT_ASSERT_DOUBLES_EQUAL(/*expected=*/0., /*actual=*/lhs.l2_norm(), std::sqrt(TOLERANCE)*TOLERANCE);

            // 2.)
            lhs.zero();
            rhs.zero();
            for (unsigned i=0; i<N; ++i)
              {
                rhs(i) = lambda_imag(eigenval) * VR(i, eigenval) + lambda_real(eigenval) * VR(i, eigenval+1);
                for (unsigned j=0; j<N; ++j)
                  lhs(i) += A(i, j) * VR(j, eigenval+1);
              }

            lhs -= rhs;
            CPPUNIT_ASSERT_DOUBLES_EQUAL(/*expected=*/0., /*actual=*/lhs.l2_norm(), std::sqrt(TOLERANCE)*TOLERANCE);

            // We'll skip the second member of the complex conjugate
            // pair.  If the first one worked, the second one should
            // as well...
            eigenval += 1;
          }
      }

    // Sort the results from Lapack *individually*.
    std::sort(lambda_real.get_values().begin(), lambda_real.get_values().end());
    std::sort(lambda_imag.get_values().begin(), lambda_imag.get_values().end());

    // Sort the true eigenvalues *individually*.
    std::sort(true_lambda_real.begin(), true_lambda_real.end());
    std::sort(true_lambda_imag.begin(), true_lambda_imag.end());

    // Compare the individually-sorted values.
    for (unsigned i=0; i<lambda_real.size(); ++i)
      {
        // Note: I initially verified the results with TOLERANCE**2,
        // but that turned out to be just a bit too tight for some of
        // the test problems.  I'm not sure what controls the accuracy
        // of the eigenvalue computation in LAPACK, there is no way to
        // set a tolerance in the LAPACKgeev_ interface.
        CPPUNIT_ASSERT_DOUBLES_EQUAL(/*expected=*/true_lambda_real[i], /*actual=*/lambda_real(i), std::sqrt(TOLERANCE)*TOLERANCE);
        CPPUNIT_ASSERT_DOUBLES_EQUAL(/*expected=*/true_lambda_imag[i], /*actual=*/lambda_imag(i), std::sqrt(TOLERANCE)*TOLERANCE);
      }
#endif
  }

  void testEVDreal()
  {
    // This is an example from Matlab's gallery(3) which is a
    // non-symmetric 3x3 matrix with eigen values lambda = 1, 2, 3.
    DenseMatrix<Real> A(3, 3);
    A(0,0) = -149; A(0,1) = -50; A(0,2) = -154;
    A(1,0) =  537; A(1,1) = 180; A(1,2) =  546;
    A(2,0) =  -27; A(2,1) =  -9; A(2,2) =  -25;

    std::vector<Real> true_lambda_real(3);
    true_lambda_real[0] = 1.;
    true_lambda_real[1] = 2.;
    true_lambda_real[2] = 3.;
    std::vector<Real> true_lambda_imag(3); // all zero

    // call helper function to compute and verify results.
    testEVD_helper(A, true_lambda_real, true_lambda_imag);
  }

  void testEVDcomplex()
  {
    // This test is also from a Matlab example, and has complex eigenvalues.
    // http://www.mathworks.com/help/matlab/math/eigenvalues.html?s_tid=gn_loc_drop
    DenseMatrix<Real> A(3, 3);
    A(0,0) =  0; A(0,1) = -6; A(0,2) =  -1;
    A(1,0) =  6; A(1,1) =  2; A(1,2) = -16;
    A(2,0) = -5; A(2,1) = 20; A(2,2) = -10;

    std::vector<Real> true_lambda_real(3);
    true_lambda_real[0] = -3.070950351248293;
    true_lambda_real[1] = -2.464524824375853;
    true_lambda_real[2] = -2.464524824375853;
    std::vector<Real> true_lambda_imag(3);
    true_lambda_imag[0] = 0.;
    true_lambda_imag[1] = 17.60083096447099;
    true_lambda_imag[2] = -17.60083096447099;

    // call helper function to compute and verify results
    testEVD_helper(A, true_lambda_real, true_lambda_imag);
  }

  void testComplexSVD()
  {
#ifdef LIBMESH_USE_COMPLEX_NUMBERS
    DenseMatrix<Complex> A(3,3);

    A(0,0) = Complex(2.18904,4.44523e-18); A(0,1) = Complex(-3.20491,-0.136699);   A(0,2) = Complex(0.716316,-0.964802);
    A(1,0) = Complex(-3.20491,0.136699);   A(1,1) = Complex(4.70076,-3.25261e-18); A(1,2) = Complex(-0.98849,1.45727);
    A(2,0) = Complex(0.716316,0.964802);   A(2,1) = Complex(-0.98849,-1.45727);    A(2,2) = Complex(0.659629,-4.01155e-18);

    DenseVector<Real> sigma;
    A.svd(sigma);

    DenseVector<Real> true_sigma(3);
    true_sigma(0) = 7.54942516052;
    true_sigma(1) = 3.17479511368e-06;
    true_sigma(2) = 6.64680908281e-07;

    for (unsigned i=0; i<sigma.size(); ++i)
      CPPUNIT_ASSERT_DOUBLES_EQUAL(sigma(i), true_sigma(i), 1.e-10);
#endif
  }

};

// Only run the test if we expect it can actually work!
#ifdef LIBMESH_HAVE_PETSC
#if !PETSC_VERSION_LESS_THAN(3,1,0)
  CPPUNIT_TEST_SUITE_REGISTRATION(DenseMatrixTest);
#endif
#endif
<|endoftext|>


<commit_before>#define CATCH_CONFIG_MAIN
#include "../../vendor/catch/catch.hpp"

#include "thread_utility.hpp"
#include "version_monitor.hpp"

TEST_CASE("initialize") {
  krbn::thread_utility::register_main_thread();
}

TEST_CASE("file_monitor") {
  system("rm -rf target");
  system("mkdir -p target/sub/");
  system("echo 1.0.0 > target/sub/version");

  {
    krbn::version_monitor version_monitor("target/sub/version");

    std::string last_changed_version;

    version_monitor.changed.connect([&](auto&& version) {
      last_changed_version = version;
    });

    version_monitor.start();

    std::this_thread::sleep_for(std::chrono::milliseconds(500));

    REQUIRE(last_changed_version.empty());

    // ========================================
    // Update version
    // ========================================

    last_changed_version.clear();

    system("echo 1.1.0 > target/sub/version");

    std::this_thread::sleep_for(std::chrono::milliseconds(500));

    REQUIRE(last_changed_version == "1.1.0");

    // ========================================
    // The callback is not called if the file contents is not changed.
    // ========================================

    last_changed_version.clear();

    system("touch target/sub/version");

    std::this_thread::sleep_for(std::chrono::milliseconds(500));

    REQUIRE(last_changed_version.empty());

    // ========================================
    // `manual_check` does not invoke callback if the version file is not actually changed.
    // ========================================

    last_changed_version.clear();

    version_monitor.manual_check();

    std::this_thread::sleep_for(std::chrono::milliseconds(500));

    REQUIRE(last_changed_version.empty());

    // ========================================
    // Self update is ignored by `kFSEventStreamCreateFlagIgnoreSelf`
    // ========================================

    last_changed_version.clear();

    {
      std::ofstream("target/sub/version") << "1.2.0";
    }

    std::this_thread::sleep_for(std::chrono::milliseconds(500));

    REQUIRE(last_changed_version.empty());

    // ========================================
    // `manual_check`
    // ========================================

    last_changed_version.clear();

    version_monitor.manual_check();

    std::this_thread::sleep_for(std::chrono::milliseconds(500));

    REQUIRE(last_changed_version == "1.2.0");

    // ========================================
    // Update version again
    // ========================================

    last_changed_version.clear();

    system("echo 1.3.0 > target/sub/version");

    std::this_thread::sleep_for(std::chrono::milliseconds(500));

    REQUIRE(last_changed_version == "1.3.0");
  }
}
<commit_msg>fix TEST_CASE name<commit_after>#define CATCH_CONFIG_MAIN
#include "../../vendor/catch/catch.hpp"

#include "thread_utility.hpp"
#include "version_monitor.hpp"

TEST_CASE("initialize") {
  krbn::thread_utility::register_main_thread();
}

TEST_CASE("version_monitor") {
  system("rm -rf target");
  system("mkdir -p target/sub/");
  system("echo 1.0.0 > target/sub/version");

  {
    krbn::version_monitor version_monitor("target/sub/version");

    std::string last_changed_version;

    version_monitor.changed.connect([&](auto&& version) {
      last_changed_version = version;
    });

    version_monitor.start();

    std::this_thread::sleep_for(std::chrono::milliseconds(500));

    REQUIRE(last_changed_version.empty());

    // ========================================
    // Update version
    // ========================================

    last_changed_version.clear();

    system("echo 1.1.0 > target/sub/version");

    std::this_thread::sleep_for(std::chrono::milliseconds(500));

    REQUIRE(last_changed_version == "1.1.0");

    // ========================================
    // The callback is not called if the file contents is not changed.
    // ========================================

    last_changed_version.clear();

    system("touch target/sub/version");

    std::this_thread::sleep_for(std::chrono::milliseconds(500));

    REQUIRE(last_changed_version.empty());

    // ========================================
    // `manual_check` does not invoke callback if the version file is not actually changed.
    // ========================================

    last_changed_version.clear();

    version_monitor.manual_check();

    std::this_thread::sleep_for(std::chrono::milliseconds(500));

    REQUIRE(last_changed_version.empty());

    // ========================================
    // Self update is ignored by `kFSEventStreamCreateFlagIgnoreSelf`
    // ========================================

    last_changed_version.clear();

    {
      std::ofstream("target/sub/version") << "1.2.0";
    }

    std::this_thread::sleep_for(std::chrono::milliseconds(500));

    REQUIRE(last_changed_version.empty());

    // ========================================
    // `manual_check`
    // ========================================

    last_changed_version.clear();

    version_monitor.manual_check();

    std::this_thread::sleep_for(std::chrono::milliseconds(500));

    REQUIRE(last_changed_version == "1.2.0");

    // ========================================
    // Update version again
    // ========================================

    last_changed_version.clear();

    system("echo 1.3.0 > target/sub/version");

    std::this_thread::sleep_for(std::chrono::milliseconds(500));

    REQUIRE(last_changed_version == "1.3.0");
  }
}
<|endoftext|>


<commit_before>/*
 * Database.cpp
 *
 *  Created on: 21 Aug 2013
 *      Author: nicholas
 */

#include "../include/Database.hpp"
#include <string>

#include <odb/transaction.hxx>
#include <odb/result.hxx>
#include <odb/schema-catalog.hxx>
#include <odb/sqlite/exceptions.hxx>

#include "table/ImageRecord.hpp"
#include "table/ImageRecord-odb.hxx"

#include "table/FilterRecord.hpp"
#include "table/FilterRecord-odb.hxx"

const char *dbName = "imageHasher.db";

const char *insertImageQuery = "INSERT INTO `imagerecord` (`path`,`sha_id`,`phash_id`) VALUES (?,?,?);";
const char *insertInvalidQuery = "INSERT INTO `badfilerecord` (`path`) VALUES (?);";
const char *insertFilterQuery = "INSERT OR IGNORE INTO `filterrecord` (`phash_id`, `reason`) VALUES (?,?);";
const char *prunePathQuery = "SELECT `path` FROM `imagerecord` WHERE `path` LIKE ? UNION SELECT `path` FROM `badfilerecord` WHERE `path` LIKE ?;";
const char *prunePathDeleteImage = "DELETE FROM `imagerecord` WHERE `path` = ?;";
const char *prunePathDeleteBadFile = "DELETE FROM `badfilerecord` WHERE `path` = ?;";
const char *checkExistsQuery = "SELECT EXISTS(SELECT 1 FROM `imagerecord` WHERE `path` = ? LIMIT 1) OR EXISTS(SELECT 1 FROM `badfilerecord`  WHERE `path` = ?  LIMIT 1);";
const char *checkSHAQuery = "SELECT EXISTS(SELECT 1 FROM `imagerecord` JOIN `sha_hash` ON `sha_id`= `id` WHERE `path` = ? AND `sha256` != '' LIMIT 1);";
const char *updateSha = "UPDATE `imagerecord` SET `sha_id`=? WHERE `path` = ?;";
const char *getSHAQuery = "SELECT `sha256` FROM `imagerecord` AS ir JOIN `sha_hash` AS h ON ir.sha_id=h.id WHERE `path` = ?;";
const char *getPhashQuery = "SELECT `pHash` FROM `imagerecord` AS ir JOIN `phash_hash` AS h ON ir.sha_id=h.id WHERE `path` = ?;";
const char *getSHAidQuery = "SELECT `id` FROM `sha_hash` WHERE `sha256`= ?;";
const char *getpHashidQuery = "SELECT `id` FROM `phash_hash` WHERE `pHash`= ?;";
const char *insertShaRecordQuery = "INSERT INTO `sha_hash` (`sha256`) VALUES (?);";
const char *insertpHashRecordQuery = "INSERT INTO `phash_hash` (`pHash`) VALUES (?);";

const char *startTransactionQuery = "BEGIN TRANSACTION;";
const char *commitTransactionQuery = "COMMIT TRANSACTION;";

using namespace odb;
using namespace imageHasher::db::table;

Database::Database(const char* dbPath) {
	dbName = dbPath;
	init();
}

Database::Database() {
	init();
}

Database::~Database() {
	shutdown();
	delete orm_db;
}

int Database::flush() {
	int drainCount = 0;

	LOG4CPLUS_INFO(logger, "Flushing lists...");

	drainCount = drain();
	drainCount += drain();

	return drainCount;
}

void Database::shutdown() {
	if (running) {
		LOG4CPLUS_INFO(logger, "Shutting down...");
		running = false;
		LOG4CPLUS_INFO(logger, "Waiting for db worker to finish...");
		workerThread->interrupt();
		workerThread->join();
		LOG4CPLUS_INFO(logger, "Closing database...");
	}
}

void Database::exec(const char* command) {
	orm_db->execute(command);
}

void Database::init() {
	boost::mutex::scoped_lock lock(dbMutex);
	this->currentList = &dataA;
	this->recordsWritten = 0;
	this->running = true;
	logger = log4cplus::Logger::getInstance(LOG4CPLUS_TEXT("Database"));

	setupDatabase();
	prepareStatements();

	workerThread = new boost::thread(&Database::doWork, this);
}

bool Database::is_db_initialised() {
	Hash hash;
	bool exists = true;
	transaction t(orm_db->begin());
	try {
		exists = orm_db->find(1, hash);
	} catch (odb::sqlite::database_exception& e) {
		exists = false;
	}
	t.commit();
	return exists;
}

void Database::initialise_db() {
	transaction t(orm_db->begin());
	odb: schema_catalog::create_schema(*orm_db, "", false);
	t.commit();
	addHashEntry("", 0);
}

void Database::setupDatabase() {
	orm_db = new sqlite::database(dbName,SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE);

	LOG4CPLUS_INFO(logger, "Setting up database " << dbName);

	bool init_done = is_db_initialised();

	if(! init_done) {
		initialise_db();
	}

	transaction t_pragma (orm_db->begin());
		orm_db->execute("PRAGMA page_size = 4096;");
		orm_db->execute("PRAGMA cache_size=10000;");
		orm_db->execute("PRAGMA locking_mode=EXCLUSIVE;");
		orm_db->execute("PRAGMA temp_store = MEMORY;");
		orm_db->execute("PRAGMA journal_mode=MEMORY;");
	t_pragma.commit();

	// perform out of transaction execution
	connection_ptr c (orm_db->connection());
	c->execute("PRAGMA synchronous=NORMAL;");
}

void Database::add(db_data data) {
	boost::mutex::scoped_lock lock(flipMutex);
	currentList->push_back(data);
}

void Database::flipLists() {
	boost::mutex::scoped_lock lock(flipMutex);

	if(currentList == &dataA) {
		currentList = &dataB;
	}else{
		currentList = &dataA;
	}
}

int Database::drain() {
	boost::mutex::scoped_lock lock(dbMutex);
	std::list<db_data>* workList;
	int drainCount = 0;

	workList = currentList;
	flipLists();

	for(std::list<db_data>::iterator ite = workList->begin(); ite != workList->end(); ++ite) {
		addToBatch(*ite);
		recordsWritten++;
		drainCount++;
	}

	workList->clear();
	return drainCount;
}

bool Database::entryExists(fs::path filePath) {
	LOG4CPLUS_DEBUG(logger, "Looking for file " << filePath);

	ImageRecord ir = get_imagerecord(filePath);

	return ir.is_valid();
}

bool Database::entryExists(db_data data) {
	return entryExists(data.filePath);
}

std::list<fs::path> Database::getFilesWithPath(fs::path directoryPath) {
	std::list<fs::path> filePaths;
	std::string path_query(directoryPath.string());
	path_query += "%";

	LOG4CPLUS_INFO(logger, "Looking for files with path " << directoryPath);

	transaction t (orm_db->begin());
	result<ImageRecord> r (orm_db->query<ImageRecord>(query<ImageRecord>::path.like(path_query)));

	for(result<ImageRecord>::iterator itr (r.begin()); itr != r.end(); ++itr) {
		filePaths.push_back(fs::path(itr->getPath()));
	}

	t.commit();

	LOG4CPLUS_INFO(logger, "Found  " << filePaths.size() << " records for path " << directoryPath);
	return filePaths;
}

void Database::prunePath(std::list<fs::path> filePaths) {
	for(std::list<fs::path>::iterator ite = filePaths.begin(); ite != filePaths.end(); ++ite){
		ImageRecord ir = get_imagerecord(*ite);

		transaction t (orm_db->begin());

		if(ir.is_valid()) {
			orm_db->erase(ir);
		}

		t.commit();
	}
}

void Database::addToBatch(db_data data) {
	int response = 0;
	int hashId = -1;

	switch (data.status) {
	case OK:
		add_record(data);
		break;

	case INVALID:
		add_invalid(data);
		break;

	case FILTER:
		add_filter(data);
		break;

	default:
		LOG4CPLUS_ERROR(logger, "Unhandled state encountered");
		throw "Unhandled state encountered";
		break;
	}
}

void Database::add_record(db_data data) {
	Hash hash = get_hash(data.sha256);

	if (!hash.is_valid()) {
		hash = addHashEntry(data.sha256, data.pHash);
	}

	if(entryExists(data)) {
		LOG4CPLUS_DEBUG(logger, "Entry for " << data.filePath << " already exists, discarding...");
		recordsWritten--;
		return;
	}

	transaction t(orm_db->begin());

	ImageRecord ir = ImageRecord(data.filePath.string(), &hash);
	orm_db->persist(ir);

	t.commit();
}

int Database::add_path_placeholder(std::string path) {

	transaction t (orm_db->begin());

	ImageRecord ir (path,NULL);
	int id = orm_db->persist(ir);

	t.commit();

	return id;
}

void Database::add_invalid(db_data data) {
	LOG4CPLUS_DEBUG(logger, "File with path " << data.filePath << " is invalid");
	recordsWritten--;
	Hash hash = get_hash("");
	transaction t(orm_db->begin());
	ImageRecord ir = ImageRecord(data.filePath.string(), &hash);
	orm_db->persist(ir);

	t.commit();
}

void Database::add_filter(db_data data) {
	LOG4CPLUS_DEBUG(logger, "Adding filter record for pHash" << data.pHash << " with reason " << data.reason);

	imageHasher::db::table::FilterRecord fr(data.pHash, data.reason);

	transaction t (orm_db->begin());

	orm_db->persist(fr);

	t.commit();
}

void Database::prepareStatements() {
	LOG4CPLUS_INFO(logger, "Creating prepared statements...");
}

void Database::doWork() {
	while(running) {
		try{
			boost::this_thread::sleep_for(boost::chrono::seconds(3));
		}catch(boost::thread_interrupted&) {
			LOG4CPLUS_INFO(logger,"DB thread interrupted");
		}

		if(currentList->size() > 1000) {
			int drainCount = drain();
			LOG4CPLUS_INFO(logger, drainCount << " records processed, Total: " << recordsWritten);
		}
	}

	// make sure both lists are committed
	int drainCount = flush();
	LOG4CPLUS_INFO(logger, drainCount << " records processed, Total: " << recordsWritten);
}

unsigned int Database::getRecordsWritten() {
	return recordsWritten;
}

std::string Database::getSHA(fs::path filepath) {
	std::string sha = "";
	LOG4CPLUS_DEBUG(logger, "Getting SHA for path " << filepath);

	transaction t (orm_db->begin());

	result<ImageRecord> r (orm_db->query<ImageRecord>(query<ImageRecord>::path == filepath.string()));

	for(result<ImageRecord>::iterator itr (r.begin()); itr != r.end(); ++itr) {
		Hash hash = itr->get_hash();
		sha = hash.get_sha256();
		break;
	}

	t.commit();

	return sha;
}

bool Database::sha_exists(std::string sha) {
	Hash hash = get_hash(sha);
	return hash.is_valid();
}

int64_t Database::getPhash(fs::path filepath) {
	LOG4CPLUS_DEBUG(logger, "Getting pHash for path " << filepath);

	ImageRecord ir = get_imagerecord(filepath);

	if(ir.is_valid()) {
		return ir.get_hash().get_pHash();
	}

	return -1;
}

imageHasher::db::table::Hash Database::get_hash(std::string sha) {
	Hash hash;
	LOG4CPLUS_DEBUG(logger, "Getting hash for sha " << sha);

	transaction t (orm_db->begin());

	result<Hash> r (orm_db->query<Hash>(query<Hash>::sha256 == sha));

	for(result<Hash>::iterator itr (r.begin()); itr != r.end(); ++itr) {
		hash = *itr;
		break;
	}

	t.commit();

	return hash;
}

imageHasher::db::table::Hash Database::get_hash(u_int64_t phash) {
	Hash hash;
	LOG4CPLUS_DEBUG(logger, "Getting hash for pHash " << phash);

	transaction t (orm_db->begin());

	result<Hash> r (orm_db->query<Hash>(query<Hash>::pHash == phash));

	for(result<Hash>::iterator itr (r.begin()); itr != r.end(); ++itr) {
		hash = *itr;
		break;
	}

	t.commit();

	return hash;
}

imageHasher::db::table::ImageRecord Database::get_imagerecord(fs::path filepath) {
	ImageRecord ir;

	transaction t (orm_db->begin());

	result<ImageRecord> r (orm_db->query<ImageRecord>(query<ImageRecord>::path == filepath.string()));

	for(result<ImageRecord>::iterator itr (r.begin()); itr != r.end(); ++itr) {
		ir = *itr;
		break;
	}

	t.commit();

	return ir;
}

Hash Database::addHashEntry(std::string sha, u_int64_t pHash) {
	Hash hash(sha,pHash);
	transaction t (orm_db->begin());
		orm_db->persist(hash);
	t.commit();

	return hash;
}
<commit_msg>Fixed workerthread not deleted after join<commit_after>/*
 * Database.cpp
 *
 *  Created on: 21 Aug 2013
 *      Author: nicholas
 */

#include "../include/Database.hpp"
#include <string>

#include <odb/transaction.hxx>
#include <odb/result.hxx>
#include <odb/schema-catalog.hxx>
#include <odb/sqlite/exceptions.hxx>

#include "table/ImageRecord.hpp"
#include "table/ImageRecord-odb.hxx"

#include "table/FilterRecord.hpp"
#include "table/FilterRecord-odb.hxx"

const char *dbName = "imageHasher.db";

const char *insertImageQuery = "INSERT INTO `imagerecord` (`path`,`sha_id`,`phash_id`) VALUES (?,?,?);";
const char *insertInvalidQuery = "INSERT INTO `badfilerecord` (`path`) VALUES (?);";
const char *insertFilterQuery = "INSERT OR IGNORE INTO `filterrecord` (`phash_id`, `reason`) VALUES (?,?);";
const char *prunePathQuery = "SELECT `path` FROM `imagerecord` WHERE `path` LIKE ? UNION SELECT `path` FROM `badfilerecord` WHERE `path` LIKE ?;";
const char *prunePathDeleteImage = "DELETE FROM `imagerecord` WHERE `path` = ?;";
const char *prunePathDeleteBadFile = "DELETE FROM `badfilerecord` WHERE `path` = ?;";
const char *checkExistsQuery = "SELECT EXISTS(SELECT 1 FROM `imagerecord` WHERE `path` = ? LIMIT 1) OR EXISTS(SELECT 1 FROM `badfilerecord`  WHERE `path` = ?  LIMIT 1);";
const char *checkSHAQuery = "SELECT EXISTS(SELECT 1 FROM `imagerecord` JOIN `sha_hash` ON `sha_id`= `id` WHERE `path` = ? AND `sha256` != '' LIMIT 1);";
const char *updateSha = "UPDATE `imagerecord` SET `sha_id`=? WHERE `path` = ?;";
const char *getSHAQuery = "SELECT `sha256` FROM `imagerecord` AS ir JOIN `sha_hash` AS h ON ir.sha_id=h.id WHERE `path` = ?;";
const char *getPhashQuery = "SELECT `pHash` FROM `imagerecord` AS ir JOIN `phash_hash` AS h ON ir.sha_id=h.id WHERE `path` = ?;";
const char *getSHAidQuery = "SELECT `id` FROM `sha_hash` WHERE `sha256`= ?;";
const char *getpHashidQuery = "SELECT `id` FROM `phash_hash` WHERE `pHash`= ?;";
const char *insertShaRecordQuery = "INSERT INTO `sha_hash` (`sha256`) VALUES (?);";
const char *insertpHashRecordQuery = "INSERT INTO `phash_hash` (`pHash`) VALUES (?);";

const char *startTransactionQuery = "BEGIN TRANSACTION;";
const char *commitTransactionQuery = "COMMIT TRANSACTION;";

using namespace odb;
using namespace imageHasher::db::table;

Database::Database(const char* dbPath) {
	dbName = dbPath;
	init();
}

Database::Database() {
	init();
}

Database::~Database() {
	shutdown();
	delete orm_db;
}

int Database::flush() {
	int drainCount = 0;

	LOG4CPLUS_INFO(logger, "Flushing lists...");

	drainCount = drain();
	drainCount += drain();

	return drainCount;
}

void Database::shutdown() {
	if (running) {
		LOG4CPLUS_INFO(logger, "Shutting down...");
		running = false;
		LOG4CPLUS_INFO(logger, "Waiting for db worker to finish...");
		workerThread->interrupt();
		workerThread->join();
		delete(workerThread);
		LOG4CPLUS_INFO(logger, "Closing database...");
	}
}

void Database::exec(const char* command) {
	orm_db->execute(command);
}

void Database::init() {
	boost::mutex::scoped_lock lock(dbMutex);
	this->currentList = &dataA;
	this->recordsWritten = 0;
	this->running = true;
	logger = log4cplus::Logger::getInstance(LOG4CPLUS_TEXT("Database"));

	setupDatabase();
	prepareStatements();

	workerThread = new boost::thread(&Database::doWork, this);
}

bool Database::is_db_initialised() {
	Hash hash;
	bool exists = true;
	transaction t(orm_db->begin());
	try {
		exists = orm_db->find(1, hash);
	} catch (odb::sqlite::database_exception& e) {
		exists = false;
	}
	t.commit();
	return exists;
}

void Database::initialise_db() {
	transaction t(orm_db->begin());
	odb: schema_catalog::create_schema(*orm_db, "", false);
	t.commit();
	addHashEntry("", 0);
}

void Database::setupDatabase() {
	orm_db = new sqlite::database(dbName,SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE);

	LOG4CPLUS_INFO(logger, "Setting up database " << dbName);

	bool init_done = is_db_initialised();

	if(! init_done) {
		initialise_db();
	}

	transaction t_pragma (orm_db->begin());
		orm_db->execute("PRAGMA page_size = 4096;");
		orm_db->execute("PRAGMA cache_size=10000;");
		orm_db->execute("PRAGMA locking_mode=EXCLUSIVE;");
		orm_db->execute("PRAGMA temp_store = MEMORY;");
		orm_db->execute("PRAGMA journal_mode=MEMORY;");
	t_pragma.commit();

	// perform out of transaction execution
	connection_ptr c (orm_db->connection());
	c->execute("PRAGMA synchronous=NORMAL;");
}

void Database::add(db_data data) {
	boost::mutex::scoped_lock lock(flipMutex);
	currentList->push_back(data);
}

void Database::flipLists() {
	boost::mutex::scoped_lock lock(flipMutex);

	if(currentList == &dataA) {
		currentList = &dataB;
	}else{
		currentList = &dataA;
	}
}

int Database::drain() {
	boost::mutex::scoped_lock lock(dbMutex);
	std::list<db_data>* workList;
	int drainCount = 0;

	workList = currentList;
	flipLists();

	for(std::list<db_data>::iterator ite = workList->begin(); ite != workList->end(); ++ite) {
		addToBatch(*ite);
		recordsWritten++;
		drainCount++;
	}

	workList->clear();
	return drainCount;
}

bool Database::entryExists(fs::path filePath) {
	LOG4CPLUS_DEBUG(logger, "Looking for file " << filePath);

	ImageRecord ir = get_imagerecord(filePath);

	return ir.is_valid();
}

bool Database::entryExists(db_data data) {
	return entryExists(data.filePath);
}

std::list<fs::path> Database::getFilesWithPath(fs::path directoryPath) {
	std::list<fs::path> filePaths;
	std::string path_query(directoryPath.string());
	path_query += "%";

	LOG4CPLUS_INFO(logger, "Looking for files with path " << directoryPath);

	transaction t (orm_db->begin());
	result<ImageRecord> r (orm_db->query<ImageRecord>(query<ImageRecord>::path.like(path_query)));

	for(result<ImageRecord>::iterator itr (r.begin()); itr != r.end(); ++itr) {
		filePaths.push_back(fs::path(itr->getPath()));
	}

	t.commit();

	LOG4CPLUS_INFO(logger, "Found  " << filePaths.size() << " records for path " << directoryPath);
	return filePaths;
}

void Database::prunePath(std::list<fs::path> filePaths) {
	for(std::list<fs::path>::iterator ite = filePaths.begin(); ite != filePaths.end(); ++ite){
		ImageRecord ir = get_imagerecord(*ite);

		transaction t (orm_db->begin());

		if(ir.is_valid()) {
			orm_db->erase(ir);
		}

		t.commit();
	}
}

void Database::addToBatch(db_data data) {
	int response = 0;
	int hashId = -1;

	switch (data.status) {
	case OK:
		add_record(data);
		break;

	case INVALID:
		add_invalid(data);
		break;

	case FILTER:
		add_filter(data);
		break;

	default:
		LOG4CPLUS_ERROR(logger, "Unhandled state encountered");
		throw "Unhandled state encountered";
		break;
	}
}

void Database::add_record(db_data data) {
	Hash hash = get_hash(data.sha256);

	if (!hash.is_valid()) {
		hash = addHashEntry(data.sha256, data.pHash);
	}

	if(entryExists(data)) {
		LOG4CPLUS_DEBUG(logger, "Entry for " << data.filePath << " already exists, discarding...");
		recordsWritten--;
		return;
	}

	transaction t(orm_db->begin());

	ImageRecord ir = ImageRecord(data.filePath.string(), &hash);
	orm_db->persist(ir);

	t.commit();
}

int Database::add_path_placeholder(std::string path) {

	transaction t (orm_db->begin());

	ImageRecord ir (path,NULL);
	int id = orm_db->persist(ir);

	t.commit();

	return id;
}

void Database::add_invalid(db_data data) {
	LOG4CPLUS_DEBUG(logger, "File with path " << data.filePath << " is invalid");
	recordsWritten--;
	Hash hash = get_hash("");
	transaction t(orm_db->begin());
	ImageRecord ir = ImageRecord(data.filePath.string(), &hash);
	orm_db->persist(ir);

	t.commit();
}

void Database::add_filter(db_data data) {
	LOG4CPLUS_DEBUG(logger, "Adding filter record for pHash" << data.pHash << " with reason " << data.reason);

	imageHasher::db::table::FilterRecord fr(data.pHash, data.reason);

	transaction t (orm_db->begin());

	orm_db->persist(fr);

	t.commit();
}

void Database::prepareStatements() {
	LOG4CPLUS_INFO(logger, "Creating prepared statements...");
}

void Database::doWork() {
	while(running) {
		try{
			boost::this_thread::sleep_for(boost::chrono::seconds(3));
		}catch(boost::thread_interrupted&) {
			LOG4CPLUS_INFO(logger,"DB thread interrupted");
		}

		if(currentList->size() > 1000) {
			int drainCount = drain();
			LOG4CPLUS_INFO(logger, drainCount << " records processed, Total: " << recordsWritten);
		}
	}

	// make sure both lists are committed
	int drainCount = flush();
	LOG4CPLUS_INFO(logger, drainCount << " records processed, Total: " << recordsWritten);
}

unsigned int Database::getRecordsWritten() {
	return recordsWritten;
}

std::string Database::getSHA(fs::path filepath) {
	std::string sha = "";
	LOG4CPLUS_DEBUG(logger, "Getting SHA for path " << filepath);

	transaction t (orm_db->begin());

	result<ImageRecord> r (orm_db->query<ImageRecord>(query<ImageRecord>::path == filepath.string()));

	for(result<ImageRecord>::iterator itr (r.begin()); itr != r.end(); ++itr) {
		Hash hash = itr->get_hash();
		sha = hash.get_sha256();
		break;
	}

	t.commit();

	return sha;
}

bool Database::sha_exists(std::string sha) {
	Hash hash = get_hash(sha);
	return hash.is_valid();
}

int64_t Database::getPhash(fs::path filepath) {
	LOG4CPLUS_DEBUG(logger, "Getting pHash for path " << filepath);

	ImageRecord ir = get_imagerecord(filepath);

	if(ir.is_valid()) {
		return ir.get_hash().get_pHash();
	}

	return -1;
}

imageHasher::db::table::Hash Database::get_hash(std::string sha) {
	Hash hash;
	LOG4CPLUS_DEBUG(logger, "Getting hash for sha " << sha);

	transaction t (orm_db->begin());

	result<Hash> r (orm_db->query<Hash>(query<Hash>::sha256 == sha));

	for(result<Hash>::iterator itr (r.begin()); itr != r.end(); ++itr) {
		hash = *itr;
		break;
	}

	t.commit();

	return hash;
}

imageHasher::db::table::Hash Database::get_hash(u_int64_t phash) {
	Hash hash;
	LOG4CPLUS_DEBUG(logger, "Getting hash for pHash " << phash);

	transaction t (orm_db->begin());

	result<Hash> r (orm_db->query<Hash>(query<Hash>::pHash == phash));

	for(result<Hash>::iterator itr (r.begin()); itr != r.end(); ++itr) {
		hash = *itr;
		break;
	}

	t.commit();

	return hash;
}

imageHasher::db::table::ImageRecord Database::get_imagerecord(fs::path filepath) {
	ImageRecord ir;

	transaction t (orm_db->begin());

	result<ImageRecord> r (orm_db->query<ImageRecord>(query<ImageRecord>::path == filepath.string()));

	for(result<ImageRecord>::iterator itr (r.begin()); itr != r.end(); ++itr) {
		ir = *itr;
		break;
	}

	t.commit();

	return ir;
}

Hash Database::addHashEntry(std::string sha, u_int64_t pHash) {
	Hash hash(sha,pHash);
	transaction t (orm_db->begin());
		orm_db->persist(hash);
	t.commit();

	return hash;
}
<|endoftext|>


<commit_before>#include "NetworkManagerClient.h"
#include <string.h>
#include <sys/types.h>
#include <stdlib.h>

#ifndef WIN32
#include <unistd.h>
#include <sys/time.h>

#else
#include <windows.h>
#include <time.h>

#endif

NetworkManagerClient::NetworkManagerClient(void) : connected(false), scores()
{
    if(SDL_Init(0) != 0){
        printf("SDL_Init done goofed: %s\n", SDL_GetError());
        exit(1);
    }

    if(SDLNet_Init() != 0){
        printf("SDLNet_Init done goofed: %s\n",SDLNet_GetError());
    }
}

NetworkManagerClient::~NetworkManagerClient(void)
{
    SDLNet_Quit();
    SDL_Quit();
}

//main method from tcpmulticlient.c in the SDLNet demos
int NetworkManagerClient::TCPConnect(char *host, long portNo, char *name)
{
    IPaddress ip;
    //	TCPsocket sock;
    char message[MAXLEN];
    int numready;
    SDLNet_SocketSet set;
    fd_set fdset;
    int result;
    char *str;
    struct timeval tv;
    Uint16 port = (Uint16)portNo;

    Packet pack;

    set = SDLNet_AllocSocketSet(1);
    if(!set)
    {
        printf("SDLNet_AllocSocketSet done goofed: %s\n", SDLNet_GetError());
        SDLNet_Quit();
        SDL_Quit();
        exit(4); /*most of the time this is a major error, but do what you want. */
    }

    /* Resolve the argument into an IPaddress type */
    printf("Connecting to %s port %d\n", host, port);
    if(SDLNet_ResolveHost(&ip, host, port)==-1)
    {
        printf("SDLNet_ResolveHost done goofed: %s\n",SDLNet_GetError());
        std::string exception = "fail_to_connect";
        throw exception;
    }

    printf("Opening server socket.\n");
    /* open the server socket */
    serverSock = SDLNet_TCP_Open(&ip);
    if(!serverSock)
    {
        printf("SDLNet_TCP_Open done goofed: %s\n",SDLNet_GetError());
        SDLNet_Quit();
        SDL_Quit();
        std::string exception = "fail_to_connect";
        throw exception;
    }

    if(SDLNet_TCP_AddSocket(set,serverSock) == -1)
    {
        printf("SDLNet_TCP_AddSocket done goofed: %s\n",SDLNet_GetError());
        SDLNet_Quit();
        SDL_Quit();
        exit(7);
    }

    pack.type = CONNECTION;
    pack.message = name;
    /* login with a name */
    char* out = PacketToCharArray(pack);
    printf("Sent %s\n", out);
    if(!TCPSend(serverSock, out))
    {
        SDLNet_TCP_Close(serverSock);
        exit(8);
    }

    mName = name;
    std::cout << "Logged in as " << mName << std::endl;
    connected = true;
}

std::string NetworkManagerClient::getPlayerScores()
{
    return scores; //"name,score;"
}

void NetworkManagerClient::resetReadyState()
{
    Packet outgoing;
    outgoing.type = READY;
    outgoing.message = const_cast<char*>("RESET");
    TCPSend(serverSock, PacketToCharArray(outgoing));
}

void NetworkManagerClient::sendPlayerInput(ISpaceShipController* controller)
{
    Packet outgoing;

    bool left = controller->left();
    bool right = controller->right();
    bool up = controller->up();
    bool down = controller->down();
    bool forward = controller->forward();
    bool back = controller->back();
    bool shoot = controller->shoot();

    char result = left;
    result = (result << 1) + right;
    result = (result << 1) + up;
    result = (result << 1) + down;
    result = (result << 1) + forward;
    result = (result << 1) + back;
    result = (result << 1) + shoot;

    outgoing.type = PLAYERINPUT;
    outgoing.message = &result;

    char *out = PacketToCharArray(outgoing);
    if(!TCPSend(serverSock, out))
        connected = false;
}

bool NetworkManagerClient::isOnline()
{
    return connected;
}

void NetworkManagerClient::quit()
{
    Packet outgoing;
    outgoing.type = CONNECTION;
    outgoing.message = const_cast<char*>("QUIT");
    while(!TCPSend(serverSock, PacketToCharArray(outgoing))); // FIXME: what if server crashed?
    connected = false;
}

void NetworkManagerClient::sendPlayerScore(double score)
{
    std::stringstream ss;
    std::string s;
    ss << score;
    s = ss.str();

    Packet outgoing;
    outgoing.type = SCORE;
    outgoing.message = const_cast<char*>(s.c_str());
    char *incoming = NULL;
    char *out = PacketToCharArray(outgoing);
    if(TCPSend(serverSock, out) && TCPReceive(serverSock, &incoming)) {
        printf("Receving: %s\n", incoming);
        Packet pack = charArrayToPacket(incoming);
        if(pack.type != SCORE)
        {
            printf("Error in sendPlayerScore() in NetworkManagerClient.cpp. Score not received from server.\n");
            scores = "";
        }
        else{
            printf("Received message: %s\n", pack.message);
            scores = pack.message;
        }
    }
    else {
        connected = false;
        scores = "";
    }
}

void NetworkManagerClient::receiveData(Ogre::SceneManager* sceneManager, SoundManager* sound, std::vector<Mineral*>& minerals, std::vector<SpaceShip*>& spaceships, std::vector<GameObject*>& walls)
{
    int packs;
    Packet outgoing;
    outgoing.type = STATE;
    outgoing.message = "NONE";
    char* incoming = NULL;
    char *out = PacketToCharArray(outgoing);
    ////std::cout << "We are sending state request and trying to receive initial response." << std::endl;
    if(TCPSend(serverSock, out) && TCPReceive(serverSock, &incoming)) {
        ////std::cout << "Received initial response." << std::endl;
        Packet numPackets = charArrayToPacket(incoming);
        packs = atoi(numPackets.message);
        ////std::cout << "We are expecting to receive " << packs << " number of packets" << std::endl;
    }
    else {
        connected = false;
        return;
    }

    for(int i = 0; i < packs; ++i)
    {
        //std::cout << "We are about to receive packet number " << i << "." << std::endl;
        if(!TCPReceive(serverSock, &incoming)) {
            connected = false; break;}
        //std::cout << "We received packet number " << i << "." << std::endl;
        Packet in = charArrayToPacket(incoming);
        if(in.type == MINERAL)
        {
            //std::cout << "It was a mineral." << std::endl;
            std::string message(in.message);
            //std::cout << "The mineral's message is " << message << std::endl;
            std::string name = message.substr(0, message.find(","));
            message = message.substr(message.find(",") + 1);
            double pos_x = atof(message.substr(0, message.find(",")).c_str());
            message = message.substr(message.find(",") + 1);
            double pos_y = atof(message.substr(0, message.find(",")).c_str());
            message = message.substr(message.find(",") + 1);
            double pos_z = atof(message.substr(0, message.find(",")).c_str());
            message = message.substr(message.find(",") + 1);
            double vel_x = atof(message.substr(0, message.find(",")).c_str());
            message = message.substr(message.find(",") + 1);
            double vel_y = atof(message.substr(0, message.find(",")).c_str());
            message = message.substr(message.find(",") + 1);
            double vel_z = atof(message.substr(0, message.find(",")).c_str());
            message = message.substr(message.find(",") + 1);
            double rot_w = atof(message.substr(0, message.find(",")).c_str());
            message = message.substr(message.find(",") + 1);
            double rot_x = atof(message.substr(0, message.find(",")).c_str());
            message = message.substr(message.find(",") + 1);
            double rot_y = atof(message.substr(0, message.find(",")).c_str());
            message = message.substr(message.find(",") + 1);
            double rot_z = atof(message.substr(0, message.find(",")).c_str());
            message = message.substr(message.find(",") + 1);
            double radius = atof(message.substr(0, message.find(",")).c_str());
            //std::cout << "We parsed that message." << message << std::endl;

            // FIXME: THIS IS BAD, oh well
            bool found = false;
            for(int j = 0; j < minerals.size(); ++j)
            {
                //std::cout << "Checking to see if " << name << " already exists." << std::endl;
                if(minerals[j]->getName() == name)
                {
                    //std::cout << "Exists." << std::endl;
                    found = true;
                    minerals[j]->getSceneNode()->setPosition(pos_x, pos_y, pos_z);
                    minerals[j]->getSceneNode()->setOrientation(rot_w, rot_x, rot_y, rot_z);
                    break;
                }
            }
            if(!found)
            {
                //std::cout << "Doesn't exist, create it." << std::endl;
                minerals.push_back(new Mineral(name, sound, sceneManager->getRootSceneNode(), pos_x, pos_y, pos_z, radius));
                minerals.back()->getSceneNode()->setOrientation(rot_w, rot_x, rot_y, rot_z);
            }
            //std::cout << "Got the Mineral!" << std::endl;
        }
        else if(in.type == SPACESHIP)
        {
            //std::cout << "It was a spaceship." << std::endl;
            std::string message(in.message);
            std::string name = message.substr(0, message.find(","));
            message = message.substr(message.find(",") + 1);
            double pos_x = atof(message.substr(0, message.find(",")).c_str());
            message = message.substr(message.find(",") + 1);
            double pos_y = atof(message.substr(0, message.find(",")).c_str());
            message = message.substr(message.find(",") + 1);
            double pos_z = atof(message.substr(0, message.find(",")).c_str());
            message = message.substr(message.find(",") + 1);
            double vel_x = atof(message.substr(0, message.find(",")).c_str());
            message = message.substr(message.find(",") + 1);
            double vel_y = atof(message.substr(0, message.find(",")).c_str());
            message = message.substr(message.find(",") + 1);
            double vel_z = atof(message.substr(0, message.find(",")).c_str());
            message = message.substr(message.find(",") + 1);
            double rot_w = atof(message.substr(0, message.find(",")).c_str());
            message = message.substr(message.find(",") + 1);
            double rot_x = atof(message.substr(0, message.find(",")).c_str());
            message = message.substr(message.find(",") + 1);
            double rot_y = atof(message.substr(0, message.find(",")).c_str());
            message = message.substr(message.find(",") + 1);
            double rot_z = atof(message.substr(0, message.find(",")).c_str());
            message = message.substr(message.find(",") + 1);
            double size = atof(message.substr(0, message.find(",")).c_str());

            // FIXME: THIS IS BAD, oh well
            bool found = false;
            if(name == mName)
            {
                spaceships[0]->getSceneNode()->setPosition(pos_x, pos_y, pos_z);
                spaceships[0]->getSceneNode()->setOrientation(rot_w, rot_x, rot_y, rot_z);
                break;
            }
            for(int j = 1; j < spaceships.size(); ++j)
            {
                if(spaceships[j]->getName() == name)
                {
                    found = true;
                    spaceships[j]->getSceneNode()->setPosition(pos_x, pos_y, pos_z);
                    spaceships[j]->getSceneNode()->setOrientation(rot_w, rot_x, rot_y, rot_z);
                    break;
                }
            }
            if(!found)
            {
                ISpaceShipController* controller = new ClientSpaceShipController();
                spaceships.push_back(new SpaceShip(name, sound, controller, sceneManager->getRootSceneNode()->createChildSceneNode(),size));
                spaceships.back()->getSceneNode()->getParentSceneNode()->setPosition(pos_x, pos_y, pos_z);
                spaceships.back()->getSceneNode()->setOrientation(rot_w, rot_x, rot_y, rot_z);
            }
            //std::cout << "Got the SpaceShip!" << std::endl;
        }
        else if(in.type == WALL)
        {
            //std::cout << "It was a wall." << std::endl;
        }
    }
}

<commit_msg>Work, you<commit_after>#include "NetworkManagerClient.h"
#include <string.h>
#include <sys/types.h>
#include <stdlib.h>

#ifndef WIN32
#include <unistd.h>
#include <sys/time.h>

#else
#include <windows.h>
#include <time.h>

#endif

NetworkManagerClient::NetworkManagerClient(void) : connected(false), scores()
{
    if(SDL_Init(0) != 0){
        printf("SDL_Init done goofed: %s\n", SDL_GetError());
        exit(1);
    }

    if(SDLNet_Init() != 0){
        printf("SDLNet_Init done goofed: %s\n",SDLNet_GetError());
    }
}

NetworkManagerClient::~NetworkManagerClient(void)
{
    SDLNet_Quit();
    SDL_Quit();
}

//main method from tcpmulticlient.c in the SDLNet demos
int NetworkManagerClient::TCPConnect(char *host, long portNo, char *name)
{
    IPaddress ip;
    //	TCPsocket sock;
    char message[MAXLEN];
    int numready;
    SDLNet_SocketSet set;
    fd_set fdset;
    int result;
    char *str;
    struct timeval tv;
    Uint16 port = (Uint16)portNo;

    Packet pack;

    set = SDLNet_AllocSocketSet(1);
    if(!set)
    {
        printf("SDLNet_AllocSocketSet done goofed: %s\n", SDLNet_GetError());
        SDLNet_Quit();
        SDL_Quit();
        exit(4); /*most of the time this is a major error, but do what you want. */
    }

    /* Resolve the argument into an IPaddress type */
    printf("Connecting to %s port %d\n", host, port);
    if(SDLNet_ResolveHost(&ip, host, port)==-1)
    {
        printf("SDLNet_ResolveHost done goofed: %s\n",SDLNet_GetError());
        std::string exception = "fail_to_connect";
        throw exception;
    }

    printf("Opening server socket.\n");
    /* open the server socket */
    serverSock = SDLNet_TCP_Open(&ip);
    if(!serverSock)
    {
        printf("SDLNet_TCP_Open done goofed: %s\n",SDLNet_GetError());
        SDLNet_Quit();
        SDL_Quit();
        std::string exception = "fail_to_connect";
        throw exception;
    }

    if(SDLNet_TCP_AddSocket(set,serverSock) == -1)
    {
        printf("SDLNet_TCP_AddSocket done goofed: %s\n",SDLNet_GetError());
        SDLNet_Quit();
        SDL_Quit();
        exit(7);
    }

    pack.type = CONNECTION;
    pack.message = name;
    /* login with a name */
    char* out = PacketToCharArray(pack);
    printf("Sent %s\n", out);
    if(!TCPSend(serverSock, out))
    {
        SDLNet_TCP_Close(serverSock);
        exit(8);
    }

    mName = name;
    std::cout << "Logged in as " << mName << std::endl;
    connected = true;
}

std::string NetworkManagerClient::getPlayerScores()
{
    return scores; //"name,score;"
}

void NetworkManagerClient::resetReadyState()
{
    Packet outgoing;
    outgoing.type = READY;
    outgoing.message = const_cast<char*>("RESET");
    TCPSend(serverSock, PacketToCharArray(outgoing));
}

void NetworkManagerClient::sendPlayerInput(ISpaceShipController* controller)
{
    Packet outgoing;

    bool left = controller->left();
    bool right = controller->right();
    bool up = controller->up();
    bool down = controller->down();
    bool forward = controller->forward();
    bool back = controller->back();
    bool shoot = controller->shoot();

    char result = left;
    result = (result << 1) + right;
    result = (result << 1) + up;
    result = (result << 1) + down;
    result = (result << 1) + forward;
    result = (result << 1) + back;
    result = (result << 1) + shoot;

    outgoing.type = PLAYERINPUT;
    outgoing.message = &result;

    char *out = PacketToCharArray(outgoing);
    if(!TCPSend(serverSock, out))
        connected = false;
}

bool NetworkManagerClient::isOnline()
{
    return connected;
}

void NetworkManagerClient::quit()
{
    Packet outgoing;
    outgoing.type = CONNECTION;
    outgoing.message = const_cast<char*>("QUIT");
    while(!TCPSend(serverSock, PacketToCharArray(outgoing))); // FIXME: what if server crashed?
    connected = false;
}

void NetworkManagerClient::sendPlayerScore(double score)
{
    std::stringstream ss;
    std::string s;
    ss << score;
    s = ss.str();

    Packet outgoing;
    outgoing.type = SCORE;
    outgoing.message = const_cast<char*>(s.c_str());
    char *incoming = NULL;
    char *out = PacketToCharArray(outgoing);
    if(TCPSend(serverSock, out) && TCPReceive(serverSock, &incoming)) {
        printf("Receving: %s\n", incoming);
        Packet pack = charArrayToPacket(incoming);
        if(pack.type != SCORE)
        {
            printf("Error in sendPlayerScore() in NetworkManagerClient.cpp. Score not received from server.\n");
            scores = "";
        }
        else{
            printf("Received message: %s\n", pack.message);
            scores = pack.message;
        }
    }
    else {
        connected = false;
        scores = "";
    }
}

void NetworkManagerClient::receiveData(Ogre::SceneManager* sceneManager, SoundManager* sound, std::vector<Mineral*>& minerals, std::vector<SpaceShip*>& spaceships, std::vector<GameObject*>& walls)
{
    int packs;
    Packet outgoing;
    outgoing.type = STATE;
    outgoing.message = "NONE";
    char* incoming = NULL;
    char *out = PacketToCharArray(outgoing);
    ////std::cout << "We are sending state request and trying to receive initial response." << std::endl;
    if(TCPSend(serverSock, out) && TCPReceive(serverSock, &incoming)) {
        ////std::cout << "Received initial response." << std::endl;
        Packet numPackets = charArrayToPacket(incoming);
        packs = atoi(numPackets.message);
        ////std::cout << "We are expecting to receive " << packs << " number of packets" << std::endl;
    }
    else {
        connected = false;
        return;
    }

    for(int i = 0; i < packs; ++i)
    {
        //std::cout << "We are about to receive packet number " << i << "." << std::endl;
        if(!TCPReceive(serverSock, &incoming)) {
            connected = false; break;}
        //std::cout << "We received packet number " << i << "." << std::endl;
        Packet in = charArrayToPacket(incoming);
        if(in.type == MINERAL)
        {
            //std::cout << "It was a mineral." << std::endl;
            std::string message(in.message);
            //std::cout << "The mineral's message is " << message << std::endl;
            std::string name = message.substr(0, message.find(","));
            message = message.substr(message.find(",") + 1);
            double pos_x = atof(message.substr(0, message.find(",")).c_str());
            message = message.substr(message.find(",") + 1);
            double pos_y = atof(message.substr(0, message.find(",")).c_str());
            message = message.substr(message.find(",") + 1);
            double pos_z = atof(message.substr(0, message.find(",")).c_str());
            message = message.substr(message.find(",") + 1);
            double vel_x = atof(message.substr(0, message.find(",")).c_str());
            message = message.substr(message.find(",") + 1);
            double vel_y = atof(message.substr(0, message.find(",")).c_str());
            message = message.substr(message.find(",") + 1);
            double vel_z = atof(message.substr(0, message.find(",")).c_str());
            message = message.substr(message.find(",") + 1);
            double rot_w = atof(message.substr(0, message.find(",")).c_str());
            message = message.substr(message.find(",") + 1);
            double rot_x = atof(message.substr(0, message.find(",")).c_str());
            message = message.substr(message.find(",") + 1);
            double rot_y = atof(message.substr(0, message.find(",")).c_str());
            message = message.substr(message.find(",") + 1);
            double rot_z = atof(message.substr(0, message.find(",")).c_str());
            message = message.substr(message.find(",") + 1);
            double radius = atof(message.substr(0, message.find(",")).c_str());
            //std::cout << "We parsed that message." << message << std::endl;

            // FIXME: THIS IS BAD, oh well
            bool found = false;
            for(int j = 0; j < minerals.size(); ++j)
            {
                //std::cout << "Checking to see if " << name << " already exists." << std::endl;
                if(minerals[j]->getName() == name)
                {
                    //std::cout << "Exists." << std::endl;
                    found = true;
                    minerals[j]->getSceneNode()->setPosition(pos_x, pos_y, pos_z);
                    minerals[j]->getSceneNode()->setOrientation(rot_w, rot_x, rot_y, rot_z);
                    break;
                }
            }
            if(!found)
            {
                //std::cout << "Doesn't exist, create it." << std::endl;
                minerals.push_back(new Mineral(name, sound, sceneManager->getRootSceneNode(), pos_x, pos_y, pos_z, radius));
                minerals.back()->getSceneNode()->setOrientation(rot_w, rot_x, rot_y, rot_z);
            }
            //std::cout << "Got the Mineral!" << std::endl;
        }
        else if(in.type == SPACESHIP)
        {
            //std::cout << "It was a spaceship." << std::endl;
            std::string message(in.message);
            std::string name = message.substr(0, message.find(","));
            message = message.substr(message.find(",") + 1);
            double pos_x = atof(message.substr(0, message.find(",")).c_str());
            message = message.substr(message.find(",") + 1);
            double pos_y = atof(message.substr(0, message.find(",")).c_str());
            message = message.substr(message.find(",") + 1);
            double pos_z = atof(message.substr(0, message.find(",")).c_str());
            message = message.substr(message.find(",") + 1);
            double vel_x = atof(message.substr(0, message.find(",")).c_str());
            message = message.substr(message.find(",") + 1);
            double vel_y = atof(message.substr(0, message.find(",")).c_str());
            message = message.substr(message.find(",") + 1);
            double vel_z = atof(message.substr(0, message.find(",")).c_str());
            message = message.substr(message.find(",") + 1);
            double rot_w = atof(message.substr(0, message.find(",")).c_str());
            message = message.substr(message.find(",") + 1);
            double rot_x = atof(message.substr(0, message.find(",")).c_str());
            message = message.substr(message.find(",") + 1);
            double rot_y = atof(message.substr(0, message.find(",")).c_str());
            message = message.substr(message.find(",") + 1);
            double rot_z = atof(message.substr(0, message.find(",")).c_str());
            message = message.substr(message.find(",") + 1);
            double size = atof(message.substr(0, message.find(",")).c_str());

            // FIXME: THIS IS BAD, oh well
            bool found = false;
            if(name == mName)
            {
                spaceships[0]->getSceneNode()->getParentSceneNode()->setPosition(pos_x, pos_y, pos_z);
                spaceships[0]->getSceneNode()->setOrientation(rot_w, rot_x, rot_y, rot_z);
                break;
            }
            for(int j = 1; j < spaceships.size(); ++j)
            {
                if(spaceships[j]->getName() == name)
                {
                    found = true;
                    spaceships[j]->getSceneNode()->setPosition(pos_x, pos_y, pos_z);
                    spaceships[j]->getSceneNode()->setOrientation(rot_w, rot_x, rot_y, rot_z);
                    break;
                }
            }
            if(!found)
            {
                ISpaceShipController* controller = new ClientSpaceShipController();
                spaceships.push_back(new SpaceShip(name, sound, controller, sceneManager->getRootSceneNode()->createChildSceneNode(),size));
                spaceships.back()->getSceneNode()->getParentSceneNode()->setPosition(pos_x, pos_y, pos_z);
                spaceships.back()->getSceneNode()->setOrientation(rot_w, rot_x, rot_y, rot_z);
            }
            //std::cout << "Got the SpaceShip!" << std::endl;
        }
        else if(in.type == WALL)
        {
            //std::cout << "It was a wall." << std::endl;
        }
    }
}

<|endoftext|>


<commit_before>/*
 * Copyright (c) 2015 Cryptonomex, Inc., and contributors.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
 *
 * 1. Any modified source or binaries are used only with the BitShares network.
 *
 * 2. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
 *
 * 3. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 */
#include <graphene/db/object_database.hpp>
#include <graphene/db/undo_database.hpp>
#include <fc/reflect/variant.hpp>

namespace graphene { namespace db {

void undo_database::enable()  { _disabled = false; }
void undo_database::disable() { _disabled = true; }

undo_database::session undo_database::start_undo_session( bool force_enable )
{
   if( _disabled && !force_enable ) return session(*this);
   bool disable_on_exit = _disabled  && force_enable;
   if( force_enable )
      _disabled = false;

   while( size() > max_size() )
      _stack.pop_front();

   _stack.emplace_back();
   ++_active_sessions;
   return session(*this, disable_on_exit );
}
void undo_database::on_create( const object& obj )
{
   if( _disabled ) return;

   if( _stack.empty() )
      _stack.emplace_back();
   auto& state = _stack.back();
   auto index_id = object_id_type( obj.id.space(), obj.id.type(), 0 );
   auto itr = state.old_index_next_ids.find( index_id );
   if( itr == state.old_index_next_ids.end() )
      state.old_index_next_ids[index_id] = obj.id;
   state.new_ids.insert(obj.id);
}
void undo_database::on_modify( const object& obj )
{
   if( _disabled ) return;

   if( _stack.empty() )
      _stack.emplace_back();
   auto& state = _stack.back();
   if( state.new_ids.find(obj.id) != state.new_ids.end() )
      return;
   auto itr =  state.old_values.find(obj.id);
   if( itr != state.old_values.end() ) return;
   state.old_values[obj.id] = obj.clone();
}
void undo_database::on_remove( const object& obj )
{
   if( _disabled ) return;

   if( _stack.empty() )
      _stack.emplace_back();
   undo_state& state = _stack.back();
   if( state.new_ids.count(obj.id) )
   {
      state.new_ids.erase(obj.id);
      return;
   }
   if( state.old_values.count(obj.id) )
   {
      state.removed[obj.id] = std::move(state.old_values[obj.id]);
      state.old_values.erase(obj.id);
      return;
   }
   if( state.removed.count(obj.id) ) return;
   state.removed[obj.id] = obj.clone();
}

void undo_database::undo()
{ try {
   FC_ASSERT( !_disabled );
   FC_ASSERT( _active_sessions > 0 );
   disable();

   auto& state = _stack.back();
   for( auto& item : state.old_values )
   {
      _db.modify( _db.get_object( item.second->id ), [&]( object& obj ){ obj.move_from( *item.second ); } );
   }

   for( auto ritr = state.new_ids.begin(); ritr != state.new_ids.end(); ++ritr  )
   {
      _db.remove( _db.get_object(*ritr) );
   }

   for( auto& item : state.old_index_next_ids )
   {
      _db.get_mutable_index( item.first.space(), item.first.type() ).set_next_id( item.second );
   }

   for( auto& item : state.removed )
      _db.insert( std::move(*item.second) );

   _stack.pop_back();
   if( _stack.empty() )
      _stack.emplace_back();
   enable();
   --_active_sessions;
} FC_CAPTURE_AND_RETHROW() }

void undo_database::merge()
{
   FC_ASSERT( _active_sessions > 0 );
   FC_ASSERT( _stack.size() >=2 );
   auto& state = _stack.back();
   auto& prev_state = _stack[_stack.size()-2];

   // An object's relationship to a state can be:
   // in new_ids            : new
   // in old_values (was=X) : upd(was=X)
   // in removed (was=X)    : del(was=X)
   // not in any of above   : nop
   //
   // When merging A=prev_state and B=state we have a 4x4 matrix of all possibilities:
   //
   //                   |--------------------- B ----------------------|
   //
   //                +------------+------------+------------+------------+
   //                | new        | upd(was=Y) | del(was=Y) | nop        |
   //   +------------+------------+------------+------------+------------+
   // / | new        | N/A        | new       A| nop       C| new       A|
   // | +------------+------------+------------+------------+------------+
   // | | upd(was=X) | N/A        | upd(was=X)A| del(was=X)C| upd(was=X)A|
   // A +------------+------------+------------+------------+------------+
   // | | del(was=X) | N/A        | N/A        | N/A        | del(was=X)A|
   // | +------------+------------+------------+------------+------------+
   // \ | nop        | new       B| upd(was=Y)B| del(was=Y)B| nop      AB|
   //   +------------+------------+------------+------------+------------+
   //
   // Each entry was composed by labelling what should occur in the given case.
   //
   // Type A means the composition of states contains the same entry as the first of the two merged states for that object.
   // Type B means the composition of states contains the same entry as the second of the two merged states for that object.
   // Type C means the composition of states contains an entry different from either of the merged states for that object.
   // Type N/A means the composition of states violates causal timing.
   // Type AB means both type A and type B simultaneously.
   //
   // The merge() operation is defined as modifying prev_state in-place to be the state object which represents the composition of
   // state A and B.
   //
   // Type A (and AB) can be implemented as a no-op; prev_state already contains the correct value for the merged state.
   // Type B (and AB) can be implemented by copying from state to prev_state.
   // Type C needs special case-by-case logic.
   // Type N/A can be ignored or assert(false) as it can only occur if prev_state and state have illegal values
   // (a serious logic error which should never happen).
   //

   // We can only be outside type A/AB (the nop path) if B is not nop, so it suffices to iterate through B's three containers.

   // *+upd
   for( auto& obj : state.old_values )
   {
      if( prev_state.new_ids.find(obj.second->id) != prev_state.new_ids.end() )
      {
         // new+upd -> new, type A
         continue;
      }
      if( prev_state.old_values.find(obj.second->id) != prev_state.old_values.end() )
      {
         // upd(was=X) + upd(was=Y) -> upd(was=X), type A
         continue;
      }
      // nop+upd(was=Y) -> upd(was=Y), type B
      prev_state.old_values[obj.second->id] = std::move(obj.second);
   }

   // *+new, but we assume the N/A cases don't happen, leaving type B nop+new -> new
   for( auto id : state.new_ids )
      prev_state.new_ids.insert(id);

   // old_index_next_ids can only be updated, iterate over *+upd cases
   for( auto& item : state.old_index_next_ids )
   {
      if( prev_state.old_index_next_ids.find( item.first ) == prev_state.old_index_next_ids.end() )
      {
         // nop+upd(was=Y) -> upd(was=Y), type B
         prev_state.old_index_next_ids[item.first] = item.second;
         continue;
      }
      else
      {
         // upd(was=X)+upd(was=Y) -> upd(was=X), type A
         // type A implementation is a no-op, as discussed above, so there is no code here
         continue;
      }
   }

   // *+del
   for( auto& obj : state.removed )
   {
      if( prev_state.new_ids.find(obj.second->id) != prev_state.new_ids.end() )
      {
         // new + del -> nop (type C)
         prev_state.new_ids.erase(obj.second->id);
         continue;
      }
      // nop + del(was=Y) -> del(was=Y)
      prev_state.removed[obj.second->id] = std::move(obj.second);
   }
   _stack.pop_back();
   --_active_sessions;
}
void undo_database::commit()
{
   FC_ASSERT( _active_sessions > 0 );
   --_active_sessions;
}

void undo_database::pop_commit()
{
   FC_ASSERT( _active_sessions == 0 );
   FC_ASSERT( !_stack.empty() );

   disable();
   try {
      auto& state = _stack.back();

      for( auto& item : state.old_values )
      {
         _db.modify( _db.get_object( item.second->id ), [&]( object& obj ){ obj.move_from( *item.second ); } );
      }

      for( auto ritr = state.new_ids.begin(); ritr != state.new_ids.end(); ++ritr  )
      {
         _db.remove( _db.get_object(*ritr) );
      }

      for( auto& item : state.old_index_next_ids )
      {
         _db.get_mutable_index( item.first.space(), item.first.type() ).set_next_id( item.second );
      }

      for( auto& item : state.removed )
         _db.insert( std::move(*item.second) );

      _stack.pop_back();
   }
   catch ( const fc::exception& e )
   {
      elog( "error popping commit ${e}", ("e", e.to_detail_string() )  );
      enable();
      throw;
   }
   enable();
}
const undo_state& undo_database::head()const
{
   FC_ASSERT( !_stack.empty() );
   return _stack.back();
}

} } // graphene::db
<commit_msg>undo_database.cpp:  Handle unimplemented upd+del case<commit_after>/*
 * Copyright (c) 2015 Cryptonomex, Inc., and contributors.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
 *
 * 1. Any modified source or binaries are used only with the BitShares network.
 *
 * 2. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
 *
 * 3. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 */
#include <graphene/db/object_database.hpp>
#include <graphene/db/undo_database.hpp>
#include <fc/reflect/variant.hpp>

namespace graphene { namespace db {

void undo_database::enable()  { _disabled = false; }
void undo_database::disable() { _disabled = true; }

undo_database::session undo_database::start_undo_session( bool force_enable )
{
   if( _disabled && !force_enable ) return session(*this);
   bool disable_on_exit = _disabled  && force_enable;
   if( force_enable )
      _disabled = false;

   while( size() > max_size() )
      _stack.pop_front();

   _stack.emplace_back();
   ++_active_sessions;
   return session(*this, disable_on_exit );
}
void undo_database::on_create( const object& obj )
{
   if( _disabled ) return;

   if( _stack.empty() )
      _stack.emplace_back();
   auto& state = _stack.back();
   auto index_id = object_id_type( obj.id.space(), obj.id.type(), 0 );
   auto itr = state.old_index_next_ids.find( index_id );
   if( itr == state.old_index_next_ids.end() )
      state.old_index_next_ids[index_id] = obj.id;
   state.new_ids.insert(obj.id);
}
void undo_database::on_modify( const object& obj )
{
   if( _disabled ) return;

   if( _stack.empty() )
      _stack.emplace_back();
   auto& state = _stack.back();
   if( state.new_ids.find(obj.id) != state.new_ids.end() )
      return;
   auto itr =  state.old_values.find(obj.id);
   if( itr != state.old_values.end() ) return;
   state.old_values[obj.id] = obj.clone();
}
void undo_database::on_remove( const object& obj )
{
   if( _disabled ) return;

   if( _stack.empty() )
      _stack.emplace_back();
   undo_state& state = _stack.back();
   if( state.new_ids.count(obj.id) )
   {
      state.new_ids.erase(obj.id);
      return;
   }
   if( state.old_values.count(obj.id) )
   {
      state.removed[obj.id] = std::move(state.old_values[obj.id]);
      state.old_values.erase(obj.id);
      return;
   }
   if( state.removed.count(obj.id) ) return;
   state.removed[obj.id] = obj.clone();
}

void undo_database::undo()
{ try {
   FC_ASSERT( !_disabled );
   FC_ASSERT( _active_sessions > 0 );
   disable();

   auto& state = _stack.back();
   for( auto& item : state.old_values )
   {
      _db.modify( _db.get_object( item.second->id ), [&]( object& obj ){ obj.move_from( *item.second ); } );
   }

   for( auto ritr = state.new_ids.begin(); ritr != state.new_ids.end(); ++ritr  )
   {
      _db.remove( _db.get_object(*ritr) );
   }

   for( auto& item : state.old_index_next_ids )
   {
      _db.get_mutable_index( item.first.space(), item.first.type() ).set_next_id( item.second );
   }

   for( auto& item : state.removed )
      _db.insert( std::move(*item.second) );

   _stack.pop_back();
   if( _stack.empty() )
      _stack.emplace_back();
   enable();
   --_active_sessions;
} FC_CAPTURE_AND_RETHROW() }

void undo_database::merge()
{
   FC_ASSERT( _active_sessions > 0 );
   FC_ASSERT( _stack.size() >=2 );
   auto& state = _stack.back();
   auto& prev_state = _stack[_stack.size()-2];

   // An object's relationship to a state can be:
   // in new_ids            : new
   // in old_values (was=X) : upd(was=X)
   // in removed (was=X)    : del(was=X)
   // not in any of above   : nop
   //
   // When merging A=prev_state and B=state we have a 4x4 matrix of all possibilities:
   //
   //                   |--------------------- B ----------------------|
   //
   //                +------------+------------+------------+------------+
   //                | new        | upd(was=Y) | del(was=Y) | nop        |
   //   +------------+------------+------------+------------+------------+
   // / | new        | N/A        | new       A| nop       C| new       A|
   // | +------------+------------+------------+------------+------------+
   // | | upd(was=X) | N/A        | upd(was=X)A| del(was=X)C| upd(was=X)A|
   // A +------------+------------+------------+------------+------------+
   // | | del(was=X) | N/A        | N/A        | N/A        | del(was=X)A|
   // | +------------+------------+------------+------------+------------+
   // \ | nop        | new       B| upd(was=Y)B| del(was=Y)B| nop      AB|
   //   +------------+------------+------------+------------+------------+
   //
   // Each entry was composed by labelling what should occur in the given case.
   //
   // Type A means the composition of states contains the same entry as the first of the two merged states for that object.
   // Type B means the composition of states contains the same entry as the second of the two merged states for that object.
   // Type C means the composition of states contains an entry different from either of the merged states for that object.
   // Type N/A means the composition of states violates causal timing.
   // Type AB means both type A and type B simultaneously.
   //
   // The merge() operation is defined as modifying prev_state in-place to be the state object which represents the composition of
   // state A and B.
   //
   // Type A (and AB) can be implemented as a no-op; prev_state already contains the correct value for the merged state.
   // Type B (and AB) can be implemented by copying from state to prev_state.
   // Type C needs special case-by-case logic.
   // Type N/A can be ignored or assert(false) as it can only occur if prev_state and state have illegal values
   // (a serious logic error which should never happen).
   //

   // We can only be outside type A/AB (the nop path) if B is not nop, so it suffices to iterate through B's three containers.

   // *+upd
   for( auto& obj : state.old_values )
   {
      if( prev_state.new_ids.find(obj.second->id) != prev_state.new_ids.end() )
      {
         // new+upd -> new, type A
         continue;
      }
      if( prev_state.old_values.find(obj.second->id) != prev_state.old_values.end() )
      {
         // upd(was=X) + upd(was=Y) -> upd(was=X), type A
         continue;
      }
      // del+upd -> N/A
      assert( prev_state.removed.find(obj.second->id) == prev_state.removed.end() );
      // nop+upd(was=Y) -> upd(was=Y), type B
      prev_state.old_values[obj.second->id] = std::move(obj.second);
   }

   // *+new, but we assume the N/A cases don't happen, leaving type B nop+new -> new
   for( auto id : state.new_ids )
      prev_state.new_ids.insert(id);

   // old_index_next_ids can only be updated, iterate over *+upd cases
   for( auto& item : state.old_index_next_ids )
   {
      if( prev_state.old_index_next_ids.find( item.first ) == prev_state.old_index_next_ids.end() )
      {
         // nop+upd(was=Y) -> upd(was=Y), type B
         prev_state.old_index_next_ids[item.first] = item.second;
         continue;
      }
      else
      {
         // upd(was=X)+upd(was=Y) -> upd(was=X), type A
         // type A implementation is a no-op, as discussed above, so there is no code here
         continue;
      }
   }

   // *+del
   for( auto& obj : state.removed )
   {
      if( prev_state.new_ids.find(obj.second->id) != prev_state.new_ids.end() )
      {
         // new + del -> nop (type C)
         prev_state.new_ids.erase(obj.second->id);
         continue;
      }
      auto it = prev_state.old_values.find(obj.second->id);
      if( it != prev_state.old_values.end() )
      {
         // upd(was=X) + del(was=Y) -> del(was=X)
         prev_state.removed[obj.second->id] = std::move(it->second);
         prev_state.old_values.erase(obj.second->id);
         continue;
      }
      // del + del -> N/A
      assert( prev_state.removed.find( obj.second->id ) == prev_state.removed.end() );
      // nop + del(was=Y) -> del(was=Y)
      prev_state.removed[obj.second->id] = std::move(obj.second);
   }
   _stack.pop_back();
   --_active_sessions;
}
void undo_database::commit()
{
   FC_ASSERT( _active_sessions > 0 );
   --_active_sessions;
}

void undo_database::pop_commit()
{
   FC_ASSERT( _active_sessions == 0 );
   FC_ASSERT( !_stack.empty() );

   disable();
   try {
      auto& state = _stack.back();

      for( auto& item : state.old_values )
      {
         _db.modify( _db.get_object( item.second->id ), [&]( object& obj ){ obj.move_from( *item.second ); } );
      }

      for( auto ritr = state.new_ids.begin(); ritr != state.new_ids.end(); ++ritr  )
      {
         _db.remove( _db.get_object(*ritr) );
      }

      for( auto& item : state.old_index_next_ids )
      {
         _db.get_mutable_index( item.first.space(), item.first.type() ).set_next_id( item.second );
      }

      for( auto& item : state.removed )
         _db.insert( std::move(*item.second) );

      _stack.pop_back();
   }
   catch ( const fc::exception& e )
   {
      elog( "error popping commit ${e}", ("e", e.to_detail_string() )  );
      enable();
      throw;
   }
   enable();
}
const undo_state& undo_database::head()const
{
   FC_ASSERT( !_stack.empty() );
   return _stack.back();
}

} } // graphene::db
<|endoftext|>