<commit_before>//===----------------------------------------------------------------------===//
//
//                         PelotonDB
//
// index_test.cpp
//
// Identification: test/index/index_util_test.cpp
//
// Copyright (c) 2015-16, Carnegie Mellon University Database Group
//
//===----------------------------------------------------------------------===//

#include "gtest/gtest.h"
#include "common/harness.h"

#include "common/logger.h"
#include "common/platform.h"
#include "index/index_factory.h"
#include "storage/tuple.h"
#include "index/index_util.h"
#include "index/scan_optimizer.h"

namespace peloton {
namespace test {

using namespace index;
using namespace storage;

class IndexUtilTests : public PelotonTest {};

// These two are here since the IndexMetadata object does not claim
// ownership for the two schema objects so they will be not destroyed
// automatically
//
// Put them here to avoid Valgrind warning
static catalog::Schema *tuple_schema = nullptr;
static catalog::Schema *key_schema = nullptr;

/*
 * BuildIndex() - Builds an index with 4 columns
 *
 * The index has 4 columns as tuple key (A, B, C, D), and three of them
 * are indexed:
 *
 * tuple key: 0 1 2 3
 * index key: 3 0   1 (i.e. the 1st column of index key is the 3rd column of
 *                     tuple key)
 */
static index::Index *BuildIndex() {
  // Build tuple and key schema
  std::vector<catalog::Column> tuple_column_list{};
  std::vector<catalog::Column> index_column_list{};

  // The following key are both in index key and tuple key and they are
  // indexed
  // The size of the key is:
  //   integer 4 * 3 = total 12

  catalog::Column column0(VALUE_TYPE_INTEGER,
                          GetTypeSize(VALUE_TYPE_INTEGER),
                          "A",
                          true);

  catalog::Column column1(VALUE_TYPE_VARCHAR,
                          1024,
                          "B",
                          false);

  // The following twoc constitutes tuple schema but does not appear in index

  catalog::Column column2(VALUE_TYPE_DOUBLE,
                          GetTypeSize(VALUE_TYPE_DOUBLE),
                          "C",
                          true);

  catalog::Column column3(VALUE_TYPE_INTEGER,
                          GetTypeSize(VALUE_TYPE_INTEGER),
                          "D",
                          true);

  // Use all four columns to build tuple schema

  tuple_column_list.push_back(column0);
  tuple_column_list.push_back(column1);
  tuple_column_list.push_back(column2);
  tuple_column_list.push_back(column3);

  tuple_schema = new catalog::Schema(tuple_column_list);

  // Use column 3, 0, 1 to build index key

  index_column_list.push_back(column3);
  index_column_list.push_back(column0);
  index_column_list.push_back(column1);

  // This will be copied into the key schema as well as into the IndexMetadata
  // object to identify indexed columns
  std::vector<oid_t> key_attrs = {3, 0, 1};

  // key schame also needs the mapping relation from index key to tuple key
  key_schema = new catalog::Schema(index_column_list);
  key_schema->SetIndexedColumns(key_attrs);

  // Build index metadata
  //
  // NOTE: Since here we use a relatively small key (size = 12)
  // so index_test is only testing with a certain kind of key
  // (most likely, GenericKey)
  //
  // For testing IntsKey and TupleKey we need more test cases
  index::IndexMetadata *index_metadata = \
    new index::IndexMetadata("index_util_test",
                             88888,                       // Index oid
                             INDEX_TYPE_BWTREE,
                             INDEX_CONSTRAINT_TYPE_DEFAULT,
                             tuple_schema,
                             key_schema,
                             key_attrs,
                             true);                      // unique_keys

  // Build index
  index::Index *index = index::IndexFactory::GetInstance(index_metadata);

  // Actually this will never be hit since if index creation fails an exception
  // would be raised (maybe out of memory would result in a nullptr? Anyway
  // leave it here)
  EXPECT_TRUE(index != NULL);

  return index;
}

/*
 * FindValueIndexTest() - Tests whether the index util correctly recognizes
 *                        point query
 *
 * The index configuration is as follows:
 *
 * tuple key: 0 1 2 3
 * index_key: 3 0 1
 */
TEST_F(IndexUtilTests, FindValueIndexTest) {
  const index::Index *index_p = BuildIndex();
  bool ret;

  std::vector<std::pair<oid_t, oid_t>> value_index_list{};

  // Test basic

  ret = FindValueIndex(index_p->GetMetadata(),
                       {3, 0, 1},
                       {EXPRESSION_TYPE_COMPARE_EQUAL,
                        EXPRESSION_TYPE_COMPARE_EQUAL,
                        EXPRESSION_TYPE_COMPARE_EQUAL},
                       value_index_list);
  EXPECT_EQ(ret, true);
  value_index_list.clear();

  ret = FindValueIndex(index_p->GetMetadata(),
                       {1, 0, 3},
                       {EXPRESSION_TYPE_COMPARE_EQUAL,
                        EXPRESSION_TYPE_COMPARE_EQUAL,
                        EXPRESSION_TYPE_COMPARE_EQUAL},
                       value_index_list);
  EXPECT_EQ(ret, true);
  value_index_list.clear();

  ret = FindValueIndex(index_p->GetMetadata(),
                       {0, 1, 3},
                       {EXPRESSION_TYPE_COMPARE_EQUAL,
                        EXPRESSION_TYPE_COMPARE_EQUAL,
                        EXPRESSION_TYPE_COMPARE_EQUAL},
                       value_index_list);
  EXPECT_EQ(ret, true);
  value_index_list.clear();

  // Test whether reconizes if only two columns are matched

  ret = FindValueIndex(index_p->GetMetadata(),
                       {0, 1},
                       {EXPRESSION_TYPE_COMPARE_EQUAL,
                        EXPRESSION_TYPE_COMPARE_EQUAL},
                       value_index_list);
  EXPECT_EQ(ret, false);
  value_index_list.clear();

  ret = FindValueIndex(index_p->GetMetadata(),
                       {3, 0},
                       {EXPRESSION_TYPE_COMPARE_EQUAL,
                        EXPRESSION_TYPE_COMPARE_EQUAL},
                       value_index_list);
  EXPECT_EQ(ret, false);
  value_index_list.clear();

  // Test empty

  ret = FindValueIndex(index_p->GetMetadata(),
                       {},
                       {},
                       value_index_list);
  EXPECT_EQ(ret, false);
  value_index_list.clear();

  // Test redundant conditions

  // This should return false, since the < already defines a lower bound
  ret = FindValueIndex(index_p->GetMetadata(),
                       {0, 3, 3, 0, 3, 1},
                       {EXPRESSION_TYPE_COMPARE_LESSTHAN,
                        EXPRESSION_TYPE_COMPARE_EQUAL,
                        EXPRESSION_TYPE_COMPARE_LESSTHAN,
                        EXPRESSION_TYPE_COMPARE_EQUAL,
                        EXPRESSION_TYPE_COMPARE_EQUAL,
                        EXPRESSION_TYPE_COMPARE_EQUAL},
                       value_index_list);
  EXPECT_EQ(ret, false);
  value_index_list.clear();

  // This should return true
  ret = FindValueIndex(index_p->GetMetadata(),
                       {0, 3, 3, 0, 3, 1},
                       {EXPRESSION_TYPE_COMPARE_EQUAL,
                        EXPRESSION_TYPE_COMPARE_EQUAL,
                        EXPRESSION_TYPE_COMPARE_LESSTHAN,
                        EXPRESSION_TYPE_COMPARE_LESSTHAN,
                        EXPRESSION_TYPE_COMPARE_EQUAL,
                        EXPRESSION_TYPE_COMPARE_EQUAL},
                       value_index_list);
  EXPECT_EQ(ret, true);
  value_index_list.clear();

  // Test duplicated conditions on a single column

  ret = FindValueIndex(index_p->GetMetadata(),
                       {3, 3, 3},
                       {EXPRESSION_TYPE_COMPARE_EQUAL,
                        EXPRESSION_TYPE_COMPARE_EQUAL,
                        EXPRESSION_TYPE_COMPARE_EQUAL},
                       value_index_list);
  EXPECT_EQ(ret, false);
  value_index_list.clear();

  //
  // The last test should logically be classified as point query
  // but our procedure does not give positive result to reduce
  // the complexity
  //

  ret = FindValueIndex(index_p->GetMetadata(),
                       {3, 0, 1, 0},
                       {EXPRESSION_TYPE_COMPARE_EQUAL,
                        EXPRESSION_TYPE_COMPARE_LESSTHANOREQUALTO,
                        EXPRESSION_TYPE_COMPARE_EQUAL,
                        EXPRESSION_TYPE_COMPARE_GREATERTHANOREQUALTO},
                       value_index_list);
  EXPECT_EQ(ret, false);
  value_index_list.clear();

  return;
}

/*
 * ConstructBoundaryKeyTest() - Tests ConstructBoundaryKey() function for
 *                              conjunctions
 */
TEST_F(IndexUtilTests, ConstructBoundaryKeyTest) {
  index::Index *index_p = BuildIndex();

  // This is the output variable
  std::vector<std::pair<oid_t, oid_t>> value_index_list{};

  std::vector<Value> value_list{};
  std::vector<oid_t> tuple_column_id_list{};
  std::vector<ExpressionType> expr_list{};

  value_list = {ValueFactory::GetIntegerValue(100),
                ValueFactory::GetIntegerValue(200),
                ValueFactory::GetIntegerValue(50), };

  tuple_column_id_list = {3, 3, 0};

  expr_list = {EXPRESSION_TYPE_COMPARE_GREATERTHAN,
               EXPRESSION_TYPE_COMPARE_LESSTHANOREQUALTO,
               EXPRESSION_TYPE_COMPARE_GREATERTHANOREQUALTO, };

  IndexScanPredicate isp{};

  isp.AddConjunctionScanPredicate(index_p,
                                  value_list,
                                  tuple_column_id_list,
                                  expr_list);

  const std::vector<ConjunctionScanPredicate> &cl = isp.GetConjunctionList();

  // First check the conjunction has been pushed into the scan predicate object
  EXPECT_EQ(cl.size(), 1UL);

  // Then make sure all values have been bound (i.e. no free variable)
  EXPECT_EQ(cl[0].GetBindingCount(), 0UL);

  // Check whether the entire predicate is full index scan (should not be)
  EXPECT_EQ(isp.IsFullIndexScan(), false);

  // Then check the conjunction predicate
  EXPECT_EQ(cl[0].IsFullIndexScan(), false);

  // Then check whether the conjunction predicate is a point query
  EXPECT_EQ(cl[0].IsPointQuery(), false);

  LOG_INFO("Low key = %s", cl[0].GetLowKey()->GetInfo().c_str());
  LOG_INFO("High key = %s", cl[0].GetHighKey()->GetInfo().c_str());

  ///////////////////////////////////////////////////////////////////
  // Test the case where there is no optimization that could be done
  //
  // The condition means first index column does not equal 100
  ///////////////////////////////////////////////////////////////////

  value_list = {ValueFactory::GetIntegerValue(100), };

  tuple_column_id_list = {3, };

  expr_list = {EXPRESSION_TYPE_COMPARE_NOTEQUAL, };

  isp.AddConjunctionScanPredicate(index_p,
                                  value_list,
                                  tuple_column_id_list,
                                  expr_list);

  const std::vector<ConjunctionScanPredicate> &cl2 = isp.GetConjunctionList();

  EXPECT_EQ(cl2.size(), 2UL);
  EXPECT_EQ(cl2[1].GetBindingCount(), 0UL);
  EXPECT_EQ(isp.IsFullIndexScan(), true);
  EXPECT_EQ(cl2[1].IsFullIndexScan(), true);
  EXPECT_EQ(cl2[1].IsPointQuery(), false);

  ///////////////////////////////////////////////////////////////////
  // Test point query and query key
  //
  // Index key = <100, 50, "Peloton!">
  ///////////////////////////////////////////////////////////////////

  IndexScanPredicate isp2{};

  value_list = {ValueFactory::GetIntegerValue(100),
                ValueFactory::GetStringValue("Peloton!"),
                ValueFactory::GetIntegerValue(50), };

  tuple_column_id_list = {3, 1, 0};

  expr_list = {EXPRESSION_TYPE_COMPARE_EQUAL,
               EXPRESSION_TYPE_COMPARE_EQUAL,
               EXPRESSION_TYPE_COMPARE_EQUAL, };

  isp2.AddConjunctionScanPredicate(index_p,
                                   value_list,
                                   tuple_column_id_list,
                                   expr_list);

  const std::vector<ConjunctionScanPredicate> &cl3 = isp2.GetConjunctionList();

  EXPECT_EQ(cl3.size(), 1UL);
  EXPECT_EQ(cl3[0].GetBindingCount(), 0UL);
  EXPECT_EQ(isp2.IsFullIndexScan(), false);
  EXPECT_EQ(cl3[0].IsFullIndexScan(), false);
  EXPECT_EQ(cl3[0].IsPointQuery(), true);

  LOG_INFO("Point query key = %s", cl3[0].GetPointQueryKey()->GetInfo().c_str());

  ///////////////////////////////////////////////////////////////////
  // End of all test cases
  ///////////////////////////////////////////////////////////////////

  return;
}

/*
 * BindKeyTest() - Tests binding values onto keys that are not bound
 */
TEST_F(IndexUtilTests, BindKeyTest) {
  index::Index *index_p = BuildIndex();

  // This is the output variable
  std::vector<std::pair<oid_t, oid_t>> value_index_list{};

  std::vector<Value> value_list{};
  std::vector<oid_t> tuple_column_id_list{};
  std::vector<ExpressionType> expr_list{};

  value_list = {ValueFactory::GetBindingOnlyIntegerValue(2),
                ValueFactory::GetBindingOnlyIntegerValue(0),
                ValueFactory::GetBindingOnlyIntegerValue(1), };

  tuple_column_id_list = {3, 3, 0};

  expr_list = {EXPRESSION_TYPE_COMPARE_GREATERTHAN,
               EXPRESSION_TYPE_COMPARE_LESSTHANOREQUALTO,
               EXPRESSION_TYPE_COMPARE_GREATERTHANOREQUALTO, };

  IndexScanPredicate isp{};

  isp.AddConjunctionScanPredicate(index_p,
                                  value_list,
                                  tuple_column_id_list,
                                  expr_list);

  const std::vector<ConjunctionScanPredicate> &cl = isp.GetConjunctionList();

  ///////////////////////////////////////////////////////////////////
  // Test bind for range query
  ///////////////////////////////////////////////////////////////////

  // Basic check to avoid surprise in later tests
  EXPECT_EQ(cl.size(), 1);
  EXPECT_EQ(cl[0].IsPointQuery(), false);
  EXPECT_EQ(isp.IsFullIndexScan(), false);

  // There are three unbound values
  EXPECT_EQ(cl[0].GetBindingCount(), 3);

  // At this time low key and high key should be binded
  // (Note that the type should be also INT but the value is likely to be 0)
  LOG_INFO("Low key (NOT BINDED) = %s", cl[0].GetLowKey()->GetInfo().c_str());
  LOG_INFO("High key (NOT BINDED) = %s", cl[0].GetHighKey()->GetInfo().c_str());

  // Bind real value
  isp.LateBindValues(index_p, {ValueFactory::GetIntegerValue(100),
                               ValueFactory::GetIntegerValue(200),
                               ValueFactory::GetIntegerValue(300)});

  // This is important - Since binding does not change the number of
  // binding points, and their information is preserved for next
  // binding
  EXPECT_EQ(cl[0].GetBindingCount(), 3);

  // At this time low key and high key should be binded
  LOG_INFO("Low key = %s", cl[0].GetLowKey()->GetInfo().c_str());
  LOG_INFO("High key = %s", cl[0].GetHighKey()->GetInfo().c_str());

  ///////////////////////////////////////////////////////////////////
  // End of all tests
  ///////////////////////////////////////////////////////////////////

  return;
}

}  // End test namespace
}  // End peloton namespace
<commit_msg>Fix the memory leak in index_util_test.<commit_after>//===----------------------------------------------------------------------===//
//
//                         PelotonDB
//
// index_test.cpp
//
// Identification: test/index/index_util_test.cpp
//
// Copyright (c) 2015-16, Carnegie Mellon University Database Group
//
//===----------------------------------------------------------------------===//

#include "gtest/gtest.h"
#include "common/harness.h"

#include "common/logger.h"
#include "common/platform.h"
#include "index/index_factory.h"
#include "storage/tuple.h"
#include "index/index_util.h"
#include "index/scan_optimizer.h"

namespace peloton {
namespace test {

using namespace index;
using namespace storage;

class IndexUtilTests : public PelotonTest {};

/*
 * BuildIndex() - Builds an index with 4 columns
 *
 * The index has 4 columns as tuple key (A, B, C, D), and three of them
 * are indexed:
 *
 * tuple key: 0 1 2 3
 * index key: 3 0   1 (i.e. the 1st column of index key is the 3rd column of
 *                     tuple key)
 */

// These two are here since the IndexMetadata object does not claim
// ownership for the two schema objects so they will be not destroyed
// automatically
//
// Put them here to avoid Valgrind warning
static std::unique_ptr<catalog::Schema> tuple_schema = nullptr;

static index::Index *BuildIndex() {
  // Build tuple and key schema
  std::vector<catalog::Column> tuple_column_list{};
  std::vector<catalog::Column> index_column_list{};

  // The following key are both in index key and tuple key and they are
  // indexed
  // The size of the key is:
  //   integer 4 * 3 = total 12

  catalog::Column column0(VALUE_TYPE_INTEGER, GetTypeSize(VALUE_TYPE_INTEGER),
                          "A", true);

  catalog::Column column1(VALUE_TYPE_VARCHAR, 1024, "B", false);

  // The following twoc constitutes tuple schema but does not appear in index

  catalog::Column column2(VALUE_TYPE_DOUBLE, GetTypeSize(VALUE_TYPE_DOUBLE),
                          "C", true);

  catalog::Column column3(VALUE_TYPE_INTEGER, GetTypeSize(VALUE_TYPE_INTEGER),
                          "D", true);

  // Use all four columns to build tuple schema

  tuple_column_list.push_back(column0);
  tuple_column_list.push_back(column1);
  tuple_column_list.push_back(column2);
  tuple_column_list.push_back(column3);

  if (tuple_schema == nullptr) {
    tuple_schema.reset(new catalog::Schema(tuple_column_list));
  }

  // Use column 3, 0, 1 to build index key

  index_column_list.push_back(column3);
  index_column_list.push_back(column0);
  index_column_list.push_back(column1);

  // This will be copied into the key schema as well as into the IndexMetadata
  // object to identify indexed columns
  std::vector<oid_t> key_attrs = {3, 0, 1};

  // key schame also needs the mapping relation from index key to tuple key
  auto key_schema = new catalog::Schema(index_column_list);
  key_schema->SetIndexedColumns(key_attrs);

  // Build index metadata
  //
  // NOTE: Since here we use a relatively small key (size = 12)
  // so index_test is only testing with a certain kind of key
  // (most likely, GenericKey)
  //
  // For testing IntsKey and TupleKey we need more test cases
  index::IndexMetadata *index_metadata = new index::IndexMetadata(
      "index_util_test", 88888,  // Index oid
      INDEX_TYPE_BWTREE, INDEX_CONSTRAINT_TYPE_DEFAULT, tuple_schema.get(),
      key_schema, key_attrs, true);  // unique_keys

  // Build index
  index::Index *index = index::IndexFactory::GetInstance(index_metadata);

  // Actually this will never be hit since if index creation fails an exception
  // would be raised (maybe out of memory would result in a nullptr? Anyway
  // leave it here)
  EXPECT_TRUE(index != NULL);

  return index;
}

/*
 * FindValueIndexTest() - Tests whether the index util correctly recognizes
 *                        point query
 *
 * The index configuration is as follows:
 *
 * tuple key: 0 1 2 3
 * index_key: 3 0 1
 */
TEST_F(IndexUtilTests, FindValueIndexTest) {
  const index::Index *index_p = BuildIndex();
  bool ret;

  std::vector<std::pair<oid_t, oid_t>> value_index_list{};

  // Test basic

  ret = FindValueIndex(
      index_p->GetMetadata(), {3, 0, 1},
      {EXPRESSION_TYPE_COMPARE_EQUAL, EXPRESSION_TYPE_COMPARE_EQUAL,
       EXPRESSION_TYPE_COMPARE_EQUAL},
      value_index_list);
  EXPECT_EQ(ret, true);
  value_index_list.clear();

  ret = FindValueIndex(
      index_p->GetMetadata(), {1, 0, 3},
      {EXPRESSION_TYPE_COMPARE_EQUAL, EXPRESSION_TYPE_COMPARE_EQUAL,
       EXPRESSION_TYPE_COMPARE_EQUAL},
      value_index_list);
  EXPECT_EQ(ret, true);
  value_index_list.clear();

  ret = FindValueIndex(
      index_p->GetMetadata(), {0, 1, 3},
      {EXPRESSION_TYPE_COMPARE_EQUAL, EXPRESSION_TYPE_COMPARE_EQUAL,
       EXPRESSION_TYPE_COMPARE_EQUAL},
      value_index_list);
  EXPECT_EQ(ret, true);
  value_index_list.clear();

  // Test whether reconizes if only two columns are matched

  ret = FindValueIndex(
      index_p->GetMetadata(), {0, 1},
      {EXPRESSION_TYPE_COMPARE_EQUAL, EXPRESSION_TYPE_COMPARE_EQUAL},
      value_index_list);
  EXPECT_EQ(ret, false);
  value_index_list.clear();

  ret = FindValueIndex(
      index_p->GetMetadata(), {3, 0},
      {EXPRESSION_TYPE_COMPARE_EQUAL, EXPRESSION_TYPE_COMPARE_EQUAL},
      value_index_list);
  EXPECT_EQ(ret, false);
  value_index_list.clear();

  // Test empty

  ret = FindValueIndex(index_p->GetMetadata(), {}, {}, value_index_list);
  EXPECT_EQ(ret, false);
  value_index_list.clear();

  // Test redundant conditions

  // This should return false, since the < already defines a lower bound
  ret = FindValueIndex(
      index_p->GetMetadata(), {0, 3, 3, 0, 3, 1},
      {EXPRESSION_TYPE_COMPARE_LESSTHAN, EXPRESSION_TYPE_COMPARE_EQUAL,
       EXPRESSION_TYPE_COMPARE_LESSTHAN, EXPRESSION_TYPE_COMPARE_EQUAL,
       EXPRESSION_TYPE_COMPARE_EQUAL,    EXPRESSION_TYPE_COMPARE_EQUAL},
      value_index_list);
  EXPECT_EQ(ret, false);
  value_index_list.clear();

  // This should return true
  ret = FindValueIndex(
      index_p->GetMetadata(), {0, 3, 3, 0, 3, 1},
      {EXPRESSION_TYPE_COMPARE_EQUAL,    EXPRESSION_TYPE_COMPARE_EQUAL,
       EXPRESSION_TYPE_COMPARE_LESSTHAN, EXPRESSION_TYPE_COMPARE_LESSTHAN,
       EXPRESSION_TYPE_COMPARE_EQUAL,    EXPRESSION_TYPE_COMPARE_EQUAL},
      value_index_list);
  EXPECT_EQ(ret, true);
  value_index_list.clear();

  // Test duplicated conditions on a single column

  ret = FindValueIndex(
      index_p->GetMetadata(), {3, 3, 3},
      {EXPRESSION_TYPE_COMPARE_EQUAL, EXPRESSION_TYPE_COMPARE_EQUAL,
       EXPRESSION_TYPE_COMPARE_EQUAL},
      value_index_list);
  EXPECT_EQ(ret, false);
  value_index_list.clear();

  //
  // The last test should logically be classified as point query
  // but our procedure does not give positive result to reduce
  // the complexity
  //

  ret = FindValueIndex(index_p->GetMetadata(), {3, 0, 1, 0},
                       {EXPRESSION_TYPE_COMPARE_EQUAL,
                        EXPRESSION_TYPE_COMPARE_LESSTHANOREQUALTO,
                        EXPRESSION_TYPE_COMPARE_EQUAL,
                        EXPRESSION_TYPE_COMPARE_GREATERTHANOREQUALTO},
                       value_index_list);
  EXPECT_EQ(ret, false);
  value_index_list.clear();

  delete index_p;

  return;
}

/*
 * ConstructBoundaryKeyTest() - Tests ConstructBoundaryKey() function for
 *                              conjunctions
 */
TEST_F(IndexUtilTests, ConstructBoundaryKeyTest) {
  index::Index *index_p = BuildIndex();

  // This is the output variable
  std::vector<std::pair<oid_t, oid_t>> value_index_list{};

  std::vector<Value> value_list{};
  std::vector<oid_t> tuple_column_id_list{};
  std::vector<ExpressionType> expr_list{};

  value_list = {ValueFactory::GetIntegerValue(100),
                ValueFactory::GetIntegerValue(200),
                ValueFactory::GetIntegerValue(50), };

  tuple_column_id_list = {3, 3, 0};

  expr_list = {EXPRESSION_TYPE_COMPARE_GREATERTHAN,
               EXPRESSION_TYPE_COMPARE_LESSTHANOREQUALTO,
               EXPRESSION_TYPE_COMPARE_GREATERTHANOREQUALTO, };

  IndexScanPredicate isp{};

  isp.AddConjunctionScanPredicate(index_p, value_list, tuple_column_id_list,
                                  expr_list);

  const std::vector<ConjunctionScanPredicate> &cl = isp.GetConjunctionList();

  // First check the conjunction has been pushed into the scan predicate object
  EXPECT_EQ(cl.size(), 1UL);

  // Then make sure all values have been bound (i.e. no free variable)
  EXPECT_EQ(cl[0].GetBindingCount(), 0UL);

  // Check whether the entire predicate is full index scan (should not be)
  EXPECT_EQ(isp.IsFullIndexScan(), false);

  // Then check the conjunction predicate
  EXPECT_EQ(cl[0].IsFullIndexScan(), false);

  // Then check whether the conjunction predicate is a point query
  EXPECT_EQ(cl[0].IsPointQuery(), false);

  LOG_INFO("Low key = %s", cl[0].GetLowKey()->GetInfo().c_str());
  LOG_INFO("High key = %s", cl[0].GetHighKey()->GetInfo().c_str());

  ///////////////////////////////////////////////////////////////////
  // Test the case where there is no optimization that could be done
  //
  // The condition means first index column does not equal 100
  ///////////////////////////////////////////////////////////////////

  value_list = {ValueFactory::GetIntegerValue(100), };

  tuple_column_id_list = {3, };

  expr_list = {EXPRESSION_TYPE_COMPARE_NOTEQUAL, };

  isp.AddConjunctionScanPredicate(index_p, value_list, tuple_column_id_list,
                                  expr_list);

  const std::vector<ConjunctionScanPredicate> &cl2 = isp.GetConjunctionList();

  EXPECT_EQ(cl2.size(), 2UL);
  EXPECT_EQ(cl2[1].GetBindingCount(), 0UL);
  EXPECT_EQ(isp.IsFullIndexScan(), true);
  EXPECT_EQ(cl2[1].IsFullIndexScan(), true);
  EXPECT_EQ(cl2[1].IsPointQuery(), false);

  ///////////////////////////////////////////////////////////////////
  // Test point query and query key
  //
  // Index key = <100, 50, "Peloton!">
  ///////////////////////////////////////////////////////////////////

  IndexScanPredicate isp2{};

  value_list = {ValueFactory::GetIntegerValue(100),
                ValueFactory::GetStringValue("Peloton!"),
                ValueFactory::GetIntegerValue(50), };

  tuple_column_id_list = {3, 1, 0};

  expr_list = {EXPRESSION_TYPE_COMPARE_EQUAL, EXPRESSION_TYPE_COMPARE_EQUAL,
               EXPRESSION_TYPE_COMPARE_EQUAL, };

  isp2.AddConjunctionScanPredicate(index_p, value_list, tuple_column_id_list,
                                   expr_list);

  const std::vector<ConjunctionScanPredicate> &cl3 = isp2.GetConjunctionList();

  EXPECT_EQ(cl3.size(), 1UL);
  EXPECT_EQ(cl3[0].GetBindingCount(), 0UL);
  EXPECT_EQ(isp2.IsFullIndexScan(), false);
  EXPECT_EQ(cl3[0].IsFullIndexScan(), false);
  EXPECT_EQ(cl3[0].IsPointQuery(), true);

  LOG_INFO("Point query key = %s",
           cl3[0].GetPointQueryKey()->GetInfo().c_str());

  ///////////////////////////////////////////////////////////////////
  // End of all test cases
  ///////////////////////////////////////////////////////////////////
  delete index_p;

  return;
}

/*
 * BindKeyTest() - Tests binding values onto keys that are not bound
 */
TEST_F(IndexUtilTests, BindKeyTest) {
  index::Index *index_p = BuildIndex();

  // This is the output variable
  std::vector<std::pair<oid_t, oid_t>> value_index_list{};

  std::vector<Value> value_list{};
  std::vector<oid_t> tuple_column_id_list{};
  std::vector<ExpressionType> expr_list{};

  value_list = {ValueFactory::GetBindingOnlyIntegerValue(2),
                ValueFactory::GetBindingOnlyIntegerValue(0),
                ValueFactory::GetBindingOnlyIntegerValue(1), };

  tuple_column_id_list = {3, 3, 0};

  expr_list = {EXPRESSION_TYPE_COMPARE_GREATERTHAN,
               EXPRESSION_TYPE_COMPARE_LESSTHANOREQUALTO,
               EXPRESSION_TYPE_COMPARE_GREATERTHANOREQUALTO, };

  IndexScanPredicate isp{};

  isp.AddConjunctionScanPredicate(index_p, value_list, tuple_column_id_list,
                                  expr_list);

  const std::vector<ConjunctionScanPredicate> &cl = isp.GetConjunctionList();

  ///////////////////////////////////////////////////////////////////
  // Test bind for range query
  ///////////////////////////////////////////////////////////////////

  // Basic check to avoid surprise in later tests
  EXPECT_EQ(cl.size(), 1);
  EXPECT_EQ(cl[0].IsPointQuery(), false);
  EXPECT_EQ(isp.IsFullIndexScan(), false);

  // There are three unbound values
  EXPECT_EQ(cl[0].GetBindingCount(), 3);

  // At this time low key and high key should be binded
  // (Note that the type should be also INT but the value is likely to be 0)
  LOG_INFO("Low key (NOT BINDED) = %s", cl[0].GetLowKey()->GetInfo().c_str());
  LOG_INFO("High key (NOT BINDED) = %s", cl[0].GetHighKey()->GetInfo().c_str());

  // Bind real value
  isp.LateBindValues(index_p, {ValueFactory::GetIntegerValue(100),
                               ValueFactory::GetIntegerValue(200),
                               ValueFactory::GetIntegerValue(300)});

  // This is important - Since binding does not change the number of
  // binding points, and their information is preserved for next
  // binding
  EXPECT_EQ(cl[0].GetBindingCount(), 3);

  // At this time low key and high key should be binded
  LOG_INFO("Low key = %s", cl[0].GetLowKey()->GetInfo().c_str());
  LOG_INFO("High key = %s", cl[0].GetHighKey()->GetInfo().c_str());

  ///////////////////////////////////////////////////////////////////
  // End of all tests
  ///////////////////////////////////////////////////////////////////
  delete index_p;

  return;
}

}  // End test namespace
}  // End peloton namespace
<|endoftext|>


<commit_before>#include "catch.hpp"

#include "../include/vector.h"

TEST_CASE("construct a new vector", "[vector]") {
    jade::vector<int> v;
    REQUIRE(v.size() == 0);
    REQUIRE(v.capacity() == 0);
}

TEST_CASE("copy construct a new vector", "[vector]") {
    jade::vector<int> v1;
    v1.push_back(1);
    v1.push_back(2);
    v1.push_back(3);

    jade::vector<int> v2(v1);

    REQUIRE(v2.size() == 3);
    REQUIRE(v2.capacity() == 4);

    int k = 1;
    for (const auto& i : v2) {
        REQUIRE(k++ == i);
    }
}

TEST_CASE("assignement operator", "[vector]") {
    jade::vector<int> v1;
    v1.push_back(1);
    v1.push_back(2);

    REQUIRE(v1.size() == 2);
    REQUIRE(v1.capacity() == 2);

    jade::vector<int> v2;
    v2.push_back(3);
    v2.push_back(4);
    v2.push_back(5);

    REQUIRE(v2.size() == 3);
    REQUIRE(v2.capacity() == 4);

    v2 = v1;

    REQUIRE(v2.size() == 2);
    REQUIRE(v2.capacity() == 2);

    int k = 1;
    for (const auto& i : v2) {
        REQUIRE(k++ == i);
    }
}

TEST_CASE("move assignement operator", "[vector]") {
    jade::vector<int> v1{1,2,3};
    REQUIRE(v1.size() == 3);
    REQUIRE(v1.capacity() == 3);

    jade::vector<int> v2{4,5,6,7,8};
    REQUIRE(v2.size() == 5);
    REQUIRE(v2.capacity() == 5);

    v1 = std::move(v2);
    REQUIRE(v1.size() == 5);
    REQUIRE(v1.capacity() == 5);
    REQUIRE(v2.size() == 0);
    REQUIRE(v2.capacity() == 0);
}

TEST_CASE("initializer list assginment operator") {
    jade::vector<int> v{1,2,3};
    REQUIRE(v.size() == 3);
    REQUIRE(v.capacity() == 3);
    REQUIRE(v[0] == 1);
    REQUIRE(v[1] == 2);
    REQUIRE(v[2] == 3);

    v = {4,5,6,7};
    REQUIRE(v.size() == 4);
    REQUIRE(v.capacity() == 4);
    REQUIRE(v[0] == 4);
    REQUIRE(v[1] == 5);
    REQUIRE(v[2] == 6);
    REQUIRE(v[3] == 7);
}

TEST_CASE("capacity doubles everytime the size increases to the current max. capacity", "[vector]") {
    jade::vector<int> v;
    v.push_back(1);
    REQUIRE(v.size() == 1);
    REQUIRE(v.capacity() == 1);
    v.push_back(2);
    REQUIRE(v.size() == 2);
    REQUIRE(v.capacity() == 2);
    v.push_back(3);
    REQUIRE(v.size() == 3);
    REQUIRE(v.capacity() == 4);
    v.push_back(4);
    REQUIRE(v.size() == 4);
    REQUIRE(v.capacity() == 4);
    v.push_back(5);
    REQUIRE(v.size() == 5);
    REQUIRE(v.capacity() == 8);
    v.push_back(6);
    v.push_back(7);
    v.push_back(8);
    REQUIRE(v.size() == 8);
    REQUIRE(v.capacity() == 8);
    v.push_back(9);
    REQUIRE(v.size() == 9);
    REQUIRE(v.capacity() == 16);
}

TEST_CASE("constructor with initializer list", "[vector]") {
    jade::vector<int> v = { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
    REQUIRE(v.capacity() == 9);
    REQUIRE(v.size() == 9);
    REQUIRE(v[0] == 1);
    REQUIRE(v[1] == 2);
    REQUIRE(v[2] == 3);
    REQUIRE(v[3] == 4);
    REQUIRE(v[4] == 5);
    REQUIRE(v[5] == 6);
    REQUIRE(v[6] == 7);
    REQUIRE(v[7] == 8);
    REQUIRE(v[8] == 9);
}

TEST_CASE("vector can be constructed with a number copies of elements with a certain value", "[vector]") {
    SECTION("default-inserted instances of T", "[vector]") {
        jade::vector<int> v1(50);
        REQUIRE(v1.capacity() == 50);
        REQUIRE(v1.size() == 50);
        for (const auto i : v1) {
            REQUIRE(i == 0);
        }
    }
    SECTION("copies of provided value", "[vector]") {
        jade::vector<int> v1(50, 123);
        REQUIRE(v1.capacity() == 50);
        REQUIRE(v1.size() == 50);
        for (const auto i : v1) {
            REQUIRE(i == 123);
        }
    }
}

TEST_CASE("move constructor", "[vector]") {
    jade::vector<int> v1 = { 1, 2, 3 };
    REQUIRE(v1.size() == 3);
    REQUIRE(v1.capacity() == 3);

    jade::vector<int> v2{std::move(v1)};
    REQUIRE(v1.size() == 0);
    REQUIRE(v1.capacity() == 0);
    REQUIRE(v2.size() == 3);
    REQUIRE(v2.capacity() == 3);
}

TEST_CASE("a vector is resizable", "[vector]") {
    jade::vector<int> v;
    v.resize(16);
    REQUIRE(v.capacity() == 16);
    REQUIRE(v.size() == 0);
}

TEST_CASE("a new vector is resizable to zero", "[vector]") {
    jade::vector<int> v;
    v.resize(0);
    REQUIRE(v.capacity() == 0);
    REQUIRE(v.size() == 0);
}

TEST_CASE("resizing a vector keeps it consistent", "[vector]") {
    jade::vector<int> v;
    REQUIRE(v.capacity() == 0);
    REQUIRE(v.size() == 0);

    v.push_back(1);
    v.push_back(2);
    v.push_back(3);
    REQUIRE(v.capacity() == 4);
    REQUIRE(v.size() == 3);

    v.resize(16);
    REQUIRE(v.capacity() == 16);
    REQUIRE(v.size() == 3);

    v.resize(0);
    REQUIRE(v.capacity() == 0);
    REQUIRE(v.size() == 0);

    v.push_back(1);
    v.push_back(2);
    v.push_back(3);
    REQUIRE(v.capacity() == 4);
    REQUIRE(v.size() == 3);

    v.resize(16);
    REQUIRE(v.capacity() == 16);
    REQUIRE(v.size() == 3);
}

TEST_CASE("at method checks bounds of vector", "[vector]") {
    jade::vector<int> v = { 1, 2, 3, 4, 5 };
    REQUIRE_THROWS_AS(v.at(-1), std::out_of_range);
    REQUIRE_NOTHROW(v.at(0));
    REQUIRE_NOTHROW(v.at(2));
    REQUIRE_NOTHROW(v.at(4));
    REQUIRE_THROWS_AS(v.at(5), std::out_of_range);
}
<commit_msg>fix typo in test description<commit_after>#include "catch.hpp"

#include "../include/vector.h"

TEST_CASE("construct a new vector", "[vector]") {
    jade::vector<int> v;
    REQUIRE(v.size() == 0);
    REQUIRE(v.capacity() == 0);
}

TEST_CASE("copy construct a new vector", "[vector]") {
    jade::vector<int> v1;
    v1.push_back(1);
    v1.push_back(2);
    v1.push_back(3);

    jade::vector<int> v2(v1);

    REQUIRE(v2.size() == 3);
    REQUIRE(v2.capacity() == 4);

    int k = 1;
    for (const auto& i : v2) {
        REQUIRE(k++ == i);
    }
}

TEST_CASE("assignment operator", "[vector]") {
    jade::vector<int> v1;
    v1.push_back(1);
    v1.push_back(2);

    REQUIRE(v1.size() == 2);
    REQUIRE(v1.capacity() == 2);

    jade::vector<int> v2;
    v2.push_back(3);
    v2.push_back(4);
    v2.push_back(5);

    REQUIRE(v2.size() == 3);
    REQUIRE(v2.capacity() == 4);

    v2 = v1;

    REQUIRE(v2.size() == 2);
    REQUIRE(v2.capacity() == 2);

    int k = 1;
    for (const auto& i : v2) {
        REQUIRE(k++ == i);
    }
}

TEST_CASE("move assignment operator", "[vector]") {
    jade::vector<int> v1{1,2,3};
    REQUIRE(v1.size() == 3);
    REQUIRE(v1.capacity() == 3);

    jade::vector<int> v2{4,5,6,7,8};
    REQUIRE(v2.size() == 5);
    REQUIRE(v2.capacity() == 5);

    v1 = std::move(v2);
    REQUIRE(v1.size() == 5);
    REQUIRE(v1.capacity() == 5);
    REQUIRE(v2.size() == 0);
    REQUIRE(v2.capacity() == 0);
}

TEST_CASE("initializer list assginment operator") {
    jade::vector<int> v{1,2,3};
    REQUIRE(v.size() == 3);
    REQUIRE(v.capacity() == 3);
    REQUIRE(v[0] == 1);
    REQUIRE(v[1] == 2);
    REQUIRE(v[2] == 3);

    v = {4,5,6,7};
    REQUIRE(v.size() == 4);
    REQUIRE(v.capacity() == 4);
    REQUIRE(v[0] == 4);
    REQUIRE(v[1] == 5);
    REQUIRE(v[2] == 6);
    REQUIRE(v[3] == 7);
}

TEST_CASE("capacity doubles everytime the size increases to the current max. capacity", "[vector]") {
    jade::vector<int> v;
    v.push_back(1);
    REQUIRE(v.size() == 1);
    REQUIRE(v.capacity() == 1);
    v.push_back(2);
    REQUIRE(v.size() == 2);
    REQUIRE(v.capacity() == 2);
    v.push_back(3);
    REQUIRE(v.size() == 3);
    REQUIRE(v.capacity() == 4);
    v.push_back(4);
    REQUIRE(v.size() == 4);
    REQUIRE(v.capacity() == 4);
    v.push_back(5);
    REQUIRE(v.size() == 5);
    REQUIRE(v.capacity() == 8);
    v.push_back(6);
    v.push_back(7);
    v.push_back(8);
    REQUIRE(v.size() == 8);
    REQUIRE(v.capacity() == 8);
    v.push_back(9);
    REQUIRE(v.size() == 9);
    REQUIRE(v.capacity() == 16);
}

TEST_CASE("constructor with initializer list", "[vector]") {
    jade::vector<int> v = { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
    REQUIRE(v.capacity() == 9);
    REQUIRE(v.size() == 9);
    REQUIRE(v[0] == 1);
    REQUIRE(v[1] == 2);
    REQUIRE(v[2] == 3);
    REQUIRE(v[3] == 4);
    REQUIRE(v[4] == 5);
    REQUIRE(v[5] == 6);
    REQUIRE(v[6] == 7);
    REQUIRE(v[7] == 8);
    REQUIRE(v[8] == 9);
}

TEST_CASE("vector can be constructed with a number copies of elements with a certain value", "[vector]") {
    SECTION("default-inserted instances of T", "[vector]") {
        jade::vector<int> v1(50);
        REQUIRE(v1.capacity() == 50);
        REQUIRE(v1.size() == 50);
        for (const auto i : v1) {
            REQUIRE(i == 0);
        }
    }
    SECTION("copies of provided value", "[vector]") {
        jade::vector<int> v1(50, 123);
        REQUIRE(v1.capacity() == 50);
        REQUIRE(v1.size() == 50);
        for (const auto i : v1) {
            REQUIRE(i == 123);
        }
    }
}

TEST_CASE("move constructor", "[vector]") {
    jade::vector<int> v1 = { 1, 2, 3 };
    REQUIRE(v1.size() == 3);
    REQUIRE(v1.capacity() == 3);

    jade::vector<int> v2{std::move(v1)};
    REQUIRE(v1.size() == 0);
    REQUIRE(v1.capacity() == 0);
    REQUIRE(v2.size() == 3);
    REQUIRE(v2.capacity() == 3);
}

TEST_CASE("a vector is resizable", "[vector]") {
    jade::vector<int> v;
    v.resize(16);
    REQUIRE(v.capacity() == 16);
    REQUIRE(v.size() == 0);
}

TEST_CASE("a new vector is resizable to zero", "[vector]") {
    jade::vector<int> v;
    v.resize(0);
    REQUIRE(v.capacity() == 0);
    REQUIRE(v.size() == 0);
}

TEST_CASE("resizing a vector keeps it consistent", "[vector]") {
    jade::vector<int> v;
    REQUIRE(v.capacity() == 0);
    REQUIRE(v.size() == 0);

    v.push_back(1);
    v.push_back(2);
    v.push_back(3);
    REQUIRE(v.capacity() == 4);
    REQUIRE(v.size() == 3);

    v.resize(16);
    REQUIRE(v.capacity() == 16);
    REQUIRE(v.size() == 3);

    v.resize(0);
    REQUIRE(v.capacity() == 0);
    REQUIRE(v.size() == 0);

    v.push_back(1);
    v.push_back(2);
    v.push_back(3);
    REQUIRE(v.capacity() == 4);
    REQUIRE(v.size() == 3);

    v.resize(16);
    REQUIRE(v.capacity() == 16);
    REQUIRE(v.size() == 3);
}

TEST_CASE("at method checks bounds of vector", "[vector]") {
    jade::vector<int> v = { 1, 2, 3, 4, 5 };
    REQUIRE_THROWS_AS(v.at(-1), std::out_of_range);
    REQUIRE_NOTHROW(v.at(0));
    REQUIRE_NOTHROW(v.at(2));
    REQUIRE_NOTHROW(v.at(4));
    REQUIRE_THROWS_AS(v.at(5), std::out_of_range);
}
<|endoftext|>


<commit_before>/*

Copyright (c) 2003, Arvid Norberg
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:

    * Redistributions of source code must retain the above copyright
      notice, this list of conditions and the following disclaimer.
    * 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.
    * Neither the name of the author nor the names of its
      contributors may be used to endorse or promote products derived
      from this software without specific prior written permission.

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 OWNER 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.

*/

#ifndef TORRENT_TORRENT_HANDLE_HPP_INCLUDED
#define TORRENT_TORRENT_HANDLE_HPP_INCLUDED

#include <vector>

#ifdef _MSC_VER
#pragma warning(push, 1)
#endif

#include <boost/date_time/posix_time/posix_time.hpp>

#ifdef _MSC_VER
#pragma warning(pop)
#endif

#include "libtorrent/peer_id.hpp"
#include "libtorrent/peer_info.hpp"
#include "libtorrent/piece_picker.hpp"
#include "libtorrent/torrent_info.hpp"
#include "libtorrent/config.hpp"

namespace libtorrent
{
	namespace aux
	{
		struct session_impl;
		struct checker_impl;
	}

	struct TORRENT_EXPORT duplicate_torrent: std::exception
	{
		virtual const char* what() const throw()
		{ return "torrent already exists in session"; }
	};

	struct TORRENT_EXPORT invalid_handle: std::exception
	{
		virtual const char* what() const throw()
		{ return "invalid torrent handle used"; }
	};

	struct TORRENT_EXPORT torrent_status
	{
		torrent_status()
			: state(queued_for_checking)
			, paused(false)
			, progress(0.f)
			, total_download(0)
			, total_upload(0)
			, total_payload_download(0)
			, total_payload_upload(0)
			, total_failed_bytes(0)
			, total_redundant_bytes(0)
			, download_rate(0)
			, upload_rate(0)
			, download_payload_rate(0)
			, upload_payload_rate(0)
			, num_peers(0)
			, num_complete(-1)
			, num_incomplete(-1)
			, pieces(0)
			, num_pieces(0)
			, total_done(0)
			, total_wanted_done(0)
			, total_wanted(0)
			, num_seeds(0)
			, distributed_copies(0.f)
			, block_size(0)
		{}

		enum state_t
		{
			queued_for_checking,
			checking_files,
			connecting_to_tracker,
			downloading_metadata,
			downloading,
			finished,
			seeding,
			allocating
		};

		state_t state;
		bool paused;
		float progress;
		boost::posix_time::time_duration next_announce;
		boost::posix_time::time_duration announce_interval;

		std::string current_tracker;

		// transferred this session!
		// total, payload plus protocol
		size_type total_download;
		size_type total_upload;

		// payload only
		size_type total_payload_download;
		size_type total_payload_upload;

		// the amount of payload bytes that
		// has failed their hash test
		size_type total_failed_bytes;

		// the number of payload bytes that
		// has been received redundantly.
		size_type total_redundant_bytes;

		// current transfer rate
		// payload plus protocol
		float download_rate;
		float upload_rate;

		// the rate of payload that is
		// sent and received
		float download_payload_rate;
		float upload_payload_rate;

		// the number of peers this torrent
		// is connected to.
		int num_peers;

		// if the tracker sends scrape info in its
		// announce reply, these fields will be
		// set to the total number of peers that
		// have the whole file and the total number
		// of peers that are still downloading
		int num_complete;
		int num_incomplete;

		const std::vector<bool>* pieces;

		// this is the number of pieces the client has
		// downloaded. it is equal to:
		// std::accumulate(pieces->begin(), pieces->end());
		int num_pieces;

		// the number of bytes of the file we have
		// including pieces that may have been filtered
		// after we downloaded them
		size_type total_done;

		// the number of bytes we have of those that we
		// want. i.e. not counting bytes from pieces that
		// are filtered as not wanted.
		size_type total_wanted_done;

		// the total number of bytes we want to download
		// this may be smaller than the total torrent size
		// in case any pieces are filtered as not wanted
		size_type total_wanted;

		// the number of peers this torrent is connected to
		// that are seeding.
		int num_seeds;

		// the number of distributed copies of the file.
		// note that one copy may be spread out among many peers.
		//
		// the whole number part tells how many copies
		//   there are of the rarest piece(s)
		//
		// the fractional part tells the fraction of pieces that
		//   have more copies than the rarest piece(s).
		float distributed_copies;

		// the block size that is used in this torrent. i.e.
		// the number of bytes each piece request asks for
		// and each bit in the download queue bitfield represents
		int block_size;
	};

	struct TORRENT_EXPORT partial_piece_info
	{
		enum { max_blocks_per_piece = piece_picker::max_blocks_per_piece };
		int piece_index;
		int blocks_in_piece;
		std::bitset<max_blocks_per_piece> requested_blocks;
		std::bitset<max_blocks_per_piece> finished_blocks;
		tcp::endpoint peer[max_blocks_per_piece];
		int num_downloads[max_blocks_per_piece];
	};

	struct TORRENT_EXPORT torrent_handle
	{
		friend class invariant_access;
		friend class aux::session_impl;
		friend class torrent;

		torrent_handle(): m_ses(0), m_chk(0) {}

		void get_peer_info(std::vector<peer_info>& v) const;
		bool send_chat_message(tcp::endpoint ip, std::string message) const;
		torrent_status status() const;
		void get_download_queue(std::vector<partial_piece_info>& queue) const;

		// fills the specified vector with the download progress [0, 1]
		// of each file in the torrent. The files are ordered as in
		// the torrent_info.
		void file_progress(std::vector<float>& progress);

		std::vector<announce_entry> const& trackers() const;
		void replace_trackers(std::vector<announce_entry> const&) const;

		void add_url_seed(std::string const& url);

		bool has_metadata() const;
		const torrent_info& get_torrent_info() const;
		bool is_valid() const;

		bool is_seed() const;
		bool is_paused() const;
		void pause() const;
		void resume() const;

		// marks the piece with the given index as filtered
		// it will not be downloaded
		void filter_piece(int index, bool filter) const;
		void filter_pieces(std::vector<bool> const& pieces) const;
		bool is_piece_filtered(int index) const;
		std::vector<bool> filtered_pieces() const;

		// marks the file with the given index as filtered
		// it will not be downloaded
		void filter_files(std::vector<bool> const& files) const;

		// set the interface to bind outgoing connections
		// to.
		void use_interface(const char* net_interface) const;

		entry write_resume_data() const;

		// kind of similar to get_torrent_info() but this
		// is lower level, returning the exact info-part of
		// the .torrent file. When hashed, this buffer
		// will produce the info hash. The reference is valid
		// only as long as the torrent is running.
		std::vector<char> const& metadata() const;

		// forces this torrent to reannounce
		// (make a rerequest from the tracker)
		void force_reannounce() const;

		// forces a reannounce in the specified amount of time.
		// This overrides the default announce interval, and no
		// announce will take place until the given time has
		// timed out.
		void force_reannounce(boost::posix_time::time_duration) const;

		// TODO: add a feature where the user can tell the torrent
		// to finish all pieces currently in the pipeline, and then
		// abort the torrent.

		void set_upload_limit(int limit) const;
		void set_download_limit(int limit) const;
		void set_sequenced_download_threshold(int threshold) const;

		void set_peer_upload_limit(tcp::endpoint ip, int limit) const;
		void set_peer_download_limit(tcp::endpoint ip, int limit) const;

		// manually connect a peer
		void connect_peer(tcp::endpoint const& adr) const;

		// valid ratios are 0 (infinite ratio) or [ 1.0 , inf )
		// the ratio is uploaded / downloaded. less than 1 is not allowed
		void set_ratio(float up_down_ratio) const;

		boost::filesystem::path save_path() const;

		// -1 means unlimited unchokes
		void set_max_uploads(int max_uploads) const;

		// -1 means unlimited connections
		void set_max_connections(int max_connections) const;

		void set_tracker_login(std::string const& name
			, std::string const& password) const;

		// post condition: save_path() == save_path if true is returned
		bool move_storage(boost::filesystem::path const& save_path) const;

		const sha1_hash& info_hash() const
		{ return m_info_hash; }

		bool operator==(const torrent_handle& h) const
		{ return m_info_hash == h.m_info_hash; }

		bool operator!=(const torrent_handle& h) const
		{ return m_info_hash != h.m_info_hash; }

		bool operator<(const torrent_handle& h) const
		{ return m_info_hash < h.m_info_hash; }

	private:

		torrent_handle(aux::session_impl* s,
			aux::checker_impl* c,
			const sha1_hash& h)
			: m_ses(s)
			, m_chk(c)
			, m_info_hash(h)
		{
			assert(m_ses != 0);
		}

#ifndef NDEBUG
		void check_invariant() const;
#endif

		aux::session_impl* m_ses;
		aux::checker_impl* m_chk;
		sha1_hash m_info_hash;

	};

}

#endif // TORRENT_TORRENT_HANDLE_HPP_INCLUDED
<commit_msg>fixed warning on msvc8<commit_after>/*

Copyright (c) 2003, Arvid Norberg
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:

    * Redistributions of source code must retain the above copyright
      notice, this list of conditions and the following disclaimer.
    * 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.
    * Neither the name of the author nor the names of its
      contributors may be used to endorse or promote products derived
      from this software without specific prior written permission.

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 OWNER 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.

*/

#ifndef TORRENT_TORRENT_HANDLE_HPP_INCLUDED
#define TORRENT_TORRENT_HANDLE_HPP_INCLUDED

#include <vector>

#ifdef _MSC_VER
#pragma warning(push, 1)
#endif

#include <boost/date_time/posix_time/posix_time.hpp>

#ifdef _MSC_VER
#pragma warning(pop)
#endif

#include "libtorrent/peer_id.hpp"
#include "libtorrent/peer_info.hpp"
#include "libtorrent/piece_picker.hpp"
#include "libtorrent/torrent_info.hpp"
#include "libtorrent/config.hpp"

namespace libtorrent
{
	namespace aux
	{
		struct session_impl;
		struct checker_impl;
	}

	struct TORRENT_EXPORT duplicate_torrent: std::exception
	{
		virtual const char* what() const throw()
		{ return "torrent already exists in session"; }
	};

	struct TORRENT_EXPORT invalid_handle: std::exception
	{
		virtual const char* what() const throw()
		{ return "invalid torrent handle used"; }
	};

	struct TORRENT_EXPORT torrent_status
	{
		torrent_status()
			: state(queued_for_checking)
			, paused(false)
			, progress(0.f)
			, total_download(0)
			, total_upload(0)
			, total_payload_download(0)
			, total_payload_upload(0)
			, total_failed_bytes(0)
			, total_redundant_bytes(0)
			, download_rate(0)
			, upload_rate(0)
			, download_payload_rate(0)
			, upload_payload_rate(0)
			, num_peers(0)
			, num_complete(-1)
			, num_incomplete(-1)
			, pieces(0)
			, num_pieces(0)
			, total_done(0)
			, total_wanted_done(0)
			, total_wanted(0)
			, num_seeds(0)
			, distributed_copies(0.f)
			, block_size(0)
		{}

		enum state_t
		{
			queued_for_checking,
			checking_files,
			connecting_to_tracker,
			downloading_metadata,
			downloading,
			finished,
			seeding,
			allocating
		};

		state_t state;
		bool paused;
		float progress;
		boost::posix_time::time_duration next_announce;
		boost::posix_time::time_duration announce_interval;

		std::string current_tracker;

		// transferred this session!
		// total, payload plus protocol
		size_type total_download;
		size_type total_upload;

		// payload only
		size_type total_payload_download;
		size_type total_payload_upload;

		// the amount of payload bytes that
		// has failed their hash test
		size_type total_failed_bytes;

		// the number of payload bytes that
		// has been received redundantly.
		size_type total_redundant_bytes;

		// current transfer rate
		// payload plus protocol
		float download_rate;
		float upload_rate;

		// the rate of payload that is
		// sent and received
		float download_payload_rate;
		float upload_payload_rate;

		// the number of peers this torrent
		// is connected to.
		int num_peers;

		// if the tracker sends scrape info in its
		// announce reply, these fields will be
		// set to the total number of peers that
		// have the whole file and the total number
		// of peers that are still downloading
		int num_complete;
		int num_incomplete;

		const std::vector<bool>* pieces;

		// this is the number of pieces the client has
		// downloaded. it is equal to:
		// std::accumulate(pieces->begin(), pieces->end());
		int num_pieces;

		// the number of bytes of the file we have
		// including pieces that may have been filtered
		// after we downloaded them
		size_type total_done;

		// the number of bytes we have of those that we
		// want. i.e. not counting bytes from pieces that
		// are filtered as not wanted.
		size_type total_wanted_done;

		// the total number of bytes we want to download
		// this may be smaller than the total torrent size
		// in case any pieces are filtered as not wanted
		size_type total_wanted;

		// the number of peers this torrent is connected to
		// that are seeding.
		int num_seeds;

		// the number of distributed copies of the file.
		// note that one copy may be spread out among many peers.
		//
		// the whole number part tells how many copies
		//   there are of the rarest piece(s)
		//
		// the fractional part tells the fraction of pieces that
		//   have more copies than the rarest piece(s).
		float distributed_copies;

		// the block size that is used in this torrent. i.e.
		// the number of bytes each piece request asks for
		// and each bit in the download queue bitfield represents
		int block_size;
	};

	struct TORRENT_EXPORT partial_piece_info
	{
		enum { max_blocks_per_piece = piece_picker::max_blocks_per_piece };
		int piece_index;
		int blocks_in_piece;
		std::bitset<max_blocks_per_piece> requested_blocks;
		std::bitset<max_blocks_per_piece> finished_blocks;
		tcp::endpoint peer[max_blocks_per_piece];
		int num_downloads[max_blocks_per_piece];
	};

	struct TORRENT_EXPORT torrent_handle
	{
		friend class invariant_access;
		friend struct aux::session_impl;
		friend class torrent;

		torrent_handle(): m_ses(0), m_chk(0) {}

		void get_peer_info(std::vector<peer_info>& v) const;
		bool send_chat_message(tcp::endpoint ip, std::string message) const;
		torrent_status status() const;
		void get_download_queue(std::vector<partial_piece_info>& queue) const;

		// fills the specified vector with the download progress [0, 1]
		// of each file in the torrent. The files are ordered as in
		// the torrent_info.
		void file_progress(std::vector<float>& progress);

		std::vector<announce_entry> const& trackers() const;
		void replace_trackers(std::vector<announce_entry> const&) const;

		void add_url_seed(std::string const& url);

		bool has_metadata() const;
		const torrent_info& get_torrent_info() const;
		bool is_valid() const;

		bool is_seed() const;
		bool is_paused() const;
		void pause() const;
		void resume() const;

		// marks the piece with the given index as filtered
		// it will not be downloaded
		void filter_piece(int index, bool filter) const;
		void filter_pieces(std::vector<bool> const& pieces) const;
		bool is_piece_filtered(int index) const;
		std::vector<bool> filtered_pieces() const;

		// marks the file with the given index as filtered
		// it will not be downloaded
		void filter_files(std::vector<bool> const& files) const;

		// set the interface to bind outgoing connections
		// to.
		void use_interface(const char* net_interface) const;

		entry write_resume_data() const;

		// kind of similar to get_torrent_info() but this
		// is lower level, returning the exact info-part of
		// the .torrent file. When hashed, this buffer
		// will produce the info hash. The reference is valid
		// only as long as the torrent is running.
		std::vector<char> const& metadata() const;

		// forces this torrent to reannounce
		// (make a rerequest from the tracker)
		void force_reannounce() const;

		// forces a reannounce in the specified amount of time.
		// This overrides the default announce interval, and no
		// announce will take place until the given time has
		// timed out.
		void force_reannounce(boost::posix_time::time_duration) const;

		// TODO: add a feature where the user can tell the torrent
		// to finish all pieces currently in the pipeline, and then
		// abort the torrent.

		void set_upload_limit(int limit) const;
		void set_download_limit(int limit) const;
		void set_sequenced_download_threshold(int threshold) const;

		void set_peer_upload_limit(tcp::endpoint ip, int limit) const;
		void set_peer_download_limit(tcp::endpoint ip, int limit) const;

		// manually connect a peer
		void connect_peer(tcp::endpoint const& adr) const;

		// valid ratios are 0 (infinite ratio) or [ 1.0 , inf )
		// the ratio is uploaded / downloaded. less than 1 is not allowed
		void set_ratio(float up_down_ratio) const;

		boost::filesystem::path save_path() const;

		// -1 means unlimited unchokes
		void set_max_uploads(int max_uploads) const;

		// -1 means unlimited connections
		void set_max_connections(int max_connections) const;

		void set_tracker_login(std::string const& name
			, std::string const& password) const;

		// post condition: save_path() == save_path if true is returned
		bool move_storage(boost::filesystem::path const& save_path) const;

		const sha1_hash& info_hash() const
		{ return m_info_hash; }

		bool operator==(const torrent_handle& h) const
		{ return m_info_hash == h.m_info_hash; }

		bool operator!=(const torrent_handle& h) const
		{ return m_info_hash != h.m_info_hash; }

		bool operator<(const torrent_handle& h) const
		{ return m_info_hash < h.m_info_hash; }

	private:

		torrent_handle(aux::session_impl* s,
			aux::checker_impl* c,
			const sha1_hash& h)
			: m_ses(s)
			, m_chk(c)
			, m_info_hash(h)
		{
			assert(m_ses != 0);
		}

#ifndef NDEBUG
		void check_invariant() const;
#endif

		aux::session_impl* m_ses;
		aux::checker_impl* m_chk;
		sha1_hash m_info_hash;

	};

}

#endif // TORRENT_TORRENT_HANDLE_HPP_INCLUDED
<|endoftext|>


<commit_before>#include "gtest/gtest.h"
#include "braincloud/BrainCloudClient.h"
#include "TestResult.h"
#include "TestBCGlobalFile.h"
#include <vector>
using namespace BrainCloud;

static const std::string testfileName = "png1.png";
static const std::string testFileId = "34802251-0da0-419e-91b5-59d91790af15";
static const std::string testFolderPath = "/existingfolder/";

TEST_F(TestBCGlobalFile, GetFileInfo)
{
	TestResult tr;
	m_bc->getGlobalFileService()->getFileInfo(testFileId, &tr);
	tr.run(m_bc);
}

TEST_F(TestBCGlobalFile, GetFileInfoSimple)
{
	TestResult tr;
	m_bc->getGlobalFileService()->getFileInfoSimple(testFolderPath, testfileName, &tr);
	tr.run(m_bc);
}

TEST_F(TestBCGlobalFile, GetGlobalCDNUrl)
{
	TestResult tr;
	m_bc->getGlobalFileService()->getGlobalCDNUrl(testFileId, &tr);
	tr.run(m_bc);
}

TEST_F(TestBCGlobalFile, GetGlobalFileList)
{
	TestResult tr;
	m_bc->getGlobalFileService()->getGlobalFileList(testFolderPath, true, &tr);
	tr.run(m_bc);
}
<commit_msg>Revert updating file/folder parameter for Global File unit tests<commit_after>#include "gtest/gtest.h"
#include "braincloud/BrainCloudClient.h"
#include "TestResult.h"
#include "TestBCGlobalFile.h"
#include <vector>
using namespace BrainCloud;

static const std::string testfileName = "testGlobalFile.png";
static const std::string testFileId = "ed2d2924-4650-4a88-b095-94b75ce9aa18";
static const std::string testFolderPath = "/fname/";

TEST_F(TestBCGlobalFile, GetFileInfo)
{
	TestResult tr;
	m_bc->getGlobalFileService()->getFileInfo(testFileId, &tr);
	tr.run(m_bc);
}

TEST_F(TestBCGlobalFile, GetFileInfoSimple)
{
	TestResult tr;
	m_bc->getGlobalFileService()->getFileInfoSimple(testFolderPath, testfileName, &tr);
	tr.run(m_bc);
}

TEST_F(TestBCGlobalFile, GetGlobalCDNUrl)
{
	TestResult tr;
	m_bc->getGlobalFileService()->getGlobalCDNUrl(testFileId, &tr);
	tr.run(m_bc);
}

TEST_F(TestBCGlobalFile, GetGlobalFileList)
{
	TestResult tr;
	m_bc->getGlobalFileService()->getGlobalFileList(testFolderPath, true, &tr);
	tr.run(m_bc);
}
<|endoftext|>


<commit_before>#include <stdlib.h>
#include <unistd.h>

#define XLOG_MIN_LOG_LVL 3

#include "tools.h"

using namespace TOOLS;
using namespace TOOLS::UNIT_TEST;
using namespace TOOLS::IO;
using namespace std;

#define LOGID "system"

#ifndef GENERATE_HEADER
void loglvl_action() { INFO << "Loglvl action successful!"; }
#endif

START_SUITE(XLoggerToolsTestSuite) {
  REG_TEST(simple)
  REG_TEST(loglvl_action)
  REG_TEST(endline)
  REG_TEST(persistent_file)
  REG_TEST(buffered_file)
}

XLogger* xlog;
MemoryBackend* mb;
std::string log_fn;

virtual void setup() {
  xlog = new XLogger(LOGID);
  // xlog->add_backend(new ConsoleBackend(LOGID));
  mb = new MemoryBackend(LOGID);
  xlog->set_min_loglvl(1);
  xlog->add_backend(mb);

  log_fn = "/tmp/logger_test_file.log";
}

virtual void tear_down() {
  unlink(log_fn.c_str());
  if (xlog != nullptr)
    delete xlog;
}

MAKE_TEST(simple) {
  CHECK(mb->log_msgs.size() == 1);
  int tmp = 123;
  DEBUG << "starting logger testing!";
  INFO << "more info logging, let's log an integer: " << tmp;
  WARN << "oh oh, this is a warning - this could end bad!";

  CHECK(mb->log_msgs.size() == 4);
}

MAKE_TEST(loglvl_action) {
  INFO << "STARTING";
  xlog->set_loglvl_action(10, loglvl_action);
  ERROR << "ERRRRORRRRR, should trigger action";
  CHECK(mb->log_msgs.size() == 4);
}

MAKE_TEST(endline) {
  INFO << "yiha - compiles -> ship it!" << std::endl;
  CHECK(true);
}

MAKE_TEST(persistent_file) {
  PersistentFileBackend* pfb = \
    new PersistentFileBackend(LOGID, log_fn);

  xlog->add_backend(pfb);

  // before logging, only header is there
  CHECK(mb->log_msgs.size() == 1);

  INFO << "writing some fancy log";
  ERROR << "some more, but now an error!";

  // after logging one additional line per log-msg
  CHECK(mb->log_msgs.size() == 3);

  delete xlog;
  xlog = nullptr;

  // destroying the logger adds another bye-bye-message to the log
  CHECK(mb->log_msgs.size() == 4);

  XString out = TOOLS::IO::readfile(log_fn);

  CHECK(out.length() > 0);
  CHECK(out.find("writing some fancy log") != string::npos);
  CHECK(out.find("some more, but now an error") != string::npos);
  CHECK(out.split("\n").size() == 5);
}

MAKE_TEST(buffered_file) {
  const int bufsize = 500;
  BufferedFileBackend* bfb = new BufferedFileBackend(LOGID, log_fn, bufsize);

  xlog->add_backend(bfb);

  // mem-logging should be unaffected!
  CHECK(mb->log_msgs.size() == 1);

  // write 2 entries, should not be written to file yet!
  XString log_out = "1234567890";
  for(size_t i=0; i<2; ++i)
    INFO << log_out;

  XString out = TOOLS::IO::readfile(log_fn);

  // still empty
  CHECK(out.length() == 0);

  // now adding a lot, in the meantime the file should have been flushed
  for(size_t i=0; i<20; ++i)
    INFO << log_out;

  // give it the FS some time to actually flush and write!
  sleep(1);

  out = TOOLS::IO::readfile(log_fn);

  // should contain at least the last full buffer, aka 'bufsize' chars
  size_t oldlen = out.length();
  CHECK(out.length() >= bufsize);

  delete xlog;
  xlog = nullptr;

  // after destruction the logdata should fully be written...
  out = TOOLS::IO::readfile(log_fn);
  CHECK(out.length() > oldlen);
}

END_SUITE()
<commit_msg>fix redefiniton warning in tests<commit_after>#include <stdlib.h>
#include <unistd.h>

#ifndef XLOG_MIN_LOG_LVL
#define XLOG_MIN_LOG_LVL 3
#endif

#include "tools.h"

using namespace TOOLS;
using namespace TOOLS::UNIT_TEST;
using namespace TOOLS::IO;
using namespace std;

#define LOGID "system"

#ifndef GENERATE_HEADER
void loglvl_action() { INFO << "Loglvl action successful!"; }
#endif

START_SUITE(XLoggerToolsTestSuite) {
  REG_TEST(simple)
  REG_TEST(loglvl_action)
  REG_TEST(endline)
  REG_TEST(persistent_file)
  REG_TEST(buffered_file)
}

XLogger* xlog;
MemoryBackend* mb;
std::string log_fn;

virtual void setup() {
  xlog = new XLogger(LOGID);
  // xlog->add_backend(new ConsoleBackend(LOGID));
  mb = new MemoryBackend(LOGID);
  xlog->set_min_loglvl(1);
  xlog->add_backend(mb);

  log_fn = "/tmp/logger_test_file.log";
}

virtual void tear_down() {
  unlink(log_fn.c_str());
  if (xlog != nullptr)
    delete xlog;
}

MAKE_TEST(simple) {
  CHECK(mb->log_msgs.size() == 1);
  int tmp = 123;
  DEBUG << "starting logger testing!";
  INFO << "more info logging, let's log an integer: " << tmp;
  WARN << "oh oh, this is a warning - this could end bad!";

  CHECK(mb->log_msgs.size() == 4);
}

MAKE_TEST(loglvl_action) {
  INFO << "STARTING";
  xlog->set_loglvl_action(10, loglvl_action);
  ERROR << "ERRRRORRRRR, should trigger action";
  CHECK(mb->log_msgs.size() == 4);
}

MAKE_TEST(endline) {
  INFO << "yiha - compiles -> ship it!" << std::endl;
  CHECK(true);
}

MAKE_TEST(persistent_file) {
  PersistentFileBackend* pfb = \
    new PersistentFileBackend(LOGID, log_fn);

  xlog->add_backend(pfb);

  // before logging, only header is there
  CHECK(mb->log_msgs.size() == 1);

  INFO << "writing some fancy log";
  ERROR << "some more, but now an error!";

  // after logging one additional line per log-msg
  CHECK(mb->log_msgs.size() == 3);

  delete xlog;
  xlog = nullptr;

  // destroying the logger adds another bye-bye-message to the log
  CHECK(mb->log_msgs.size() == 4);

  XString out = TOOLS::IO::readfile(log_fn);

  CHECK(out.length() > 0);
  CHECK(out.find("writing some fancy log") != string::npos);
  CHECK(out.find("some more, but now an error") != string::npos);
  CHECK(out.split("\n").size() == 5);
}

MAKE_TEST(buffered_file) {
  const int bufsize = 500;
  BufferedFileBackend* bfb = new BufferedFileBackend(LOGID, log_fn, bufsize);

  xlog->add_backend(bfb);

  // mem-logging should be unaffected!
  CHECK(mb->log_msgs.size() == 1);

  // write 2 entries, should not be written to file yet!
  XString log_out = "1234567890";
  for(size_t i=0; i<2; ++i)
    INFO << log_out;

  XString out = TOOLS::IO::readfile(log_fn);

  // still empty
  CHECK(out.length() == 0);

  // now adding a lot, in the meantime the file should have been flushed
  for(size_t i=0; i<20; ++i)
    INFO << log_out;

  // give it the FS some time to actually flush and write!
  sleep(1);

  out = TOOLS::IO::readfile(log_fn);

  // should contain at least the last full buffer, aka 'bufsize' chars
  size_t oldlen = out.length();
  CHECK(out.length() >= bufsize);

  delete xlog;
  xlog = nullptr;

  // after destruction the logdata should fully be written...
  out = TOOLS::IO::readfile(log_fn);
  CHECK(out.length() > oldlen);
}

END_SUITE()
<|endoftext|>


<commit_before>/* Copyright (c) 2008-2019 the MRtrix3 contributors.
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 *
 * Covered Software is provided under this License on an "as is"
 * basis, without warranty of any kind, either expressed, implied, or
 * statutory, including, without limitation, warranties that the
 * Covered Software is free of defects, merchantable, fit for a
 * particular purpose or non-infringing.
 * See the Mozilla Public License v. 2.0 for more details.
 *
 * For more details, see http://www.mrtrix.org/.
 */

#include <Eigen/Geometry>
#include <unsupported/Eigen/MatrixFunctions>
#include <algorithm>
#include "command.h"
#include "math/math.h"
#include "math/average_space.h"
#include "image.h"
#include "file/nifti_utils.h"
#include "transform.h"
#include "file/key_value.h"

using namespace MR;
using namespace App;

const char* operations[] = {
  "invert",
  "half",
  "rigid",
  "header",
  "average",
  "interpolate",
  "decompose",
  "align_vertices_rigid",
  NULL
};

void usage ()
{
  AUTHOR = "Max Pietsch (maximilian.pietsch@kcl.ac.uk)";

  SYNOPSIS = "Perform calculations on linear transformation matrices";

  ARGUMENTS
  + Argument ("inputs", "the input(s) for the specified operation").allow_multiple()
  + Argument ("operation", "the operation to perform, one of: " + join(operations, ", ") + " (see description section for details).").type_choice (operations)
  + Argument ("output", "the output transformation matrix.").type_file_out ();

  EXAMPLES
  + Example ("Invert a transformation",
             "transformcalc matrix_in.txt invert matrix_out.txt",
             "")

  + Example ("Calculate the matrix square root of the input transformation (halfway transformation)",
             "transformcalc matrix_in.txt half matrix_out.txt",
             "")

  + Example ("Calculate the rigid component of an affine input transformation",
             "transformcalc affine_in.txt rigid rigid_out.txt",
             "")

  + Example ("Calculate the transformation matrix from an original image and an image with modified header",
             "transformcalc mov mapmovhdr header output",
             "")

  + Example ("Calculate the average affine matrix of a set of input matrices",
             "transformcalc input1.txt ... inputN.txt average matrix_out.txt",
             "")

  + Example ("Create interpolated transformation matrix between two inputs",
             "transformcalc input1.txt input2.txt interpolate matrix_out.txt",
             "Based on matrix decomposition with linear interpolation of "
             "translation, rotation and stretch described in: "
             "Shoemake, K., Hill, M., & Duff, T. (1992). Matrix Animation and Polar Decomposition. "
             "Matrix, 92, 258-264. doi:10.1.1.56.1336")

  + Example ("Decompose transformation matrix M into translation, rotation and stretch and shear (M = T * R * S)",
             "transformcalc matrix_in.txt decompose matrixes_out.txt",
             "The output is a key-value text file containing: "
             "scaling: vector of 3 scaling factors in x, y, z direction; "
             "shear: list of shear factors for xy, xz, yz axes; "
             "angles: list of Euler angles about static x, y, z axes in radians in the range [0:pi]x[-pi:pi]x[-pi:pi]; "
             "angle_axis: angle in radians and rotation axis; "
             "translation : translation vector along x, y, z axes in mm; "
             "R: composed roation matrix (R = rot_x * rot_y * rot_z); "
             "S: composed scaling and shear matrix")

  + Example ("Align two sets of landmarks using a rigid transformation",
             "transformcalc input moving.txt fixed.txt align_vertices_rigid output",
             "Vertex coordinates are in scanner space, corresponding vertices must be stored in the same row "
             "of moving.txt and fixed.txt. Requires 3 or more vertices in each file. "
             "Algorithm: Kabsch 'A solution for the best rotation to relate two sets of vectors' DOI:10.1107/S0567739476001873");

}

template <typename T> int sgn(T val) {
    return (T(0) < val) - (val < T(0));
}

transform_type align_corresponding_vertices (const Eigen::MatrixXd &target_vertices, const Eigen::MatrixXd &moving_vertices, bool scale) {
  //  this function aligns two sets of vertices which must have corresponding vertices stored in the same row
  //
  //  scale == false --> Kabsch
  //  minimise (target_vertices.row(i) - M * moving_vertices.row(i) + t).squaredNorm();
  //
  //  scale == true --> Umeyama
  //  nonrigid version of Kabsch algorithm that also includes scale (not shear)
  //
  assert(target_vertices.rows() == moving_vertices.rows());
  const size_t n = moving_vertices.rows();
  assert (n > 2);

  assert(target_vertices.cols() == moving_vertices.cols());
  assert(target_vertices.cols() == 3 && "align_corresponding_vertices implemented only for 3D data");

  Eigen::VectorXd moving_centre = moving_vertices.colwise().mean();
  Eigen::VectorXd target_centre = target_vertices.colwise().mean();
  Eigen::MatrixXd moving_centered = moving_vertices.rowwise() - moving_centre.transpose();
  Eigen::MatrixXd target_centered = target_vertices.rowwise() - target_centre.transpose();
  Eigen::MatrixXd cov = (target_centered.adjoint() * moving_centered) / default_type (n - 1);

  Eigen::JacobiSVD<Eigen::Matrix3d> svd (cov, Eigen::ComputeFullU | Eigen::ComputeFullV);

  // rotation matrix
  Eigen::Matrix3d R = svd.matrixV() * svd.matrixU().transpose();

  // calculate determinant of V*U^T to disambiguate rotation sign
  default_type f_det = R.determinant();
  Eigen::Vector3d e(1, 1, (f_det < 0)? -1 : 1);

  // recompute the rotation if the determinant was negative
  if (f_det < 0)
    R.noalias() = svd.matrixV() * e.asDiagonal() * svd.matrixU().transpose();

  // renormalize the rotation, R needs to be Matrix3d
  R = Eigen::Quaterniond(R).normalized().toRotationMatrix();

  if (scale) {
    default_type fsq = 0;
    for (size_t i = 0; i < n; ++ i)
      fsq += moving_centered.row(i).squaredNorm();
    // calculate and apply the scale
    default_type fscale = svd.singularValues().dot(e) / fsq;
    R *= fscale;
  }

  transform_type T;
  T.linear() = R;
  T.translation() = target_centre - (R * moving_centre);
  return T;
}

void run ()
{
  const size_t num_inputs = argument.size() - 2;
  const int op = argument[num_inputs];
  const std::string& output_path = argument.back();

  switch (op) {
    case 0: { // invert
      if (num_inputs != 1)
        throw Exception ("invert requires 1 input");
      transform_type input = load_transform (argument[0]);
      save_transform (input.inverse(), output_path);
      break;
    }
    case 1: { // half
      if (num_inputs != 1)
        throw Exception ("half requires 1 input");
      Eigen::Transform<default_type, 3, Eigen::Projective> input = load_transform (argument[0]);
      transform_type output;
      Eigen::Matrix<default_type, 4, 4> half = input.matrix().sqrt();
      output.matrix() = half.topLeftCorner(3,4);
      save_transform (output, output_path);
      break;
    }
    case 2: { // rigid
      if (num_inputs != 1)
        throw Exception ("rigid requires 1 input");
      transform_type input = load_transform (argument[0]);
      transform_type output (input);
      output.linear() = input.rotation();
      save_transform (output, output_path);
      break;
    }
    case 3: { // header
      if (num_inputs != 2)
        throw Exception ("header requires 2 inputs");
      auto orig_header = Header::open (argument[0]);
      auto modified_header = Header::open (argument[1]);

      transform_type forward_transform = Transform(modified_header).voxel2scanner * Transform(orig_header).voxel2scanner.inverse();
      save_transform (forward_transform.inverse(), output_path);
      break;
    }
    case 4: { // average
      if (num_inputs < 2)
        throw Exception ("average requires at least 2 inputs");
      transform_type transform_out;
      Eigen::Transform<default_type, 3, Eigen::Projective> Tin;
      Eigen::MatrixXd Min;
      vector<Eigen::MatrixXd> matrices;
      for (size_t i = 0; i < num_inputs; i++) {
        DEBUG(str(argument[i]));
        Tin = load_transform (argument[i]);
        matrices.push_back(Tin.matrix());
      }

      Eigen::MatrixXd average_matrix;
      Math::matrix_average ( matrices, average_matrix);
      transform_out.matrix() = average_matrix.topLeftCorner(3,4);
      save_transform (transform_out, output_path);
      break;
    }
    case 5: { // interpolate
      if (num_inputs != 3)
        throw Exception ("interpolation requires 3 inputs");
      transform_type transform1 = load_transform (argument[0]);
      transform_type transform2 = load_transform (argument[1]);
      default_type t = parse_floats(argument[2])[0];

      transform_type transform_out;

      if (t < 0.0 || t > 1.0)
        throw Exception ("t has to be in the interval [0,1]");

      Eigen::MatrixXd M1 = transform1.linear();
      Eigen::MatrixXd M2 = transform2.linear();
      if (sgn(M1.determinant()) != sgn(M2.determinant()))
        WARN("transformation determinants have different signs");

      Eigen::Matrix3d R1 = transform1.rotation();
      Eigen::Matrix3d R2 = transform2.rotation();
      Eigen::Quaterniond Q1(R1);
      Eigen::Quaterniond Q2(R2);
      Eigen::Quaterniond Qout;

      // stretch (shear becomes roation and stretch)
      Eigen::MatrixXd S1 = R1.transpose() * M1;
      Eigen::MatrixXd S2 = R2.transpose() * M2;
      if (!M1.isApprox(R1*S1))
        WARN ("M1 matrix decomposition might have failed");
      if (!M2.isApprox(R2*S2))
        WARN ("M2 matrix decomposition might have failed");

      transform_out.translation() = ((1.0 - t) * transform1.translation() + t * transform2.translation());
      Qout = Q1.slerp(t, Q2);
      transform_out.linear() = Qout * ((1 - t) * S1 + t * S2);
      INFO("\n"+str(transform_out.matrix().format(
        Eigen::IOFormat(Eigen::FullPrecision, 0, ", ", ",\n", "[", "]", "[", "]"))));
      save_transform (transform_out, output_path);
      break;
    }
    case 6: { // decompose
      if (num_inputs != 1)
        throw Exception ("decomposition requires 1 input");
      transform_type transform = load_transform (argument[0]);

      Eigen::MatrixXd M = transform.linear();
      Eigen::Matrix3d R = transform.rotation();
      Eigen::MatrixXd S = R.transpose() * M;
      if (!M.isApprox(R*S))
        WARN ("matrix decomposition might have failed");

      Eigen::Vector3d euler_angles = R.eulerAngles(0, 1, 2);
      assert (R.isApprox((Eigen::AngleAxisd(euler_angles[0], Eigen::Vector3d::UnitX())
              * Eigen::AngleAxisd(euler_angles[1], Eigen::Vector3d::UnitY())
              * Eigen::AngleAxisd(euler_angles[2], Eigen::Vector3d::UnitZ())).matrix()));

      Eigen::RowVector4d angle_axis;
      {
        auto AA = Eigen::AngleAxis<default_type> (R);
        angle_axis(0) = AA.angle();
        angle_axis.block<1,3>(0,1) = AA.axis();
      }

      File::OFStream out (output_path);
      Eigen::IOFormat fmt(Eigen::FullPrecision, Eigen::DontAlignCols, " ", "\n", "", "", "", "\n");
      out << "scaling: "     << Eigen::RowVector3d(S(0,0), S(1,1), S(2,2)).format(fmt);
      out << "shear: "       << Eigen::RowVector3d(S(0,1), S(0,2), S(1,2)).format(fmt);
      out << "angles: "      << euler_angles.transpose().format(fmt);
      out << "angle_axis: "   << angle_axis.format(fmt);
      out << "translation: " << transform.translation().transpose().format(fmt);
      out << "R: " << R.row(0).format(fmt);
      out << "R: " << R.row(1).format(fmt);
      out << "R: " << R.row(2).format(fmt);
      out << "S: " << S.row(0).format(fmt);
      out << "S: " << S.row(1).format(fmt);
      out << "S: " << S.row(2).format(fmt);

      break;
    }
    case 7: { // align_vertices_rigid
      if (num_inputs != 2)
        throw Exception ("align_vertices_rigid requires 2 input");
      const Eigen::MatrixXd target_vertices = load_matrix (argument[0]);
      const Eigen::MatrixXd moving_vertices = load_matrix (argument[1]);
      const transform_type T = align_corresponding_vertices (target_vertices, moving_vertices, false);
      save_transform (T, output_path);
      break;
    }
    default: assert (0);
  }

}
<commit_msg>transformcalc: also output jacobian determinant<commit_after>/* Copyright (c) 2008-2019 the MRtrix3 contributors.
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 *
 * Covered Software is provided under this License on an "as is"
 * basis, without warranty of any kind, either expressed, implied, or
 * statutory, including, without limitation, warranties that the
 * Covered Software is free of defects, merchantable, fit for a
 * particular purpose or non-infringing.
 * See the Mozilla Public License v. 2.0 for more details.
 *
 * For more details, see http://www.mrtrix.org/.
 */

#include <Eigen/Geometry>
#include <unsupported/Eigen/MatrixFunctions>
#include <algorithm>
#include "command.h"
#include "math/math.h"
#include "math/average_space.h"
#include "image.h"
#include "file/nifti_utils.h"
#include "transform.h"
#include "file/key_value.h"

using namespace MR;
using namespace App;

const char* operations[] = {
  "invert",
  "half",
  "rigid",
  "header",
  "average",
  "interpolate",
  "decompose",
  "align_vertices_rigid",
  NULL
};

void usage ()
{
  AUTHOR = "Max Pietsch (maximilian.pietsch@kcl.ac.uk)";

  SYNOPSIS = "Perform calculations on linear transformation matrices";

  ARGUMENTS
  + Argument ("inputs", "the input(s) for the specified operation").allow_multiple()
  + Argument ("operation", "the operation to perform, one of: " + join(operations, ", ") + " (see description section for details).").type_choice (operations)
  + Argument ("output", "the output transformation matrix.").type_file_out ();

  EXAMPLES
  + Example ("Invert a transformation",
             "transformcalc matrix_in.txt invert matrix_out.txt",
             "")

  + Example ("Calculate the matrix square root of the input transformation (halfway transformation)",
             "transformcalc matrix_in.txt half matrix_out.txt",
             "")

  + Example ("Calculate the rigid component of an affine input transformation",
             "transformcalc affine_in.txt rigid rigid_out.txt",
             "")

  + Example ("Calculate the transformation matrix from an original image and an image with modified header",
             "transformcalc mov mapmovhdr header output",
             "")

  + Example ("Calculate the average affine matrix of a set of input matrices",
             "transformcalc input1.txt ... inputN.txt average matrix_out.txt",
             "")

  + Example ("Create interpolated transformation matrix between two inputs",
             "transformcalc input1.txt input2.txt interpolate matrix_out.txt",
             "Based on matrix decomposition with linear interpolation of "
             "translation, rotation and stretch described in: "
             "Shoemake, K., Hill, M., & Duff, T. (1992). Matrix Animation and Polar Decomposition. "
             "Matrix, 92, 258-264. doi:10.1.1.56.1336")

  + Example ("Decompose transformation matrix M into translation, rotation and stretch and shear (M = T * R * S)",
             "transformcalc matrix_in.txt decompose matrixes_out.txt",
             "The output is a key-value text file containing: "
             "scaling: vector of 3 scaling factors in x, y, z direction; "
             "shear: list of shear factors for xy, xz, yz axes; "
             "angles: list of Euler angles about static x, y, z axes in radians in the range [0:pi]x[-pi:pi]x[-pi:pi]; "
             "angle_axis: angle in radians and rotation axis; "
             "translation : translation vector along x, y, z axes in mm; "
             "R: composed roation matrix (R = rot_x * rot_y * rot_z); "
             "S: composed scaling and shear matrix")

  + Example ("Align two sets of landmarks using a rigid transformation",
             "transformcalc input moving.txt fixed.txt align_vertices_rigid output",
             "Vertex coordinates are in scanner space, corresponding vertices must be stored in the same row "
             "of moving.txt and fixed.txt. Requires 3 or more vertices in each file. "
             "Algorithm: Kabsch 'A solution for the best rotation to relate two sets of vectors' DOI:10.1107/S0567739476001873");

}

template <typename T> int sgn(T val) {
    return (T(0) < val) - (val < T(0));
}

transform_type align_corresponding_vertices (const Eigen::MatrixXd &target_vertices, const Eigen::MatrixXd &moving_vertices, bool scale) {
  //  this function aligns two sets of vertices which must have corresponding vertices stored in the same row
  //
  //  scale == false --> Kabsch
  //  minimise (target_vertices.row(i) - M * moving_vertices.row(i) + t).squaredNorm();
  //
  //  scale == true --> Umeyama
  //  nonrigid version of Kabsch algorithm that also includes scale (not shear)
  //
  assert(target_vertices.rows() == moving_vertices.rows());
  const size_t n = moving_vertices.rows();
  assert (n > 2);

  assert(target_vertices.cols() == moving_vertices.cols());
  assert(target_vertices.cols() == 3 && "align_corresponding_vertices implemented only for 3D data");

  Eigen::VectorXd moving_centre = moving_vertices.colwise().mean();
  Eigen::VectorXd target_centre = target_vertices.colwise().mean();
  Eigen::MatrixXd moving_centered = moving_vertices.rowwise() - moving_centre.transpose();
  Eigen::MatrixXd target_centered = target_vertices.rowwise() - target_centre.transpose();
  Eigen::MatrixXd cov = (target_centered.adjoint() * moving_centered) / default_type (n - 1);

  Eigen::JacobiSVD<Eigen::Matrix3d> svd (cov, Eigen::ComputeFullU | Eigen::ComputeFullV);

  // rotation matrix
  Eigen::Matrix3d R = svd.matrixV() * svd.matrixU().transpose();

  // calculate determinant of V*U^T to disambiguate rotation sign
  default_type f_det = R.determinant();
  Eigen::Vector3d e(1, 1, (f_det < 0)? -1 : 1);

  // recompute the rotation if the determinant was negative
  if (f_det < 0)
    R.noalias() = svd.matrixV() * e.asDiagonal() * svd.matrixU().transpose();

  // renormalize the rotation, R needs to be Matrix3d
  R = Eigen::Quaterniond(R).normalized().toRotationMatrix();

  if (scale) {
    default_type fsq = 0;
    for (size_t i = 0; i < n; ++ i)
      fsq += moving_centered.row(i).squaredNorm();
    // calculate and apply the scale
    default_type fscale = svd.singularValues().dot(e) / fsq;
    R *= fscale;
  }

  transform_type T;
  T.linear() = R;
  T.translation() = target_centre - (R * moving_centre);
  return T;
}

void run ()
{
  const size_t num_inputs = argument.size() - 2;
  const int op = argument[num_inputs];
  const std::string& output_path = argument.back();

  switch (op) {
    case 0: { // invert
      if (num_inputs != 1)
        throw Exception ("invert requires 1 input");
      transform_type input = load_transform (argument[0]);
      save_transform (input.inverse(), output_path);
      break;
    }
    case 1: { // half
      if (num_inputs != 1)
        throw Exception ("half requires 1 input");
      Eigen::Transform<default_type, 3, Eigen::Projective> input = load_transform (argument[0]);
      transform_type output;
      Eigen::Matrix<default_type, 4, 4> half = input.matrix().sqrt();
      output.matrix() = half.topLeftCorner(3,4);
      save_transform (output, output_path);
      break;
    }
    case 2: { // rigid
      if (num_inputs != 1)
        throw Exception ("rigid requires 1 input");
      transform_type input = load_transform (argument[0]);
      transform_type output (input);
      output.linear() = input.rotation();
      save_transform (output, output_path);
      break;
    }
    case 3: { // header
      if (num_inputs != 2)
        throw Exception ("header requires 2 inputs");
      auto orig_header = Header::open (argument[0]);
      auto modified_header = Header::open (argument[1]);

      transform_type forward_transform = Transform(modified_header).voxel2scanner * Transform(orig_header).voxel2scanner.inverse();
      save_transform (forward_transform.inverse(), output_path);
      break;
    }
    case 4: { // average
      if (num_inputs < 2)
        throw Exception ("average requires at least 2 inputs");
      transform_type transform_out;
      Eigen::Transform<default_type, 3, Eigen::Projective> Tin;
      Eigen::MatrixXd Min;
      vector<Eigen::MatrixXd> matrices;
      for (size_t i = 0; i < num_inputs; i++) {
        DEBUG(str(argument[i]));
        Tin = load_transform (argument[i]);
        matrices.push_back(Tin.matrix());
      }

      Eigen::MatrixXd average_matrix;
      Math::matrix_average ( matrices, average_matrix);
      transform_out.matrix() = average_matrix.topLeftCorner(3,4);
      save_transform (transform_out, output_path);
      break;
    }
    case 5: { // interpolate
      if (num_inputs != 3)
        throw Exception ("interpolation requires 3 inputs");
      transform_type transform1 = load_transform (argument[0]);
      transform_type transform2 = load_transform (argument[1]);
      default_type t = parse_floats(argument[2])[0];

      transform_type transform_out;

      if (t < 0.0 || t > 1.0)
        throw Exception ("t has to be in the interval [0,1]");

      Eigen::MatrixXd M1 = transform1.linear();
      Eigen::MatrixXd M2 = transform2.linear();
      if (sgn(M1.determinant()) != sgn(M2.determinant()))
        WARN("transformation determinants have different signs");

      Eigen::Matrix3d R1 = transform1.rotation();
      Eigen::Matrix3d R2 = transform2.rotation();
      Eigen::Quaterniond Q1(R1);
      Eigen::Quaterniond Q2(R2);
      Eigen::Quaterniond Qout;

      // stretch (shear becomes roation and stretch)
      Eigen::MatrixXd S1 = R1.transpose() * M1;
      Eigen::MatrixXd S2 = R2.transpose() * M2;
      if (!M1.isApprox(R1*S1))
        WARN ("M1 matrix decomposition might have failed");
      if (!M2.isApprox(R2*S2))
        WARN ("M2 matrix decomposition might have failed");

      transform_out.translation() = ((1.0 - t) * transform1.translation() + t * transform2.translation());
      Qout = Q1.slerp(t, Q2);
      transform_out.linear() = Qout * ((1 - t) * S1 + t * S2);
      INFO("\n"+str(transform_out.matrix().format(
        Eigen::IOFormat(Eigen::FullPrecision, 0, ", ", ",\n", "[", "]", "[", "]"))));
      save_transform (transform_out, output_path);
      break;
    }
    case 6: { // decompose
      if (num_inputs != 1)
        throw Exception ("decomposition requires 1 input");
      transform_type transform = load_transform (argument[0]);

      Eigen::MatrixXd M = transform.linear();
      Eigen::Matrix3d R = transform.rotation();
      Eigen::MatrixXd S = R.transpose() * M;
      if (!M.isApprox(R*S))
        WARN ("matrix decomposition might have failed");

      Eigen::Vector3d euler_angles = R.eulerAngles(0, 1, 2);
      assert (R.isApprox((Eigen::AngleAxisd(euler_angles[0], Eigen::Vector3d::UnitX())
              * Eigen::AngleAxisd(euler_angles[1], Eigen::Vector3d::UnitY())
              * Eigen::AngleAxisd(euler_angles[2], Eigen::Vector3d::UnitZ())).matrix()));

      Eigen::RowVector4d angle_axis;
      {
        auto AA = Eigen::AngleAxis<default_type> (R);
        angle_axis(0) = AA.angle();
        angle_axis.block<1,3>(0,1) = AA.axis();
      }

      File::OFStream out (output_path);
      Eigen::IOFormat fmt(Eigen::FullPrecision, Eigen::DontAlignCols, " ", "\n", "", "", "", "\n");
      out << "scaling: "     << Eigen::RowVector3d(S(0,0), S(1,1), S(2,2)).format(fmt);
      out << "shear: "       << Eigen::RowVector3d(S(0,1), S(0,2), S(1,2)).format(fmt);
      out << "angles: "      << euler_angles.transpose().format(fmt);
      out << "angle_axis: "   << angle_axis.format(fmt);
      out << "translation: " << transform.translation().transpose().format(fmt);
      out << "R: " << R.row(0).format(fmt);
      out << "R: " << R.row(1).format(fmt);
      out << "R: " << R.row(2).format(fmt);
      out << "S: " << S.row(0).format(fmt);
      out << "S: " << S.row(1).format(fmt);
      out << "S: " << S.row(2).format(fmt);
      out << "jacobian_det: " << str(transform.linear().topLeftCorner<3,3>().determinant()) << "\n";

      break;
    }
    case 7: { // align_vertices_rigid
      if (num_inputs != 2)
        throw Exception ("align_vertices_rigid requires 2 input");
      const Eigen::MatrixXd target_vertices = load_matrix (argument[0]);
      const Eigen::MatrixXd moving_vertices = load_matrix (argument[1]);
      const transform_type T = align_corresponding_vertices (target_vertices, moving_vertices, false);
      save_transform (T, output_path);
      break;
    }
    default: assert (0);
  }

}
<|endoftext|>


<commit_before><commit_msg>INTEGRATION: CWS mergesvp (1.18.2); FILE MERGED 2007/07/04 15:32:40 pl 1.18.2.1: #i79023# header moving<commit_after><|endoftext|>