<commit_before>// This file is part of the dune-gdt project:
//   http://users.dune-project.org/projects/dune-gdt
// Copyright holders: Felix Schindler
// License: BSD 2-Clause License (http://opensource.org/licenses/BSD-2-Clause)

#include <dune/stuff/test/test_common.hh>

#undef HAVE_FASP

#include <dune/common/exceptions.hh>

#if HAVE_ALUGRID_SERIAL_H || HAVE_ALUGRID_PARALLEL_H
# define ENABLE_ALUGRID 1
# include <dune/stuff/common/disable_warnings.hh>
#   include <dune/grid/alugrid.hh>
# include <dune/stuff/common/reenable_warnings.hh>
#else
# error This test requires ALUGrid!
#endif

#include "elliptic-testcases.hh"
#include "elliptic-swipdg-discretization.hh"

// change this to toggle test_output
std::ostream& test_out = std::cout;
//std::ostream& test_out = DSC_LOG.devnull();

class errors_are_not_as_expected
  : public Dune::Exception
{};

std::vector< double > truncate_vector(const std::vector< double >& in, const size_t size)
{
  assert(size <= in.size());
  if (size == in.size())
    return in;
  else {
    std::vector< double > ret(size);
    for (size_t ii = 0; ii < size; ++ii)
      ret[ii] = in[ii];
    return ret;
  }
} // ... truncate_vector(...)

typedef Dune::ALUConformGrid< 2, 2 > AluConform2dGridType;

typedef testing::Types< //EllipticTestCase::ESV07< AluConform2dGridType >
                       EllipticTestCase::LocalThermalBlock< AluConform2dGridType >
                      , EllipticTestCase::Spe10Model1< AluConform2dGridType >
                      > AluConform2dTestCases;

template< class TestCase >
struct EllipticSWIPDGDiscretization
  : public ::testing::Test
{
  void produces_correct_results() const
  {
    const TestCase test_case;
    test_case.print_header(test_out);
    test_out << std::endl;
    EllipticSWIPDG::EstimatorStudy< TestCase > estimator_study(test_case);
    auto results = estimator_study.run(test_out);
    std::stringstream ss;
    for (const auto& norm : estimator_study.provided_norms())
      if (!Dune::Stuff::Common::FloatCmp::lt(results[norm],
                                             truncate_vector(estimator_study.expected_results(norm),
                                                             results[norm].size()))) {
        Dune::Stuff::Common::print(results[norm],                          "   errors           (" + norm + ")", ss);
        Dune::Stuff::Common::print(estimator_study.expected_results(norm), "   expected results (" + norm + ")", ss);
      }
    const std::string failure = ss.str();
    if (!failure.empty()) DUNE_THROW_COLORFULLY(errors_are_not_as_expected, "\n" << failure);
  } // ... produces_correct_results()
}; // struct EllipticSWIPDGDiscretization

TYPED_TEST_CASE(EllipticSWIPDGDiscretization, AluConform2dTestCases);
TYPED_TEST(EllipticSWIPDGDiscretization, produces_correct_results) {
  this->produces_correct_results();
}

int main(int argc, char** argv)
{
  try {
    test_init(argc, argv);
    return RUN_ALL_TESTS();
  } catch (Dune::Exception& e) {
    std::cerr << "Dune reported error: " << e.what() << std::endl;
    std::abort();
  } catch (std::exception& e) {
    std::cerr << e.what() << std::endl;
    std::abort();
  } catch (...) {
    std::cerr << "Unknown exception thrown!" << std::endl;
    std::abort();
  } // try
}
<commit_msg>[tests.elliptic-estimators] enabled ESV07, disabled rest<commit_after>// This file is part of the dune-gdt project:
//   http://users.dune-project.org/projects/dune-gdt
// Copyright holders: Felix Schindler
// License: BSD 2-Clause License (http://opensource.org/licenses/BSD-2-Clause)

#include <dune/stuff/test/test_common.hh>

#undef HAVE_FASP

#include <dune/common/exceptions.hh>

#if HAVE_ALUGRID_SERIAL_H || HAVE_ALUGRID_PARALLEL_H
# define ENABLE_ALUGRID 1
# include <dune/stuff/common/disable_warnings.hh>
#   include <dune/grid/alugrid.hh>
# include <dune/stuff/common/reenable_warnings.hh>
#else
# error This test requires ALUGrid!
#endif

#include "elliptic-testcases.hh"
#include "elliptic-swipdg-discretization.hh"

// change this to toggle test_output
std::ostream& test_out = std::cout;
//std::ostream& test_out = DSC_LOG.devnull();

class errors_are_not_as_expected
  : public Dune::Exception
{};

std::vector< double > truncate_vector(const std::vector< double >& in, const size_t size)
{
  assert(size <= in.size());
  if (size == in.size())
    return in;
  else {
    std::vector< double > ret(size);
    for (size_t ii = 0; ii < size; ++ii)
      ret[ii] = in[ii];
    return ret;
  }
} // ... truncate_vector(...)

typedef Dune::ALUConformGrid< 2, 2 > AluConform2dGridType;

typedef testing::Types< EllipticTestCase::ESV07< AluConform2dGridType >
//                      , EllipticTestCase::LocalThermalBlock< AluConform2dGridType >
//                      , EllipticTestCase::Spe10Model1< AluConform2dGridType >
                      > AluConform2dTestCases;

template< class TestCase >
struct EllipticSWIPDGDiscretization
  : public ::testing::Test
{
  void produces_correct_results() const
  {
    const TestCase test_case;
    test_case.print_header(test_out);
    test_out << std::endl;
    EllipticSWIPDG::EstimatorStudy< TestCase > estimator_study(test_case);
    auto results = estimator_study.run(test_out);
    std::stringstream ss;
    for (const auto& norm : estimator_study.provided_norms())
      if (!Dune::Stuff::Common::FloatCmp::lt(results[norm],
                                             truncate_vector(estimator_study.expected_results(norm),
                                                             results[norm].size()))) {
        Dune::Stuff::Common::print(results[norm],                          "   errors           (" + norm + ")", ss);
        Dune::Stuff::Common::print(estimator_study.expected_results(norm), "   expected results (" + norm + ")", ss);
      }
    const std::string failure = ss.str();
    if (!failure.empty()) DUNE_THROW_COLORFULLY(errors_are_not_as_expected, "\n" << failure);
  } // ... produces_correct_results()
}; // struct EllipticSWIPDGDiscretization

TYPED_TEST_CASE(EllipticSWIPDGDiscretization, AluConform2dTestCases);
TYPED_TEST(EllipticSWIPDGDiscretization, produces_correct_results) {
  this->produces_correct_results();
}

int main(int argc, char** argv)
{
  try {
    test_init(argc, argv);
    return RUN_ALL_TESTS();
  } catch (Dune::Exception& e) {
    std::cerr << "Dune reported error: " << e.what() << std::endl;
    std::abort();
  } catch (std::exception& e) {
    std::cerr << e.what() << std::endl;
    std::abort();
  } catch (...) {
    std::cerr << "Unknown exception thrown!" << std::endl;
    std::abort();
  } // try
}
<|endoftext|>


<commit_before>//===----------------------------------------------------------------------===//
//
//                         PelotonDB
//
// hash_join_test.cpp
//
// Identification: tests/executor/hash_join_test.cpp
//
// Copyright (c) 2015, Carnegie Mellon University Database Group
//
//===----------------------------------------------------------------------===//

#include <memory>

#include "gmock/gmock.h"
#include "gtest/gtest.h"

#include "backend/common/types.h"
#include "backend/executor/logical_tile.h"
#include "backend/executor/logical_tile_factory.h"

#include "backend/executor/hash_join_executor.h"
#include "backend/executor/hash_executor.h"
#include "backend/executor/merge_join_executor.h"
#include "backend/executor/nested_loop_join_executor.h"

#include "backend/expression/abstract_expression.h"
#include "backend/expression/tuple_value_expression.h"
#include "backend/expression/expression_util.h"

#include "backend/planner/hash_join_plan.h"
#include "backend/planner/hash_plan.h"
#include "backend/planner/merge_join_plan.h"
#include "backend/planner/nested_loop_join_plan.h"

#include "backend/storage/data_table.h"

#include "mock_executor.h"
#include "executor/executor_tests_util.h"
#include "executor/join_tests_util.h"
#include "harness.h"

using ::testing::NotNull;
using ::testing::Return;

namespace peloton {
namespace test {

std::vector<planner::MergeJoinPlan::JoinClause> CreateJoinClauses() {
  std::vector<planner::MergeJoinPlan::JoinClause> join_clauses;
  auto left = expression::TupleValueFactory(0, 1);
  auto right = expression::TupleValueFactory(1, 1);
  bool reversed = false;
  join_clauses.emplace_back(left, right, reversed);
  return join_clauses;
}

std::vector<PlanNodeType> join_algorithms = {
    PLAN_NODE_TYPE_NESTLOOP,
    PLAN_NODE_TYPE_MERGEJOIN,
    PLAN_NODE_TYPE_HASHJOIN
};

std::vector<PelotonJoinType> join_types = {
    JOIN_TYPE_INNER,
    JOIN_TYPE_LEFT,
    JOIN_TYPE_RIGHT,
    JOIN_TYPE_OUTER
};

void ExecuteJoinTest(PlanNodeType join_algorithm, PelotonJoinType join_type);

TEST(JoinTests, JoinPredicateTest) {

  // Go over all join algorithms
  for(auto join_algorithm : join_algorithms) {
    std::cout << "JOIN ALGORITHM :: " << PlanNodeTypeToString(join_algorithm) << "\n";

    // Go over all join types
    for(auto join_type : join_types) {
      std::cout << "JOIN TYPE :: " << join_type << "\n";

      // Execute the join test
      ExecuteJoinTest(join_algorithm, join_type);

    }
  }

}

void ExecuteJoinTest(PlanNodeType join_algorithm, PelotonJoinType join_type) {

  //===--------------------------------------------------------------------===//
  // Mock table scan executors
  //===--------------------------------------------------------------------===//

  MockExecutor left_table_scan_executor, right_table_scan_executor;

  // Create a table and wrap it in logical tile
  size_t tile_group_size = TESTS_TUPLES_PER_TILEGROUP;
  size_t left_table_tile_group_count = 3;
  size_t right_table_tile_group_count = 2;

  // Left table has 3 tile groups
  std::unique_ptr<storage::DataTable> left_table(
      ExecutorTestsUtil::CreateTable(tile_group_size));
  ExecutorTestsUtil::PopulateTable(left_table.get(),
                                   tile_group_size * left_table_tile_group_count,
                                   false,
                                   false, false);

  //std::cout << (*left_table);

  // Right table has 2 tile groups
  std::unique_ptr<storage::DataTable> right_table(
      ExecutorTestsUtil::CreateTable(tile_group_size));
  ExecutorTestsUtil::PopulateTable(right_table.get(),
                                   tile_group_size * right_table_tile_group_count,
                                   false, false, false);

  //std::cout << (*right_table);

  // Wrap the input tables with logical tiles
  std::unique_ptr<executor::LogicalTile> left_table_logical_tile1(
      executor::LogicalTileFactory::WrapTileGroup(left_table->GetTileGroup(0)));
  std::unique_ptr<executor::LogicalTile> left_table_logical_tile2(
      executor::LogicalTileFactory::WrapTileGroup(left_table->GetTileGroup(1)));
  std::unique_ptr<executor::LogicalTile> left_table_logical_tile3(
      executor::LogicalTileFactory::WrapTileGroup(left_table->GetTileGroup(2)));

  std::unique_ptr<executor::LogicalTile> right_table_logical_tile1(
      executor::LogicalTileFactory::WrapTileGroup(
          right_table->GetTileGroup(0)));
  std::unique_ptr<executor::LogicalTile> right_table_logical_tile2(
      executor::LogicalTileFactory::WrapTileGroup(
          right_table->GetTileGroup(1)));

  // Left scan executor returns logical tiles from the left table

  EXPECT_CALL(left_table_scan_executor, DInit())
  .WillOnce(Return(true));

  EXPECT_CALL(left_table_scan_executor, DExecute())
  .WillOnce(Return(true))
  .WillOnce(Return(true))
  .WillOnce(Return(true))
  .WillOnce(Return(false));

  EXPECT_CALL(left_table_scan_executor, GetOutput())
  .WillOnce(Return(left_table_logical_tile1.release()))
  .WillOnce(Return(left_table_logical_tile2.release()))
  .WillOnce(Return(left_table_logical_tile3.release()));

  // Right scan executor returns logical tiles from the right table

  EXPECT_CALL(right_table_scan_executor, DInit())
  .WillOnce(Return(true));

  EXPECT_CALL(right_table_scan_executor, DExecute())
  .WillOnce(Return(true))
  .WillOnce(Return(true))
  .WillOnce(Return(false));

  EXPECT_CALL(right_table_scan_executor, GetOutput())
  .WillOnce(Return(right_table_logical_tile1.release()))
  .WillOnce(Return(right_table_logical_tile2.release()));

  //===--------------------------------------------------------------------===//
  // Setup join plan nodes and executors and run them
  //===--------------------------------------------------------------------===//

  auto result_tuple_count = 0;
  auto projection = JoinTestsUtil::CreateProjection();

  // Construct predicate
  expression::AbstractExpression *predicate = JoinTestsUtil::CreateJoinPredicate();

  // Differ based on join algorithm
  switch(join_algorithm) {

    case PLAN_NODE_TYPE_NESTLOOP: {

      // Create nested loop join plan node.
      planner::NestedLoopJoinPlan nested_loop_join_node(join_type, predicate, projection);

      // Run the nested loop join executor
      executor::NestedLoopJoinExecutor nested_loop_join_executor(&nested_loop_join_node, nullptr);

      // Construct the executor tree
      nested_loop_join_executor.AddChild(&left_table_scan_executor);
      nested_loop_join_executor.AddChild(&right_table_scan_executor);

      // Run the nested loop join executor
      EXPECT_TRUE(nested_loop_join_executor.Init());
      while(nested_loop_join_executor.Execute() == true) {
        std::unique_ptr<executor::LogicalTile> result_logical_tile(nested_loop_join_executor.GetOutput());

        if(result_logical_tile != nullptr) {
          result_tuple_count += result_logical_tile->GetTupleCount();
        }
      }

    }
    break;

    case PLAN_NODE_TYPE_MERGEJOIN: {

      // Create join clauses
      std::vector<planner::MergeJoinPlan::JoinClause> join_clauses;
      join_clauses = CreateJoinClauses();

      // Create merge join plan node
      planner::MergeJoinPlan merge_join_node(join_type, predicate, projection, join_clauses);

      // Construct the merge join executor
      executor::MergeJoinExecutor merge_join_executor(&merge_join_node, nullptr);

      // Construct the executor tree
      merge_join_executor.AddChild(&left_table_scan_executor);
      merge_join_executor.AddChild(&right_table_scan_executor);

      // Run the merge join executor
      EXPECT_TRUE(merge_join_executor.Init());
      while(merge_join_executor.Execute() == true) {
        std::unique_ptr<executor::LogicalTile> result_logical_tile(merge_join_executor.GetOutput());

        if(result_logical_tile != nullptr) {
          result_tuple_count += result_logical_tile->GetTupleCount();
          //std::cout << (*result_logical_tile);
        }
      }

    }
    break;

    case PLAN_NODE_TYPE_HASHJOIN: {

      // Create hash plan node
      expression::AbstractExpression *right_table_attr_1 =
          new expression::TupleValueExpression(1, 1);

      std::vector<std::unique_ptr<const expression::AbstractExpression> > hash_keys;
      hash_keys.emplace_back(right_table_attr_1);

      // Create hash plan node
      planner::HashPlan hash_plan_node(hash_keys);

      // Construct the hash executor
      executor::HashExecutor hash_executor(&hash_plan_node, nullptr);

      // Create hash join plan node.
      planner::HashJoinPlan hash_join_plan_node(join_type, predicate, projection);

      // Construct the hash join executor
      executor::HashJoinExecutor hash_join_executor(&hash_join_plan_node, nullptr);

      // Construct the executor tree
      hash_join_executor.AddChild(&left_table_scan_executor);
      hash_join_executor.AddChild(&hash_executor);

      hash_executor.AddChild(&right_table_scan_executor);

      // Run the hash_join_executor
      EXPECT_TRUE(hash_join_executor.Init());
      while(hash_join_executor.Execute() == true) {
        std::unique_ptr<executor::LogicalTile> result_logical_tile(hash_join_executor.GetOutput());

        if(result_logical_tile != nullptr) {
          result_tuple_count += result_logical_tile->GetTupleCount();
          //std::cout << (*result_logical_tile);
        }
      }

    }
    break;

    default:
      throw Exception("Unsupported join algorithm : " + std::to_string(join_algorithm));
      break;
  }

  //===--------------------------------------------------------------------===//
  // Execute test
  //===--------------------------------------------------------------------===//

  // Check output
  switch(join_type) {
    case JOIN_TYPE_INNER:
      EXPECT_EQ(result_tuple_count, 2 * tile_group_size);
      break;

    case JOIN_TYPE_LEFT:
      EXPECT_EQ(result_tuple_count, 3 * tile_group_size);
      break;

    case JOIN_TYPE_RIGHT:
      EXPECT_EQ(result_tuple_count, 2 * tile_group_size);
      break;

    case JOIN_TYPE_OUTER:
      EXPECT_EQ(result_tuple_count, 3 * tile_group_size);
      break;

    default:
      throw Exception("Unsupported join type : " + std::to_string(join_type));
      break;
  }

}

}  // namespace test
}  // namespace peloton
<commit_msg>Updated the join test<commit_after>//===----------------------------------------------------------------------===//
//
//                         PelotonDB
//
// hash_join_test.cpp
//
// Identification: tests/executor/hash_join_test.cpp
//
// Copyright (c) 2015, Carnegie Mellon University Database Group
//
//===----------------------------------------------------------------------===//

#include <memory>

#include "gmock/gmock.h"
#include "gtest/gtest.h"

#include "backend/common/types.h"
#include "backend/executor/logical_tile.h"
#include "backend/executor/logical_tile_factory.h"

#include "backend/executor/hash_join_executor.h"
#include "backend/executor/hash_executor.h"
#include "backend/executor/merge_join_executor.h"
#include "backend/executor/nested_loop_join_executor.h"

#include "backend/expression/abstract_expression.h"
#include "backend/expression/tuple_value_expression.h"
#include "backend/expression/expression_util.h"

#include "backend/planner/hash_join_plan.h"
#include "backend/planner/hash_plan.h"
#include "backend/planner/merge_join_plan.h"
#include "backend/planner/nested_loop_join_plan.h"

#include "backend/storage/data_table.h"

#include "mock_executor.h"
#include "executor/executor_tests_util.h"
#include "executor/join_tests_util.h"
#include "harness.h"

using ::testing::NotNull;
using ::testing::Return;

namespace peloton {
namespace test {

std::vector<planner::MergeJoinPlan::JoinClause> CreateJoinClauses() {
  std::vector<planner::MergeJoinPlan::JoinClause> join_clauses;
  auto left = expression::TupleValueFactory(0, 1);
  auto right = expression::TupleValueFactory(1, 1);
  bool reversed = false;
  join_clauses.emplace_back(left, right, reversed);
  return join_clauses;
}

std::vector<PlanNodeType> join_algorithms = {
    PLAN_NODE_TYPE_NESTLOOP,
    PLAN_NODE_TYPE_MERGEJOIN,
    PLAN_NODE_TYPE_HASHJOIN
};

std::vector<PelotonJoinType> join_types = {
    JOIN_TYPE_INNER,
    JOIN_TYPE_LEFT,
    JOIN_TYPE_RIGHT,
    JOIN_TYPE_OUTER
};

void ExecuteJoinTest(PlanNodeType join_algorithm, PelotonJoinType join_type);

oid_t CountTuplesWithNullFields(executor::LogicalTile *logical_tile);

TEST(JoinTests, JoinPredicateTest) {

  // Go over all join algorithms
  for(auto join_algorithm : join_algorithms) {
    std::cout << "JOIN ALGORITHM :: " << PlanNodeTypeToString(join_algorithm) << "\n";

    // Go over all join types
    for(auto join_type : join_types) {
      std::cout << "JOIN TYPE :: " << join_type << "\n";

      // Execute the join test
      ExecuteJoinTest(join_algorithm, join_type);

    }
  }

}

void ExecuteJoinTest(PlanNodeType join_algorithm, PelotonJoinType join_type) {

  //===--------------------------------------------------------------------===//
  // Mock table scan executors
  //===--------------------------------------------------------------------===//

  MockExecutor left_table_scan_executor, right_table_scan_executor;

  // Create a table and wrap it in logical tile
  size_t tile_group_size = TESTS_TUPLES_PER_TILEGROUP;
  size_t left_table_tile_group_count = 3;
  size_t right_table_tile_group_count = 2;

  // Left table has 3 tile groups
  std::unique_ptr<storage::DataTable> left_table(
      ExecutorTestsUtil::CreateTable(tile_group_size));
  ExecutorTestsUtil::PopulateTable(left_table.get(),
                                   tile_group_size * left_table_tile_group_count,
                                   false,
                                   false, false);

  //std::cout << (*left_table);

  // Right table has 2 tile groups
  std::unique_ptr<storage::DataTable> right_table(
      ExecutorTestsUtil::CreateTable(tile_group_size));
  ExecutorTestsUtil::PopulateTable(right_table.get(),
                                   tile_group_size * right_table_tile_group_count,
                                   false, false, false);

  //std::cout << (*right_table);

  // Wrap the input tables with logical tiles
  std::unique_ptr<executor::LogicalTile> left_table_logical_tile1(
      executor::LogicalTileFactory::WrapTileGroup(left_table->GetTileGroup(0)));
  std::unique_ptr<executor::LogicalTile> left_table_logical_tile2(
      executor::LogicalTileFactory::WrapTileGroup(left_table->GetTileGroup(1)));
  std::unique_ptr<executor::LogicalTile> left_table_logical_tile3(
      executor::LogicalTileFactory::WrapTileGroup(left_table->GetTileGroup(2)));

  std::unique_ptr<executor::LogicalTile> right_table_logical_tile1(
      executor::LogicalTileFactory::WrapTileGroup(
          right_table->GetTileGroup(0)));
  std::unique_ptr<executor::LogicalTile> right_table_logical_tile2(
      executor::LogicalTileFactory::WrapTileGroup(
          right_table->GetTileGroup(1)));

  // Left scan executor returns logical tiles from the left table

  EXPECT_CALL(left_table_scan_executor, DInit())
  .WillOnce(Return(true));

  EXPECT_CALL(left_table_scan_executor, DExecute())
  .WillOnce(Return(true))
  .WillOnce(Return(true))
  .WillOnce(Return(true))
  .WillOnce(Return(false));

  EXPECT_CALL(left_table_scan_executor, GetOutput())
  .WillOnce(Return(left_table_logical_tile1.release()))
  .WillOnce(Return(left_table_logical_tile2.release()))
  .WillOnce(Return(left_table_logical_tile3.release()));

  // Right scan executor returns logical tiles from the right table

  EXPECT_CALL(right_table_scan_executor, DInit())
  .WillOnce(Return(true));

  EXPECT_CALL(right_table_scan_executor, DExecute())
  .WillOnce(Return(true))
  .WillOnce(Return(true))
  .WillOnce(Return(false));

  EXPECT_CALL(right_table_scan_executor, GetOutput())
  .WillOnce(Return(right_table_logical_tile1.release()))
  .WillOnce(Return(right_table_logical_tile2.release()));

  //===--------------------------------------------------------------------===//
  // Setup join plan nodes and executors and run them
  //===--------------------------------------------------------------------===//

  oid_t result_tuple_count = 0;
  oid_t tuples_with_null = 0;
  auto projection = JoinTestsUtil::CreateProjection();

  // Construct predicate
  expression::AbstractExpression *predicate = JoinTestsUtil::CreateJoinPredicate();

  // Differ based on join algorithm
  switch(join_algorithm) {

    case PLAN_NODE_TYPE_NESTLOOP: {

      // Create nested loop join plan node.
      planner::NestedLoopJoinPlan nested_loop_join_node(join_type, predicate, projection);

      // Run the nested loop join executor
      executor::NestedLoopJoinExecutor nested_loop_join_executor(&nested_loop_join_node, nullptr);

      // Construct the executor tree
      nested_loop_join_executor.AddChild(&left_table_scan_executor);
      nested_loop_join_executor.AddChild(&right_table_scan_executor);

      // Run the nested loop join executor
      EXPECT_TRUE(nested_loop_join_executor.Init());
      while(nested_loop_join_executor.Execute() == true) {
        std::unique_ptr<executor::LogicalTile> result_logical_tile(nested_loop_join_executor.GetOutput());

        if(result_logical_tile != nullptr) {
          result_tuple_count += result_logical_tile->GetTupleCount();
          tuples_with_null += CountTuplesWithNullFields(result_logical_tile.get());
        }
      }

    }
    break;

    case PLAN_NODE_TYPE_MERGEJOIN: {

      // Create join clauses
      std::vector<planner::MergeJoinPlan::JoinClause> join_clauses;
      join_clauses = CreateJoinClauses();

      // Create merge join plan node
      planner::MergeJoinPlan merge_join_node(join_type, predicate, projection, join_clauses);

      // Construct the merge join executor
      executor::MergeJoinExecutor merge_join_executor(&merge_join_node, nullptr);

      // Construct the executor tree
      merge_join_executor.AddChild(&left_table_scan_executor);
      merge_join_executor.AddChild(&right_table_scan_executor);

      // Run the merge join executor
      EXPECT_TRUE(merge_join_executor.Init());
      while(merge_join_executor.Execute() == true) {
        std::unique_ptr<executor::LogicalTile> result_logical_tile(merge_join_executor.GetOutput());

        if(result_logical_tile != nullptr) {
          result_tuple_count += result_logical_tile->GetTupleCount();
          tuples_with_null += CountTuplesWithNullFields(result_logical_tile.get());
          //std::cout << (*result_logical_tile);
        }
      }

    }
    break;

    case PLAN_NODE_TYPE_HASHJOIN: {

      // Create hash plan node
      expression::AbstractExpression *right_table_attr_1 =
          new expression::TupleValueExpression(1, 1);

      std::vector<std::unique_ptr<const expression::AbstractExpression> > hash_keys;
      hash_keys.emplace_back(right_table_attr_1);

      // Create hash plan node
      planner::HashPlan hash_plan_node(hash_keys);

      // Construct the hash executor
      executor::HashExecutor hash_executor(&hash_plan_node, nullptr);

      // Create hash join plan node.
      planner::HashJoinPlan hash_join_plan_node(join_type, predicate, projection);

      // Construct the hash join executor
      executor::HashJoinExecutor hash_join_executor(&hash_join_plan_node, nullptr);

      // Construct the executor tree
      hash_join_executor.AddChild(&left_table_scan_executor);
      hash_join_executor.AddChild(&hash_executor);

      hash_executor.AddChild(&right_table_scan_executor);

      // Run the hash_join_executor
      EXPECT_TRUE(hash_join_executor.Init());
      while(hash_join_executor.Execute() == true) {
        std::unique_ptr<executor::LogicalTile> result_logical_tile(hash_join_executor.GetOutput());

        if(result_logical_tile != nullptr) {
          result_tuple_count += result_logical_tile->GetTupleCount();
          tuples_with_null += CountTuplesWithNullFields(result_logical_tile.get());
          //std::cout << (*result_logical_tile);
        }
      }

    }
    break;

    default:
      throw Exception("Unsupported join algorithm : " + std::to_string(join_algorithm));
      break;
  }

  //===--------------------------------------------------------------------===//
  // Execute test
  //===--------------------------------------------------------------------===//

  // Check output
  switch(join_type) {
    case JOIN_TYPE_INNER:
      EXPECT_EQ(result_tuple_count, 2 * tile_group_size);
      EXPECT_EQ(tuples_with_null, 0 * tile_group_size);
      break;

    case JOIN_TYPE_LEFT:
      EXPECT_EQ(result_tuple_count, 3 * tile_group_size);
      EXPECT_EQ(tuples_with_null, 1 * tile_group_size);
      break;

    case JOIN_TYPE_RIGHT:
      EXPECT_EQ(result_tuple_count, 2 * tile_group_size);
      EXPECT_EQ(tuples_with_null, 0 * tile_group_size);
      break;

    case JOIN_TYPE_OUTER:
      EXPECT_EQ(result_tuple_count, 3 * tile_group_size);
      EXPECT_EQ(tuples_with_null, 1 * tile_group_size);
      break;

    default:
      throw Exception("Unsupported join type : " + std::to_string(join_type));
      break;
  }

}

oid_t CountTuplesWithNullFields(executor::LogicalTile *logical_tile) {
  assert(logical_tile);

  // Get column count
  auto column_count = logical_tile->GetColumnCount();
  oid_t tuples_with_null = 0;

  // Go over the tile
  for (auto logical_tile_itr : *logical_tile) {
    const expression::ContainerTuple<executor::LogicalTile> left_tuple(
        logical_tile, logical_tile_itr);

      // Go over all the fields and check for null values
      for(oid_t col_itr = 0; col_itr < column_count; col_itr++) {
        auto val = left_tuple.GetValue(col_itr);
        if(val.IsNull()) {
          tuples_with_null++;
          break;
        }
      }

  }

  return tuples_with_null;
}

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


<commit_before>//
// Created by Ivan Shynkarenka on 18.09.2016.
//

#include "catch.hpp"

#include "logging/record.h"

using namespace CppLogging;

class Date
{
public:
    Date(int year, int month, int day) : _year(year), _month(month), _day(day) {}

    friend std::ostream& operator<<(std::ostream& os, const Date& date)
    { return os << date._year << '-' << date._month << '-' << date._day; }

private:
    int _year, _month, _day;
};

template <typename... Args>
std::string format(const char* pattern, const Args&... args)
{
    Record record;
    record.FormatSerialize(pattern, args...);
    record.FormatDeserialize();
    return record.message;
}

TEST_CASE("Format message", "[CppLogging]")
{

    REQUIRE(format("no arguments") == "no arguments");
/*
    REQUIRE(format("{0}, {1}, {2}", -1, 0, 1) == "-1, 0, 1");
    REQUIRE(format("{0}, {1}, {2}", 'a', 'b', 'c') == "a, b, c");
    REQUIRE(format("{}, {}, {}", 'a', 'b', 'c') == "a, b, c");
    REQUIRE(format("{2}, {1}, {0}", 'a', 'b', 'c') == "c, b, a");
    REQUIRE(format("{0}{1}{0}", "abra", "cad") == "abracadabra");
    REQUIRE(format("{:<30}", "left aligned") == "left aligned                  ");
    REQUIRE(format("{:>30}", "right aligned") == "                 right aligned");
    REQUIRE(format("{:^30}", "centered") == "           centered           ");
    REQUIRE(format("{:*^30}", "centered") == "***********centered***********");
    REQUIRE(format("{:+f}; {:+f}", 3.14, -3.14) == "+3.140000; -3.140000");
    REQUIRE(format("{: f}; {: f}", 3.14, -3.14) == " 3.140000; -3.140000");
    REQUIRE(format("{:-f}; {:-f}", 3.14, -3.14) == "3.140000; -3.140000");
    REQUIRE(format("int: {0:d};  hex: {0:x};  oct: {0:o}; bin: {0:b}", 42) == "int: 42;  hex: 2a;  oct: 52; bin: 101010");
    REQUIRE(format("int: {0:d};  hex: {0:#x};  oct: {0:#o};  bin: {0:#b}", 42) == "int: 42;  hex: 0x2a;  oct: 052;  bin: 0b101010");
    REQUIRE(format("The date is {}", Date(2012, 12, 9)) == "The date is 2012-12-9");
    REQUIRE(format("Elapsed time: {s:.2f} seconds", "s"_a = 1.23) == "Elapsed time: 1.23 seconds");
    REQUIRE(format("The answer is {}"_format(42).c_str()) == "The answer is 42");
*/
}
<commit_msg>Clang<commit_after>//
// Created by Ivan Shynkarenka on 18.09.2016.
//

#include "catch.hpp"

#include "logging/record.h"

using namespace CppLogging;

class Date
{
public:
    Date(int year, int month, int day) : _year(year), _month(month), _day(day) {}

    friend std::ostream& operator<<(std::ostream& os, const Date& date)
    { return os << date._year << '-' << date._month << '-' << date._day; }

private:
    int _year, _month, _day;
};

template <typename... Args>
std::string format(const char* pattern, const Args&... args)
{
    Record record;
    record.FormatSerialize(pattern, args...);
    record.FormatDeserialize();
    return record.message;
}

TEST_CASE("Format message", "[CppLogging]")
{

    REQUIRE(format("no arguments") == "no arguments");
    REQUIRE(format("{0}, {1}, {2}", -1, 0, 1) == "-1, 0, 1");
/*
    REQUIRE(format("{0}, {1}, {2}", 'a', 'b', 'c') == "a, b, c");
    REQUIRE(format("{}, {}, {}", 'a', 'b', 'c') == "a, b, c");
    REQUIRE(format("{2}, {1}, {0}", 'a', 'b', 'c') == "c, b, a");
    REQUIRE(format("{0}{1}{0}", "abra", "cad") == "abracadabra");
    REQUIRE(format("{:<30}", "left aligned") == "left aligned                  ");
    REQUIRE(format("{:>30}", "right aligned") == "                 right aligned");
    REQUIRE(format("{:^30}", "centered") == "           centered           ");
    REQUIRE(format("{:*^30}", "centered") == "***********centered***********");
    REQUIRE(format("{:+f}; {:+f}", 3.14, -3.14) == "+3.140000; -3.140000");
    REQUIRE(format("{: f}; {: f}", 3.14, -3.14) == " 3.140000; -3.140000");
    REQUIRE(format("{:-f}; {:-f}", 3.14, -3.14) == "3.140000; -3.140000");
    REQUIRE(format("int: {0:d};  hex: {0:x};  oct: {0:o}; bin: {0:b}", 42) == "int: 42;  hex: 2a;  oct: 52; bin: 101010");
    REQUIRE(format("int: {0:d};  hex: {0:#x};  oct: {0:#o};  bin: {0:#b}", 42) == "int: 42;  hex: 0x2a;  oct: 052;  bin: 0b101010");
    REQUIRE(format("The date is {}", Date(2012, 12, 9)) == "The date is 2012-12-9");
    REQUIRE(format("Elapsed time: {s:.2f} seconds", "s"_a = 1.23) == "Elapsed time: 1.23 seconds");
    REQUIRE(format("The answer is {}"_format(42).c_str()) == "The answer is 42");
*/
}
<|endoftext|>


<commit_before>/*
 * Copyright (C) 2017  Bastien Penavayre
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */

#pragma once
#include "println.hpp"
#include "meta_tlist.hpp"

template <class T> void unit(T);

namespace auto_testing
{
    using unit_list = unconstexpr::meta_tlist<>;

    template <class Char, Char... chars>
    struct TestLogger final
    {
        template <int = unit_list::push_back<TestLogger>()>
            using pusher = TestLogger;

        static void run() { unit(TestLogger{}); }
        static constexpr Char repr[] = {chars..., ':', '\0'};

        TestLogger() { log::Println("TESTING", repr); }
        ~TestLogger() { log::Println(); }
    };

    template <class... Args>
    constexpr void run(detail::type_list<Args...>)
    {
        (Args::run(), ...);
    }
};

template <class Char, Char... chars>
constexpr auto operator""_logger_string() {
    return (typename auto_testing::TestLogger<Char, chars...>::template pusher<>){};
}

#define new_unit(x) template <> void unit(decltype(x ## _logger_string))
#define run_units_so_far() auto_testing::run(auto_testing::unit_list::current_type<>{})
<commit_msg>Add conditional macro DONT_USE_META in aut_testing.hpp<commit_after>/*
 * Copyright (C) 2017  Bastien Penavayre
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */

#pragma once
#include "println.hpp"

#ifndef DONT_USE_META

# include "meta_tlist.hpp"
template <class T> void unit(T);

namespace auto_testing
{
    using unit_list = unconstexpr::meta_tlist<>;

    template <class Char, Char... chars>
    struct TestLogger final
    {
        template <int = unit_list::push_back<TestLogger>()>
            using pusher = TestLogger;

        static void run() { unit(TestLogger{}); }
        static constexpr Char repr[] = {chars..., ':', '\0'};

        TestLogger() { log::Println("TESTING", repr); }
        ~TestLogger() { log::Println(); }
    };

    template <class... Args>
    constexpr void run(detail::type_list<Args...>)
    {
        (Args::run(), ...);
    }
};

template <class Char, Char... chars>
constexpr auto operator""_logger_string() {
    return (typename auto_testing::TestLogger<Char, chars...>::template pusher<>){};
}

# define new_unit(x) template <> void unit(decltype(x ## _logger_string))
# define run_units_so_far() auto_testing::run(auto_testing::unit_list::current_type<>{})

#else /*DONT_USE_META*/

namespace auto_testing
{
    template <class Char, Char... Chars>
    struct TestLogger final
    {
        static constexpr Char repr[] = {Chars..., ':', '\0'};
    };

    template <size_t N, class Literal>
    struct Number final
    {
        Number(std::integral_constant<size_t, N>)
        {
            if constexpr(N != 0) log::Println();
            log::Println("TESTING", Literal::repr);
        }
    };

    template <size_t... Indexs>
    constexpr auto make_integral(std::index_sequence<Indexs...>) ->
        std::tuple<std::integral_constant<size_t, Indexs>...>
    {
        return {};
    }
}

template <class Char, Char... Chars>
constexpr auto operator""_logger_string() -> auto_testing::TestLogger<Char, Chars...> {
    return {};
}

# define new_unit(x)                                    \
    void unit(auto_testing::Number<__COUNTER__, decltype(x ## _logger_string)>)

# define run_units_so_far()                                             \
    std::apply([](auto... args) { (unit(args), ...); },                 \
               auto_testing::make_integral(std::make_index_sequence<__COUNTER__-1>{}));

#endif /*DONT_USE_META*/
<|endoftext|>


<commit_before>/******************************************************************************
*       SOFA, Simulation Open-Framework Architecture, development version     *
*                (c) 2006-2017 INRIA, USTL, UJF, CNRS, MGH                    *
*                                                                             *
* This program is free software; you can redistribute it and/or modify it     *
* under the terms of the GNU Lesser General Public License as published by    *
* the Free Software Foundation; either version 2.1 of the License, or (at     *
* your option) any later version.                                             *
*                                                                             *
* This program is distributed in the hope that it will be useful, but WITHOUT *
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or       *
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License *
* for more details.                                                           *
*                                                                             *
* You should have received a copy of the GNU Lesser General Public License    *
* along with this program. If not, see <http://www.gnu.org/licenses/>.        *
*******************************************************************************
* Authors: The SOFA Team and external contributors (see Authors.txt)          *
*                                                                             *
* Contact information: contact@sofa-framework.org                             *
******************************************************************************/
#include <SofaBaseCollision/DefaultPipeline.h>

#include <sofa/core/CollisionModel.h>
#include <sofa/core/ObjectFactory.h>
#include <sofa/core/collision/BroadPhaseDetection.h>
#include <sofa/core/collision/NarrowPhaseDetection.h>
#include <sofa/core/collision/CollisionGroupManager.h>
#include <sofa/core/collision/ContactManager.h>
#include <sofa/core/visual/VisualParams.h>

#include <sofa/simulation/Node.h>

#ifdef SOFA_DUMP_VISITOR_INFO
#include <sofa/simulation/Visitor.h>
#endif

#include <sofa/helper/AdvancedTimer.h>
using sofa::helper::AdvancedTimer ;
using sofa::helper::ScopedAdvancedTimer ;

namespace sofa
{

namespace component
{

namespace collision
{

using namespace core;
using namespace core::objectmodel;
using namespace core::collision;
using namespace sofa::defaulttype;

SOFA_DECL_CLASS(DefaultPipeline)

int DefaultPipelineClass = core::RegisterObject("The default collision detection and modeling pipeline")
        .add< DefaultPipeline >()
        .addAlias("CollisionPipeline")
        ;

DefaultPipeline::DefaultPipeline()
    : d_doPrintInfoMessage(initData(&d_doPrintInfoMessage, false, "verbose",
                                    "Display extra informations at each computation step. (default=false)"))
    , d_doDebugDraw(initData(&d_doDebugDraw, false, "draw",
                             "Draw the detected collisions. (default=false)"))
    //TODO(dmarchal) fix the min & max value with response from discussion.
    , d_depth(initData(&d_depth, 6, "depth",
                       "Max depth of bounding trees. (default=6, min=?, max=?)"))
{
}

#ifdef SOFA_DUMP_VISITOR_INFO
typedef simulation::Visitor::ctime_t ctime_t;
#endif

void DefaultPipeline::init()
{
    if(d_depth.getValue() < 0)
    {
        msg_warning() << "Invalid value 'depth'="<<d_depth.getValue() << "." << msgendl
                      << "Replaced with the default value = 6." ;
        d_depth.setValue(6) ;
    }
}

void DefaultPipeline::doCollisionReset()
{
    msg_info_when(d_doPrintInfoMessage.getValue())
            << "DefaultPipeline::doCollisionReset" ;

    core::objectmodel::BaseContext* scene = getContext();
    // clear all contacts
    if (contactManager!=nullptr)
    {
        const helper::vector<Contact::SPtr>& contacts = contactManager->getContacts();
        for (helper::vector<Contact::SPtr>::const_iterator it = contacts.begin(); it!=contacts.end(); ++it)
        {
            (*it)->removeResponse();
        }
    }
    // clear all collision groups
    if (groupManager!=nullptr)
    {
        groupManager->clearGroups(scene);
    }
}

void DefaultPipeline::doCollisionDetection(const helper::vector<core::CollisionModel*>& collisionModels)
{
    ScopedAdvancedTimer docollisiontimer("doCollisionDetection");

    msg_info_when(d_doPrintInfoMessage.getValue())
         << "doCollisionDetection, compute Bounding Trees" ;

    // First, we compute a bounding volume for the collision model (for example bounding sphere)
    // or we have loaded a collision model that knows its other model

    helper::vector<CollisionModel*> vectBoundingVolume;
    {
        ScopedAdvancedTimer bboxtimer("BBox");

#ifdef SOFA_DUMP_VISITOR_INFO
        simulation::Visitor::printNode("ComputeBoundingTree");
#endif
        const bool continuous = intersectionMethod->useContinuous();
        const SReal dt       = getContext()->getDt();

        helper::vector<CollisionModel*>::const_iterator it;
        const helper::vector<CollisionModel*>::const_iterator itEnd = collisionModels.end();
        int nActive = 0;

        for (it = collisionModels.begin(); it != itEnd; ++it)
        {
            msg_info_when(d_doPrintInfoMessage.getValue())
                << "doCollisionDetection, consider model" ;

            if (!(*it)->isActive()) continue;

            int used_depth = broadPhaseDetection->needsDeepBoundingTree() ? d_depth.getValue() : 0;

            if (continuous)
                (*it)->computeContinuousBoundingTree(dt, used_depth);
            else
                (*it)->computeBoundingTree(used_depth);

                vectBoundingVolume.push_back ((*it)->getFirst());
            ++nActive;
        }

#ifdef SOFA_DUMP_VISITOR_INFO
        simulation::Visitor::printCloseNode("ComputeBoundingTree");
#endif

        msg_info_when(d_doPrintInfoMessage.getValue())
                << "doCollisionDetection, Computed "<<nActive<<" BBoxs" ;
    }
    // then we start the broad phase
    if (broadPhaseDetection==nullptr) return; // can't go further

    msg_info_when(d_doPrintInfoMessage.getValue())
            << "doCollisionDetection, BroadPhaseDetection "<<broadPhaseDetection->getName();

#ifdef SOFA_DUMP_VISITOR_INFO
    simulation::Visitor::printNode("BroadPhase");
#endif
    {
        ScopedAdvancedTimer broadphase("BroadPhase");
        intersectionMethod->beginBroadPhase();
        broadPhaseDetection->beginBroadPhase();
        broadPhaseDetection->addCollisionModels(vectBoundingVolume);  // detection is done there
        broadPhaseDetection->endBroadPhase();
        intersectionMethod->endBroadPhase();
    }
#ifdef SOFA_DUMP_VISITOR_INFO
    simulation::Visitor::printCloseNode("BroadPhase");
#endif

    // then we start the narrow phase
    if (narrowPhaseDetection==nullptr) return; // can't go further

    msg_info_when(d_doPrintInfoMessage.getValue())
        << "doCollisionDetection, NarrowPhaseDetection "<<narrowPhaseDetection->getName();

#ifdef SOFA_DUMP_VISITOR_INFO
    simulation::Visitor::printNode("NarrowPhase");
#endif
    {
        ScopedAdvancedTimer narrowphase("NarrowPhase");
        intersectionMethod->beginNarrowPhase();
        narrowPhaseDetection->beginNarrowPhase();
        helper::vector<std::pair<CollisionModel*, CollisionModel*> >& vectCMPair = broadPhaseDetection->getCollisionModelPairs();

        msg_info_when(d_doPrintInfoMessage.getValue())
                << "doCollisionDetection, "<< vectCMPair.size()<<" colliding model pairs" ;

        narrowPhaseDetection->addCollisionPairs(vectCMPair);
        narrowPhaseDetection->endNarrowPhase();
        intersectionMethod->endNarrowPhase();
    }
#ifdef SOFA_DUMP_VISITOR_INFO
    simulation::Visitor::printCloseNode("NarrowPhase");
#endif

}

void DefaultPipeline::doCollisionResponse()
{
    core::objectmodel::BaseContext* scene = getContext();
    // then we start the creation of contacts
    if (contactManager==nullptr) return; // can't go further

    msg_info_when(d_doPrintInfoMessage.getValue())
        << "Create Contacts "<<contactManager->getName() ;

    contactManager->createContacts(narrowPhaseDetection->getDetectionOutputs());

    // finally we start the creation of collisionGroup

    const helper::vector<Contact::SPtr>& contacts = contactManager->getContacts();

    // First we remove all contacts with non-simulated objects and directly add them
    helper::vector<Contact::SPtr> notStaticContacts;

    for (helper::vector<Contact::SPtr>::const_iterator it = contacts.begin(); it!=contacts.end(); ++it)
    {
        Contact::SPtr c = *it;
        if (!c->getCollisionModels().first->isSimulated())
        {
            c->createResponse(c->getCollisionModels().second->getContext());
        }
        else if (!c->getCollisionModels().second->isSimulated())
        {
            c->createResponse(c->getCollisionModels().first->getContext());
        }
        else
            notStaticContacts.push_back(c);
    }

    if (groupManager==nullptr)
    {
        msg_info_when(d_doPrintInfoMessage.getValue())
            << "Linking all contacts to Scene" ;

        for (helper::vector<Contact::SPtr>::const_iterator it = notStaticContacts.begin(); it!=notStaticContacts.end(); ++it)
        {
            (*it)->createResponse(scene);
        }
    }
    else
    {
        msg_info_when(d_doPrintInfoMessage.getValue())
           << "Create Groups "<<groupManager->getName();

        groupManager->createGroups(scene, notStaticContacts);
    }
}

std::set< std::string > DefaultPipeline::getResponseList() const
{
    std::set< std::string > listResponse;
    core::collision::Contact::Factory::iterator it;
    for (it=core::collision::Contact::Factory::getInstance()->begin(); it!=core::collision::Contact::Factory::getInstance()->end(); ++it)
    {
        listResponse.insert(it->first);
    }
    return listResponse;
}

void DefaultPipeline::draw(const core::visual::VisualParams* )
{
    if (!d_doDebugDraw.getValue()) return;
    if (!narrowPhaseDetection) return;

//TODO(dmarchal): remove this code or reactivate or do a proper #ifdef
//TODO(dmarchal): it makes also no sense to keep a 'draw' attribute while nothing is displayed.
#if 0
    glDisable(GL_LIGHTING);
    glLineWidth(2);
    glBegin(GL_LINES);
    DetectionOutputMap& outputsMap = narrowPhaseDetection->getDetectionOutputs();
    for (DetectionOutputMap::iterator it = outputsMap.begin(); it!=outputsMap.end(); it++)
    {
        /*
        DetectionOutputVector& outputs = it->second;
        for (DetectionOutputVector::iterator it2 = outputs.begin(); it2!=outputs.end(); it2++)
        {
            DetectionOutput* d = &*it2;
            if (d->distance<0)
                glColor3f(1.0f,0.5f,0.5f);
            else
                glColor3f(0.5f,1.0f,0.5f);
            glVertex3dv(d->point[0].ptr());
            glVertex3dv(d->point[1].ptr());
        }
        */
    }
    glEnd();
    glLineWidth(1);
#endif
}
} // namespace collision

} // namespace component

} // namespace sofa

<commit_msg>[SofaKernel] DefaultPipeline.cpp fix invalid init that cause crashes<commit_after>/******************************************************************************
*       SOFA, Simulation Open-Framework Architecture, development version     *
*                (c) 2006-2017 INRIA, USTL, UJF, CNRS, MGH                    *
*                                                                             *
* This program is free software; you can redistribute it and/or modify it     *
* under the terms of the GNU Lesser General Public License as published by    *
* the Free Software Foundation; either version 2.1 of the License, or (at     *
* your option) any later version.                                             *
*                                                                             *
* This program is distributed in the hope that it will be useful, but WITHOUT *
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or       *
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License *
* for more details.                                                           *
*                                                                             *
* You should have received a copy of the GNU Lesser General Public License    *
* along with this program. If not, see <http://www.gnu.org/licenses/>.        *
*******************************************************************************
* Authors: The SOFA Team and external contributors (see Authors.txt)          *
*                                                                             *
* Contact information: contact@sofa-framework.org                             *
******************************************************************************/
#include <SofaBaseCollision/DefaultPipeline.h>

#include <sofa/core/CollisionModel.h>
#include <sofa/core/ObjectFactory.h>
#include <sofa/core/collision/BroadPhaseDetection.h>
#include <sofa/core/collision/NarrowPhaseDetection.h>
#include <sofa/core/collision/CollisionGroupManager.h>
#include <sofa/core/collision/ContactManager.h>
#include <sofa/core/visual/VisualParams.h>

#include <sofa/simulation/Node.h>

#ifdef SOFA_DUMP_VISITOR_INFO
#include <sofa/simulation/Visitor.h>
#endif

#include <sofa/helper/AdvancedTimer.h>
using sofa::helper::AdvancedTimer ;
using sofa::helper::ScopedAdvancedTimer ;

namespace sofa
{

namespace component
{

namespace collision
{

using namespace core;
using namespace core::objectmodel;
using namespace core::collision;
using namespace sofa::defaulttype;

SOFA_DECL_CLASS(DefaultPipeline)

int DefaultPipelineClass = core::RegisterObject("The default collision detection and modeling pipeline")
        .add< DefaultPipeline >()
        .addAlias("CollisionPipeline")
        ;

DefaultPipeline::DefaultPipeline()
    : d_doPrintInfoMessage(initData(&d_doPrintInfoMessage, false, "verbose",
                                    "Display extra informations at each computation step. (default=false)"))
    , d_doDebugDraw(initData(&d_doDebugDraw, false, "draw",
                             "Draw the detected collisions. (default=false)"))

    //TODO(dmarchal 2017-05-16) Fix the min & max value with response from a github issue. Remove in 1 year if not done.
    , d_depth(initData(&d_depth, 6, "depth",
                       "Max depth of bounding trees. (default=6, min=?, max=?)"))
{
}

#ifdef SOFA_DUMP_VISITOR_INFO
typedef simulation::Visitor::ctime_t ctime_t;
#endif

void DefaultPipeline::init()
{
    Inherit1::init() ;
    if(d_depth.getValue() < 0)
    {
        msg_warning() << "Invalid value 'depth'="<<d_depth.getValue() << "." << msgendl
                      << "Replaced with the default value = 6." ;
        d_depth.setValue(6) ;
    }
}

void DefaultPipeline::doCollisionReset()
{
    msg_info_when(d_doPrintInfoMessage.getValue())
            << "DefaultPipeline::doCollisionReset" ;

    core::objectmodel::BaseContext* scene = getContext();
    // clear all contacts
    if (contactManager!=nullptr)
    {
        const helper::vector<Contact::SPtr>& contacts = contactManager->getContacts();
        for (helper::vector<Contact::SPtr>::const_iterator it = contacts.begin(); it!=contacts.end(); ++it)
        {
            (*it)->removeResponse();
        }
    }
    // clear all collision groups
    if (groupManager!=nullptr)
    {
        groupManager->clearGroups(scene);
    }
}

void DefaultPipeline::doCollisionDetection(const helper::vector<core::CollisionModel*>& collisionModels)
{
    ScopedAdvancedTimer docollisiontimer("doCollisionDetection");

    msg_info_when(d_doPrintInfoMessage.getValue())
         << "doCollisionDetection, compute Bounding Trees" ;

    // First, we compute a bounding volume for the collision model (for example bounding sphere)
    // or we have loaded a collision model that knows its other model

    helper::vector<CollisionModel*> vectBoundingVolume;
    {
        ScopedAdvancedTimer bboxtimer("BBox");

#ifdef SOFA_DUMP_VISITOR_INFO
        simulation::Visitor::printNode("ComputeBoundingTree");
#endif
        const bool continuous = intersectionMethod->useContinuous();
        const SReal dt       = getContext()->getDt();

        helper::vector<CollisionModel*>::const_iterator it;
        const helper::vector<CollisionModel*>::const_iterator itEnd = collisionModels.end();
        int nActive = 0;

        for (it = collisionModels.begin(); it != itEnd; ++it)
        {
            msg_info_when(d_doPrintInfoMessage.getValue())
                << "doCollisionDetection, consider model" ;

            if (!(*it)->isActive()) continue;

            int used_depth = broadPhaseDetection->needsDeepBoundingTree() ? d_depth.getValue() : 0;

            if (continuous)
                (*it)->computeContinuousBoundingTree(dt, used_depth);
            else
                (*it)->computeBoundingTree(used_depth);

                vectBoundingVolume.push_back ((*it)->getFirst());
            ++nActive;
        }

#ifdef SOFA_DUMP_VISITOR_INFO
        simulation::Visitor::printCloseNode("ComputeBoundingTree");
#endif

        msg_info_when(d_doPrintInfoMessage.getValue())
                << "doCollisionDetection, Computed "<<nActive<<" BBoxs" ;
    }
    // then we start the broad phase
    if (broadPhaseDetection==nullptr) return; // can't go further

    msg_info_when(d_doPrintInfoMessage.getValue())
            << "doCollisionDetection, BroadPhaseDetection "<<broadPhaseDetection->getName();

#ifdef SOFA_DUMP_VISITOR_INFO
    simulation::Visitor::printNode("BroadPhase");
#endif
    {
        ScopedAdvancedTimer broadphase("BroadPhase");
        intersectionMethod->beginBroadPhase();
        broadPhaseDetection->beginBroadPhase();
        broadPhaseDetection->addCollisionModels(vectBoundingVolume);  // detection is done there
        broadPhaseDetection->endBroadPhase();
        intersectionMethod->endBroadPhase();
    }
#ifdef SOFA_DUMP_VISITOR_INFO
    simulation::Visitor::printCloseNode("BroadPhase");
#endif

    // then we start the narrow phase
    if (narrowPhaseDetection==nullptr) return; // can't go further

    msg_info_when(d_doPrintInfoMessage.getValue())
        << "doCollisionDetection, NarrowPhaseDetection "<<narrowPhaseDetection->getName();

#ifdef SOFA_DUMP_VISITOR_INFO
    simulation::Visitor::printNode("NarrowPhase");
#endif
    {
        ScopedAdvancedTimer narrowphase("NarrowPhase");
        intersectionMethod->beginNarrowPhase();
        narrowPhaseDetection->beginNarrowPhase();
        helper::vector<std::pair<CollisionModel*, CollisionModel*> >& vectCMPair = broadPhaseDetection->getCollisionModelPairs();

        msg_info_when(d_doPrintInfoMessage.getValue())
                << "doCollisionDetection, "<< vectCMPair.size()<<" colliding model pairs" ;

        narrowPhaseDetection->addCollisionPairs(vectCMPair);
        narrowPhaseDetection->endNarrowPhase();
        intersectionMethod->endNarrowPhase();
    }
#ifdef SOFA_DUMP_VISITOR_INFO
    simulation::Visitor::printCloseNode("NarrowPhase");
#endif

}

void DefaultPipeline::doCollisionResponse()
{
    core::objectmodel::BaseContext* scene = getContext();
    // then we start the creation of contacts
    if (contactManager==nullptr) return; // can't go further

    msg_info_when(d_doPrintInfoMessage.getValue())
        << "Create Contacts "<<contactManager->getName() ;

    contactManager->createContacts(narrowPhaseDetection->getDetectionOutputs());

    // finally we start the creation of collisionGroup

    const helper::vector<Contact::SPtr>& contacts = contactManager->getContacts();

    // First we remove all contacts with non-simulated objects and directly add them
    helper::vector<Contact::SPtr> notStaticContacts;

    for (helper::vector<Contact::SPtr>::const_iterator it = contacts.begin(); it!=contacts.end(); ++it)
    {
        Contact::SPtr c = *it;
        if (!c->getCollisionModels().first->isSimulated())
        {
            c->createResponse(c->getCollisionModels().second->getContext());
        }
        else if (!c->getCollisionModels().second->isSimulated())
        {
            c->createResponse(c->getCollisionModels().first->getContext());
        }
        else
            notStaticContacts.push_back(c);
    }

    if (groupManager==nullptr)
    {
        msg_info_when(d_doPrintInfoMessage.getValue())
            << "Linking all contacts to Scene" ;

        for (helper::vector<Contact::SPtr>::const_iterator it = notStaticContacts.begin(); it!=notStaticContacts.end(); ++it)
        {
            (*it)->createResponse(scene);
        }
    }
    else
    {
        msg_info_when(d_doPrintInfoMessage.getValue())
           << "Create Groups "<<groupManager->getName();

        groupManager->createGroups(scene, notStaticContacts);
    }
}

std::set< std::string > DefaultPipeline::getResponseList() const
{
    std::set< std::string > listResponse;
    core::collision::Contact::Factory::iterator it;
    for (it=core::collision::Contact::Factory::getInstance()->begin(); it!=core::collision::Contact::Factory::getInstance()->end(); ++it)
    {
        listResponse.insert(it->first);
    }
    return listResponse;
}

void DefaultPipeline::draw(const core::visual::VisualParams* )
{
    if (!d_doDebugDraw.getValue()) return;
    if (!narrowPhaseDetection) return;

//TODO(dmarchal 2017-05-17): remove this code or reactivate or do a proper #ifdef
//TODO(dmarchal): it makes also no sense to keep a 'draw' attribute while nothing is displayed.
#if 0
    glDisable(GL_LIGHTING);
    glLineWidth(2);
    glBegin(GL_LINES);
    DetectionOutputMap& outputsMap = narrowPhaseDetection->getDetectionOutputs();
    for (DetectionOutputMap::iterator it = outputsMap.begin(); it!=outputsMap.end(); it++)
    {
        /*
        DetectionOutputVector& outputs = it->second;
        for (DetectionOutputVector::iterator it2 = outputs.begin(); it2!=outputs.end(); it2++)
        {
            DetectionOutput* d = &*it2;
            if (d->distance<0)
                glColor3f(1.0f,0.5f,0.5f);
            else
                glColor3f(0.5f,1.0f,0.5f);
            glVertex3dv(d->point[0].ptr());
            glVertex3dv(d->point[1].ptr());
        }
        */
    }
    glEnd();
    glLineWidth(1);
#endif
}
} // namespace collision

} // namespace component

} // namespace sofa

<|endoftext|>


<commit_before>/*
 Copyright (c) 2014, The Cinder Project

 This code is intended to be used with the Cinder C++ library, http://libcinder.org

 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.

 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED
 WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
 PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
 ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
 TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 POSSIBILITY OF SUCH DAMAGE.
 */

#include "cinder/audio/Node.h"
#include "cinder/audio/DelayNode.h"
#include "cinder/audio/Context.h"
#include "cinder/audio/dsp/Dsp.h"
#include "cinder/audio/dsp/Converter.h"
#include "cinder/audio/Debug.h"
#include "cinder/CinderAssert.h"
#include "cinder/System.h"

#include <limits>

using namespace std;

namespace cinder { namespace audio {

// ----------------------------------------------------------------------------------------------------
// MARK: - Node
// ----------------------------------------------------------------------------------------------------

Node::Node( const Format &format )
	: mInitialized( false ), mEnabled( false ),	mChannelMode( format.getChannelMode() ),
		mNumChannels( 1 ), mAutoEnabled( true ), mProcessInPlace( true ), mLastProcessedFrame( numeric_limits<uint64_t>::max() )
{
	if( format.getChannels() ) {
		mNumChannels = format.getChannels();
		mChannelMode = ChannelMode::SPECIFIED;
	}

	if( ! boost::indeterminate( format.getAutoEnable() ) )
		setAutoEnabled( format.getAutoEnable() );
}

Node::~Node()
{
}

void Node::connect( const NodeRef &output )
{
	// make a reference to ourselves so that we aren't deallocated in the case of the last owner
	// disconnecting us, which we may need later anyway
	NodeRef thisRef = shared_from_this();

	if( ! output->canConnectToInput( thisRef ) )
		return;

	if( checkCycle( thisRef, output ) )
		throw NodeCycleExc( thisRef, output );

	mOutputs.push_back( output ); // set output first, so that it is visible in configureConnections()
	output->connectInput( thisRef );

	output->notifyConnectionsDidChange();
}

void Node::disconnect( const NodeRef &output )
{
	if( ! output )
		return;

	for( auto weakOutIt = mOutputs.begin(); weakOutIt != mOutputs.end(); ++weakOutIt ) {
		if( weakOutIt->lock() == output ) {
			mOutputs.erase( weakOutIt );
			break;
		}
	}

	output->disconnectInput( shared_from_this() );
	output->notifyConnectionsDidChange();
}

void Node::disconnectAll()
{
	disconnectAllInputs();
	disconnectAllOutputs();
}

void Node::disconnectAllOutputs()
{
	NodeRef thisRef = shared_from_this();

	auto outputs = getOutputs(); // first make a copy of only the still-alive NodeRef's
	for( const auto &output : outputs )
		disconnect( output );
}

void Node::disconnectAllInputs()
{
	NodeRef thisRef = shared_from_this();

	for( auto &input : mInputs )
		input->disconnectOutput( thisRef );

	mInputs.clear();
	notifyConnectionsDidChange();
}

void Node::connectInput( const NodeRef &input )
{
	lock_guard<mutex> lock( getContext()->getMutex() );

	mInputs.insert( input );
	configureConnections();
}

void Node::disconnectInput( const NodeRef &input )
{
	lock_guard<mutex> lock( getContext()->getMutex() );

	for( auto inIt = mInputs.begin(); inIt != mInputs.end(); ++inIt ) {
		if( *inIt == input ) {
			mInputs.erase( inIt );
			break;
		}
	}
}

void Node::disconnectOutput( const NodeRef &output )
{
	lock_guard<mutex> lock( getContext()->getMutex() );

	for( auto outIt = mOutputs.begin(); outIt != mOutputs.end(); ++outIt ) {
		if( outIt->lock() == output ) {
			mOutputs.erase( outIt );
			break;
		}
	}
}

vector<NodeRef> Node::getOutputs() const
{
	vector<NodeRef> result;
	for( const weak_ptr<Node> &outputWeak : mOutputs ) {
		NodeRef output = outputWeak.lock();
		if( output )
			result.push_back( output );
	}

	return result;
}

void Node::enable()
{
	if( ! mInitialized )
		initializeImpl();

	if( mEnabled )
		return;

	mEnabled = true;
	enableProcessing();
}

void Node::disable()
{
	if( ! mEnabled )
		return;

	mEnabled = false;
	disableProcessing();
}

void Node::setEnabled( bool b )
{
	if( b )
		enable();
	else
		disable();
}

size_t Node::getNumConnectedInputs() const
{
	return mInputs.size();
}

size_t Node::getNumConnectedOutputs() const
{
	return mOutputs.size();
}

void Node::initializeImpl()
{
	if( mInitialized )
		return;

	if( mProcessInPlace && ! supportsProcessInPlace() )
		setupProcessWithSumming();

	initialize();
	mInitialized = true;

	if( mAutoEnabled )
		enable();
}

void Node::uninitializeImpl()
{
	if( ! mInitialized )
		return;

	disable();
	uninitialize();
	mInitialized = false;
}

void Node::setNumChannels( size_t numChannels )
{
	if( mNumChannels == numChannels )
		return;

	uninitializeImpl();
	mNumChannels = numChannels;
}

void Node::setChannelMode( ChannelMode mode )
{
	CI_ASSERT_MSG( ! mInitialized, "setting the channel mode must be done before initializing" );

	if( mChannelMode == mode )
		return;

	mChannelMode = mode;
}

size_t Node::getMaxNumInputChannels() const
{
	size_t result = 0;
	for( auto &in : mInputs )
		result = max( result, in->getNumChannels() );

	return result;
}

size_t Node::getSampleRate() const
{
	return getContext()->getSampleRate();
}

size_t Node::getFramesPerBlock() const
{
	return getContext()->getFramesPerBlock();
}

// TODO: Checking for DelayNode below is a kludge and will not work for other types that want to support feedback.
//		 With more investigation it might be possible to avoid this, or at least define some interface that
//       specifies whether this input needs to be summed.
void Node::configureConnections()
{
	CI_ASSERT( getContext() );

	mProcessInPlace = supportsProcessInPlace();

	if( getNumConnectedInputs() > 1 || getNumConnectedOutputs() > 1 )
		mProcessInPlace = false;

	bool isDelay = ( dynamic_cast<DelayNode *>( this ) != nullptr ); // see note above

	for( auto &input : mInputs ) {
		bool inputProcessInPlace = true;

		size_t inputNumChannels = input->getNumChannels();
		if( ! supportsInputNumChannels( inputNumChannels ) ) {
			if( mChannelMode == ChannelMode::MATCHES_INPUT )
				setNumChannels( getMaxNumInputChannels() );
			else if( input->getChannelMode() == ChannelMode::MATCHES_OUTPUT ) {
				input->setNumChannels( mNumChannels );
				input->configureConnections();
			}
			else {
				mProcessInPlace = false;
				inputProcessInPlace = false;
			}
		}

		// inputs with more than one output cannot process in-place, so make them sum
		if( input->getProcessInPlace() && input->getNumConnectedOutputs() > 1 )
			inputProcessInPlace = false;

		// if we're unable to process in-place and we're a DelayNode, its possible that the input may be part of a feedback loop, in which case input must sum.
		if( ! mProcessInPlace && isDelay )
			inputProcessInPlace = false;

		if( ! inputProcessInPlace )
			input->setupProcessWithSumming();

		input->initializeImpl();
	}

	for( auto &out : mOutputs ) {
		NodeRef output = out.lock();
		if( ! output )
			continue;
		if( ! output->supportsInputNumChannels( mNumChannels ) ) {
			if( output->getChannelMode() == ChannelMode::MATCHES_INPUT ) {
				output->setNumChannels( mNumChannels );
				output->configureConnections();
			}
			else
				mProcessInPlace = false;
		}
	}

	if( ! mProcessInPlace )
		setupProcessWithSumming();

	initializeImpl();
}

void Node::pullInputs( Buffer *inPlaceBuffer )
{
	CI_ASSERT( getContext() );

	if( mProcessInPlace ) {
		if( mInputs.empty() ) {
			// Fastest route: no inputs and process in-place. If disabled, get rid of any previously processsed samples.
			if( mEnabled )
				process( inPlaceBuffer );
			else
				inPlaceBuffer->zero();
		}
		else {
			// First pull the input (can only be one when in-place), then run process() if input did any processing.
			const NodeRef &input = *mInputs.begin();
			input->pullInputs( inPlaceBuffer );

			if( ! input->getProcessInPlace() )
				dsp::mixBuffers( input->getInternalBuffer(), inPlaceBuffer );

			if( mEnabled )
				process( inPlaceBuffer );
		}
	}
	else {
		// Pull and sum all enabled inputs. Only do this once per processing block, which is checked by the current number of processed frames.
		uint64_t numProcessedFrames = getContext()->getNumProcessedFrames();
		if( mLastProcessedFrame != numProcessedFrames ) {
			mLastProcessedFrame = numProcessedFrames;

			mSummingBuffer.zero();
			sumInputs();
		}
	}
}

void Node::sumInputs()
{
	// Pull all inputs, summing the results from the buffer that input used for processing.
	// mInternalBuffer is not zero'ed before pulling inputs to allow for feedback.
	for( auto &input : mInputs ) {
		input->pullInputs( &mInternalBuffer );
		const Buffer *processedBuffer = input->getProcessInPlace() ? &mInternalBuffer : input->getInternalBuffer();
		dsp::sumBuffers( processedBuffer, &mSummingBuffer );
	}

	// Process the summed results if enabled.
	if( mEnabled )
		process( &mSummingBuffer );

	// copy summed buffer back to internal so downstream can get it.
	dsp::mixBuffers( &mSummingBuffer, &mInternalBuffer );
}

void Node::setupProcessWithSumming()
{
	CI_ASSERT( getContext() );

	mProcessInPlace = false;
	size_t framesPerBlock = getFramesPerBlock();

	mInternalBuffer.setSize( framesPerBlock, mNumChannels );
	mSummingBuffer.setSize( framesPerBlock, mNumChannels );
}

bool Node::checkCycle( const NodeRef &sourceNode, const NodeRef &destNode ) const
{
	if( sourceNode == destNode )
		return true;

	if( sourceNode->supportsCycles() || destNode->supportsCycles() )
		return false;

	for( const auto &input : sourceNode->getInputs() ) {
		if( checkCycle( input, destNode ) )
			return true;
	}

	return false;
}

void Node::notifyConnectionsDidChange()
{
	getContext()->connectionsDidChange( shared_from_this() );
}

bool Node::canConnectToInput( const NodeRef &input )
{
	if( ! input || input == shared_from_this() )
		return false;

	for( const auto& in : mInputs )
		if( input == in )
			return false;

	return true;
}

std::string Node::getName()
{
	return ! mName.empty() ? mName : System::demangleTypeName( typeid( *this ).name() );
}

// ----------------------------------------------------------------------------------------------------
// MARK: - NodeAutoPullable
// ----------------------------------------------------------------------------------------------------

NodeAutoPullable::NodeAutoPullable( const Format &format )
	: Node( format ), mIsPulledByContext( false )
{
}

NodeAutoPullable::~NodeAutoPullable()
{
}

void NodeAutoPullable::connect( const NodeRef &output )
{
	Node::connect( output );
	updatePullMethod();
}

void NodeAutoPullable::connectInput( const NodeRef &input )
{
	Node::connectInput( input );
	updatePullMethod();
}

void NodeAutoPullable::disconnectInput( const NodeRef &input )
{
	Node::disconnectInput( input );
	updatePullMethod();
}

void NodeAutoPullable::disconnectAllOutputs()
{
	Node::disconnectAllOutputs();

	if( mIsPulledByContext ) {
		mIsPulledByContext = false;
		getContext()->removeAutoPulledNode( shared_from_this() );
	}
}

void NodeAutoPullable::updatePullMethod()
{
	bool hasOutputs = ! getOutputs().empty();
	if( ! hasOutputs && ! mIsPulledByContext ) {
		mIsPulledByContext = true;
		getContext()->addAutoPulledNode( shared_from_this() );
	}
	else if( hasOutputs && mIsPulledByContext ) {
		mIsPulledByContext = false;
		getContext()->removeAutoPulledNode( shared_from_this() );
	}
}

// ----------------------------------------------------------------------------------------------------
// MARK: - ScopedEnableNode
// ----------------------------------------------------------------------------------------------------

ScopedEnableNode::ScopedEnableNode( const NodeRef &node )
	: mNode( node )
{
	mWasEnabled = ( mNode ? mNode->isEnabled() : false );
}

ScopedEnableNode::ScopedEnableNode( const NodeRef &node, bool enable )
	: mNode( node )
{
	if( mNode ) {
		mWasEnabled = mNode->isEnabled();
		mNode->setEnabled( enable );
	}
	else
		mWasEnabled = false;
}

ScopedEnableNode::~ScopedEnableNode()
{
	if( mNode )
		mNode->setEnabled( mWasEnabled );
}

// ----------------------------------------------------------------------------------------------------
// MARK: - Exceptions
// ----------------------------------------------------------------------------------------------------

NodeCycleExc::NodeCycleExc( const NodeRef &sourceNode, const NodeRef &destNode )
	: AudioExc( string( "cyclical connection between source: " ) + sourceNode->getName() + string( " and dest: " ) + destNode->getName() )
{
}

} } // namespace cinder::audio
<commit_msg>reverting back to only disable() from uninit if isAutoEnabled() == true.  This allows for context-wide re-initializing and the current state of the graph to be completely restored (otherwise, non-auto-enabled Node’s will be unknowingly disabled())<commit_after>/*
 Copyright (c) 2014, The Cinder Project

 This code is intended to be used with the Cinder C++ library, http://libcinder.org

 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.

 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED
 WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
 PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
 ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
 TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 POSSIBILITY OF SUCH DAMAGE.
 */

#include "cinder/audio/Node.h"
#include "cinder/audio/DelayNode.h"
#include "cinder/audio/Context.h"
#include "cinder/audio/dsp/Dsp.h"
#include "cinder/audio/dsp/Converter.h"
#include "cinder/audio/Debug.h"
#include "cinder/CinderAssert.h"
#include "cinder/System.h"

#include <limits>

using namespace std;

namespace cinder { namespace audio {

// ----------------------------------------------------------------------------------------------------
// MARK: - Node
// ----------------------------------------------------------------------------------------------------

Node::Node( const Format &format )
	: mInitialized( false ), mEnabled( false ),	mChannelMode( format.getChannelMode() ),
		mNumChannels( 1 ), mAutoEnabled( true ), mProcessInPlace( true ), mLastProcessedFrame( numeric_limits<uint64_t>::max() )
{
	if( format.getChannels() ) {
		mNumChannels = format.getChannels();
		mChannelMode = ChannelMode::SPECIFIED;
	}

	if( ! boost::indeterminate( format.getAutoEnable() ) )
		setAutoEnabled( format.getAutoEnable() );
}

Node::~Node()
{
}

void Node::connect( const NodeRef &output )
{
	// make a reference to ourselves so that we aren't deallocated in the case of the last owner
	// disconnecting us, which we may need later anyway
	NodeRef thisRef = shared_from_this();

	if( ! output->canConnectToInput( thisRef ) )
		return;

	if( checkCycle( thisRef, output ) )
		throw NodeCycleExc( thisRef, output );

	mOutputs.push_back( output ); // set output first, so that it is visible in configureConnections()
	output->connectInput( thisRef );

	output->notifyConnectionsDidChange();
}

void Node::disconnect( const NodeRef &output )
{
	if( ! output )
		return;

	for( auto weakOutIt = mOutputs.begin(); weakOutIt != mOutputs.end(); ++weakOutIt ) {
		if( weakOutIt->lock() == output ) {
			mOutputs.erase( weakOutIt );
			break;
		}
	}

	output->disconnectInput( shared_from_this() );
	output->notifyConnectionsDidChange();
}

void Node::disconnectAll()
{
	disconnectAllInputs();
	disconnectAllOutputs();
}

void Node::disconnectAllOutputs()
{
	NodeRef thisRef = shared_from_this();

	auto outputs = getOutputs(); // first make a copy of only the still-alive NodeRef's
	for( const auto &output : outputs )
		disconnect( output );
}

void Node::disconnectAllInputs()
{
	NodeRef thisRef = shared_from_this();

	for( auto &input : mInputs )
		input->disconnectOutput( thisRef );

	mInputs.clear();
	notifyConnectionsDidChange();
}

void Node::connectInput( const NodeRef &input )
{
	lock_guard<mutex> lock( getContext()->getMutex() );

	mInputs.insert( input );
	configureConnections();
}

void Node::disconnectInput( const NodeRef &input )
{
	lock_guard<mutex> lock( getContext()->getMutex() );

	for( auto inIt = mInputs.begin(); inIt != mInputs.end(); ++inIt ) {
		if( *inIt == input ) {
			mInputs.erase( inIt );
			break;
		}
	}
}

void Node::disconnectOutput( const NodeRef &output )
{
	lock_guard<mutex> lock( getContext()->getMutex() );

	for( auto outIt = mOutputs.begin(); outIt != mOutputs.end(); ++outIt ) {
		if( outIt->lock() == output ) {
			mOutputs.erase( outIt );
			break;
		}
	}
}

vector<NodeRef> Node::getOutputs() const
{
	vector<NodeRef> result;
	for( const weak_ptr<Node> &outputWeak : mOutputs ) {
		NodeRef output = outputWeak.lock();
		if( output )
			result.push_back( output );
	}

	return result;
}

void Node::enable()
{
	if( ! mInitialized )
		initializeImpl();

	if( mEnabled )
		return;

	mEnabled = true;
	enableProcessing();
}

void Node::disable()
{
	if( ! mEnabled )
		return;

	mEnabled = false;
	disableProcessing();
}

void Node::setEnabled( bool b )
{
	if( b )
		enable();
	else
		disable();
}

size_t Node::getNumConnectedInputs() const
{
	return mInputs.size();
}

size_t Node::getNumConnectedOutputs() const
{
	return mOutputs.size();
}

void Node::initializeImpl()
{
	if( mInitialized )
		return;

	if( mProcessInPlace && ! supportsProcessInPlace() )
		setupProcessWithSumming();

	initialize();
	mInitialized = true;

	if( mAutoEnabled )
		enable();
}

void Node::uninitializeImpl()
{
	if( ! mInitialized )
		return;

	if( mAutoEnabled )
		disable();

	uninitialize();
	mInitialized = false;
}

void Node::setNumChannels( size_t numChannels )
{
	if( mNumChannels == numChannels )
		return;

	uninitializeImpl();
	mNumChannels = numChannels;
}

void Node::setChannelMode( ChannelMode mode )
{
	CI_ASSERT_MSG( ! mInitialized, "setting the channel mode must be done before initializing" );

	if( mChannelMode == mode )
		return;

	mChannelMode = mode;
}

size_t Node::getMaxNumInputChannels() const
{
	size_t result = 0;
	for( auto &in : mInputs )
		result = max( result, in->getNumChannels() );

	return result;
}

size_t Node::getSampleRate() const
{
	return getContext()->getSampleRate();
}

size_t Node::getFramesPerBlock() const
{
	return getContext()->getFramesPerBlock();
}

// TODO: Checking for DelayNode below is a kludge and will not work for other types that want to support feedback.
//		 With more investigation it might be possible to avoid this, or at least define some interface that
//       specifies whether this input needs to be summed.
void Node::configureConnections()
{
	CI_ASSERT( getContext() );

	mProcessInPlace = supportsProcessInPlace();

	if( getNumConnectedInputs() > 1 || getNumConnectedOutputs() > 1 )
		mProcessInPlace = false;

	bool isDelay = ( dynamic_cast<DelayNode *>( this ) != nullptr ); // see note above

	for( auto &input : mInputs ) {
		bool inputProcessInPlace = true;

		size_t inputNumChannels = input->getNumChannels();
		if( ! supportsInputNumChannels( inputNumChannels ) ) {
			if( mChannelMode == ChannelMode::MATCHES_INPUT )
				setNumChannels( getMaxNumInputChannels() );
			else if( input->getChannelMode() == ChannelMode::MATCHES_OUTPUT ) {
				input->setNumChannels( mNumChannels );
				input->configureConnections();
			}
			else {
				mProcessInPlace = false;
				inputProcessInPlace = false;
			}
		}

		// inputs with more than one output cannot process in-place, so make them sum
		if( input->getProcessInPlace() && input->getNumConnectedOutputs() > 1 )
			inputProcessInPlace = false;

		// if we're unable to process in-place and we're a DelayNode, its possible that the input may be part of a feedback loop, in which case input must sum.
		if( ! mProcessInPlace && isDelay )
			inputProcessInPlace = false;

		if( ! inputProcessInPlace )
			input->setupProcessWithSumming();

		input->initializeImpl();
	}

	for( auto &out : mOutputs ) {
		NodeRef output = out.lock();
		if( ! output )
			continue;
		if( ! output->supportsInputNumChannels( mNumChannels ) ) {
			if( output->getChannelMode() == ChannelMode::MATCHES_INPUT ) {
				output->setNumChannels( mNumChannels );
				output->configureConnections();
			}
			else
				mProcessInPlace = false;
		}
	}

	if( ! mProcessInPlace )
		setupProcessWithSumming();

	initializeImpl();
}

void Node::pullInputs( Buffer *inPlaceBuffer )
{
	CI_ASSERT( getContext() );

	if( mProcessInPlace ) {
		if( mInputs.empty() ) {
			// Fastest route: no inputs and process in-place. If disabled, get rid of any previously processsed samples.
			if( mEnabled )
				process( inPlaceBuffer );
			else
				inPlaceBuffer->zero();
		}
		else {
			// First pull the input (can only be one when in-place), then run process() if input did any processing.
			const NodeRef &input = *mInputs.begin();
			input->pullInputs( inPlaceBuffer );

			if( ! input->getProcessInPlace() )
				dsp::mixBuffers( input->getInternalBuffer(), inPlaceBuffer );

			if( mEnabled )
				process( inPlaceBuffer );
		}
	}
	else {
		// Pull and sum all enabled inputs. Only do this once per processing block, which is checked by the current number of processed frames.
		uint64_t numProcessedFrames = getContext()->getNumProcessedFrames();
		if( mLastProcessedFrame != numProcessedFrames ) {
			mLastProcessedFrame = numProcessedFrames;

			mSummingBuffer.zero();
			sumInputs();
		}
	}
}

void Node::sumInputs()
{
	// Pull all inputs, summing the results from the buffer that input used for processing.
	// mInternalBuffer is not zero'ed before pulling inputs to allow for feedback.
	for( auto &input : mInputs ) {
		input->pullInputs( &mInternalBuffer );
		const Buffer *processedBuffer = input->getProcessInPlace() ? &mInternalBuffer : input->getInternalBuffer();
		dsp::sumBuffers( processedBuffer, &mSummingBuffer );
	}

	// Process the summed results if enabled.
	if( mEnabled )
		process( &mSummingBuffer );

	// copy summed buffer back to internal so downstream can get it.
	dsp::mixBuffers( &mSummingBuffer, &mInternalBuffer );
}

void Node::setupProcessWithSumming()
{
	CI_ASSERT( getContext() );

	mProcessInPlace = false;
	size_t framesPerBlock = getFramesPerBlock();

	mInternalBuffer.setSize( framesPerBlock, mNumChannels );
	mSummingBuffer.setSize( framesPerBlock, mNumChannels );
}

bool Node::checkCycle( const NodeRef &sourceNode, const NodeRef &destNode ) const
{
	if( sourceNode == destNode )
		return true;

	if( sourceNode->supportsCycles() || destNode->supportsCycles() )
		return false;

	for( const auto &input : sourceNode->getInputs() ) {
		if( checkCycle( input, destNode ) )
			return true;
	}

	return false;
}

void Node::notifyConnectionsDidChange()
{
	getContext()->connectionsDidChange( shared_from_this() );
}

bool Node::canConnectToInput( const NodeRef &input )
{
	if( ! input || input == shared_from_this() )
		return false;

	for( const auto& in : mInputs )
		if( input == in )
			return false;

	return true;
}

std::string Node::getName()
{
	return ! mName.empty() ? mName : System::demangleTypeName( typeid( *this ).name() );
}

// ----------------------------------------------------------------------------------------------------
// MARK: - NodeAutoPullable
// ----------------------------------------------------------------------------------------------------

NodeAutoPullable::NodeAutoPullable( const Format &format )
	: Node( format ), mIsPulledByContext( false )
{
}

NodeAutoPullable::~NodeAutoPullable()
{
}

void NodeAutoPullable::connect( const NodeRef &output )
{
	Node::connect( output );
	updatePullMethod();
}

void NodeAutoPullable::connectInput( const NodeRef &input )
{
	Node::connectInput( input );
	updatePullMethod();
}

void NodeAutoPullable::disconnectInput( const NodeRef &input )
{
	Node::disconnectInput( input );
	updatePullMethod();
}

void NodeAutoPullable::disconnectAllOutputs()
{
	Node::disconnectAllOutputs();

	if( mIsPulledByContext ) {
		mIsPulledByContext = false;
		getContext()->removeAutoPulledNode( shared_from_this() );
	}
}

void NodeAutoPullable::updatePullMethod()
{
	bool hasOutputs = ! getOutputs().empty();
	if( ! hasOutputs && ! mIsPulledByContext ) {
		mIsPulledByContext = true;
		getContext()->addAutoPulledNode( shared_from_this() );
	}
	else if( hasOutputs && mIsPulledByContext ) {
		mIsPulledByContext = false;
		getContext()->removeAutoPulledNode( shared_from_this() );
	}
}

// ----------------------------------------------------------------------------------------------------
// MARK: - ScopedEnableNode
// ----------------------------------------------------------------------------------------------------

ScopedEnableNode::ScopedEnableNode( const NodeRef &node )
	: mNode( node )
{
	mWasEnabled = ( mNode ? mNode->isEnabled() : false );
}

ScopedEnableNode::ScopedEnableNode( const NodeRef &node, bool enable )
	: mNode( node )
{
	if( mNode ) {
		mWasEnabled = mNode->isEnabled();
		mNode->setEnabled( enable );
	}
	else
		mWasEnabled = false;
}

ScopedEnableNode::~ScopedEnableNode()
{
	if( mNode )
		mNode->setEnabled( mWasEnabled );
}

// ----------------------------------------------------------------------------------------------------
// MARK: - Exceptions
// ----------------------------------------------------------------------------------------------------

NodeCycleExc::NodeCycleExc( const NodeRef &sourceNode, const NodeRef &destNode )
	: AudioExc( string( "cyclical connection between source: " ) + sourceNode->getName() + string( " and dest: " ) + destNode->getName() )
{
}

} } // namespace cinder::audio
<|endoftext|>