<commit_before>//===----------------------------------------------------------------------===//
//
//                         Peloton
//
// optimizer_sql_test.cpp
//
// Identification: test/sql/optimizer_sql_test.cpp
//
// Copyright (c) 2015-16, Carnegie Mellon University Database Group
//
//===----------------------------------------------------------------------===//

#include <memory>

#include "sql/testing_sql_util.h"
#include "catalog/catalog.h"
#include "common/harness.h"
#include "executor/create_executor.h"
#include "optimizer/optimizer.h"
#include "optimizer/simple_optimizer.h"
#include "planner/create_plan.h"
#include "planner/order_by_plan.h"

using std::vector;
using std::unordered_set;
using std::string;
using std::unique_ptr;
using std::shared_ptr;

namespace peloton {
namespace test {

class OptimizerSQLTests : public PelotonTest {
 protected:
  virtual void SetUp() override {
    // Call parent virtual function first
    PelotonTest::SetUp();

    // Create test database
    CreateAndLoadTable();
    optimizer.reset(new optimizer::Optimizer());
  }

  virtual void TearDown() override {
    // Destroy test database
    auto &txn_manager = concurrency::TransactionManagerFactory::GetInstance();
    auto txn = txn_manager.BeginTransaction();
    catalog::Catalog::GetInstance()->DropDatabaseWithName(DEFAULT_DB_NAME, txn);
    txn_manager.CommitTransaction(txn);

    // Call parent virtual function
    PelotonTest::TearDown();
  }

  /*** Helper functions **/
  void CreateAndLoadTable() {
    // Create database
    catalog::Catalog::GetInstance()->CreateDatabase(DEFAULT_DB_NAME, nullptr);

    // Create a table first
    TestingSQLUtil::ExecuteSQLQuery(
        "CREATE TABLE test(a INT PRIMARY KEY, b INT, c INT);");

    // Insert tuples into table
    TestingSQLUtil::ExecuteSQLQuery("INSERT INTO test VALUES (1, 22, 333);");
    TestingSQLUtil::ExecuteSQLQuery("INSERT INTO test VALUES (2, 11, 000);");
    TestingSQLUtil::ExecuteSQLQuery("INSERT INTO test VALUES (3, 33, 444);");
    TestingSQLUtil::ExecuteSQLQuery("INSERT INTO test VALUES (4, 00, 555);");
  }

  void TestUtil(string query, vector<string> ref_result, bool ordered,
                vector<PlanNodeType> expected_plans={}) {
    LOG_DEBUG("Running Query \"%s\"", query.c_str());

    // Check Plan Nodes are correct if provided
    if (expected_plans.size() > 0) {
      auto plan = TestingSQLUtil::GeneratePlanWithOptimizer(optimizer, query);
      auto plan_ptr = plan.get();
      vector<PlanNodeType> actual_plans;
      while (true) {
        actual_plans.push_back(plan_ptr->GetPlanNodeType());
        if (plan_ptr->GetChildren().size() == 0)
          break;
        plan_ptr = plan_ptr->GetChildren()[0].get();
      }
      EXPECT_EQ(actual_plans, expected_plans);
    }

    // Check plan execution results are correct
    TestingSQLUtil::ExecuteSQLQueryWithOptimizer(optimizer, query, result,
                                                 tuple_descriptor, rows_changed,
                                                 error_message);
    EXPECT_EQ(ref_result.size(), result.size());
    if (ordered) {
      // If deterministic, do comparision with expected result in order
      for (unsigned i = 0; i < ref_result.size(); i++) {
        EXPECT_EQ(ref_result[i],
                  TestingSQLUtil::GetResultValueAsString(result, i));
      }
    } else {
      // If non-deterministic, make sure they have the same set of value
      unordered_set<string> ref_set(ref_result.begin(), ref_result.end());
      for (unsigned i = 0; i < result.size(); i++) {
        string result_str = TestingSQLUtil::GetResultValueAsString(result, i);
        EXPECT_TRUE(ref_set.find(result_str) != ref_set.end());
      }
    }
  }

 protected:
  unique_ptr<optimizer::AbstractOptimizer> optimizer;
  vector<StatementResult> result;
  vector<FieldInfo> tuple_descriptor;
  string error_message;
  int rows_changed;
};

TEST_F(OptimizerSQLTests, SimpleSelectTest) {
  // Testing select star expression
  TestUtil("SELECT * from test", {"333", "22", "1", "2", "11", "0", "3", "33",
                                  "444", "4", "0", "555"}, false);

  // Testing predicate
  TestUtil("SELECT c, b from test where a=1", {"333", "22"}, false);
}

TEST_F(OptimizerSQLTests, SelectOrderByTest) {
  // Testing order by columns different from select columns
  TestUtil("SELECT b from test order by c", {"11", "22", "33", "0"}, true);

  // Testing order by desc
  TestUtil("SELECT a from test order by c desc", {"4", "3", "1", "2"}, true);

  // Testing order by complex expression
  TestUtil("SELECT * from test order by a + c", {"2", "11", "0", "1", "22",
                                                 "333", "3", "33", "444", "4", "0", "555"}, true);
}

TEST_F(OptimizerSQLTests, SelectLimitTest) {
  // Test limit with default offset
  TestUtil("SELECT b FROM test ORDER BY b LIMIT 3", {"0", "11", "22"}, true);

  // Test limit with offset
  TestUtil("SELECT b FROM test ORDER BY b LIMIT 2 OFFSET 2", {"22", "33"}, true);
}

TEST_F(OptimizerSQLTests, SelectProjectionTest) {
  // Test complex expression projection
  TestUtil("SELECT a * 5 + b, -1 + c from test", {"27", "332", "48", "443",
                                                  "21", "-1", "20", "554"}, false);

  // Test complex expression in select and order by
  TestUtil("SELECT a * 5 + b - c FROM test ORDER BY a * 10 + b", {"21", "-306",
                                                                  "-535", "-396"}, true);

  // Test mixing up select simple columns with complex expression
  TestUtil("SELECT a, a + c FROM test ORDER BY a * 3 * b DESC, b + c / 5 ASC",
           {"3", "447", "2", "2", "1", "334", "4", "559"}, true);
}

TEST_F(OptimizerSQLTests, DeleteSqlTest) {
  // TODO: Test for index scan

  // Delete with predicates
  string query = "DELETE FROM test WHERE a = 1 and c = 333";
  TestingSQLUtil::ExecuteSQLQueryWithOptimizer(
      optimizer, query, result, tuple_descriptor, rows_changed, error_message);
  EXPECT_EQ(1, rows_changed);
  TestingSQLUtil::ExecuteSQLQueryWithOptimizer(optimizer, "SELECT * FROM test",
                                               result, tuple_descriptor,
                                               rows_changed, error_message);
  EXPECT_EQ(9, result.size());

  // Delete with predicates
  query = "DELETE FROM test WHERE b = 33";
  TestingSQLUtil::ExecuteSQLQueryWithOptimizer(
      optimizer, query, result, tuple_descriptor, rows_changed, error_message);
  EXPECT_EQ(1, rows_changed);
  TestingSQLUtil::ExecuteSQLQueryWithOptimizer(optimizer, "SELECT * FROM test",
                                               result, tuple_descriptor,
                                               rows_changed, error_message);
  EXPECT_EQ(6, result.size());

  // Delete with false predicates
  query = "DELETE FROM test WHERE b = 123";
  TestingSQLUtil::ExecuteSQLQueryWithOptimizer(
      optimizer, query, result, tuple_descriptor, rows_changed, error_message);
  EXPECT_EQ(0, rows_changed);
  TestingSQLUtil::ExecuteSQLQueryWithOptimizer(optimizer, "SELECT * FROM test",
                                               result, tuple_descriptor,
                                               rows_changed, error_message);
  EXPECT_EQ(6, result.size());

  // Full deletion
  query = "DELETE FROM test";
  TestingSQLUtil::ExecuteSQLQueryWithOptimizer(
      optimizer, query, result, tuple_descriptor, rows_changed, error_message);
  EXPECT_EQ(2, rows_changed);
  TestingSQLUtil::ExecuteSQLQueryWithOptimizer(optimizer, "SELECT * FROM test",
                                               result, tuple_descriptor,
                                               rows_changed, error_message);
  EXPECT_EQ(0, result.size());
}

TEST_F(OptimizerSQLTests, UpdateSqlTest) {
  // Test Update with complex expression and predicate
  string query = "UPDATE test SET c = b + 1 WHERE a = 1";
  TestingSQLUtil::ExecuteSQLQueryWithOptimizer(
      optimizer, query, result, tuple_descriptor, rows_changed, error_message);
  EXPECT_EQ(1, rows_changed);
  TestUtil("SELECT c FROM test WHERE a=1", {"23"}, false);
}

TEST_F(OptimizerSQLTests, InsertSqlTest) {
  string query = "INSERT INTO test VALUES (5, 55, 555);";
  TestingSQLUtil::ExecuteSQLQueryWithOptimizer(
      optimizer, query, result, tuple_descriptor, rows_changed, error_message);
  EXPECT_EQ(1, rows_changed);

  // Test the tuple is succesfully inserted
  TestUtil("SELECT * FROM test WHERE a=5", {"5", "55", "555"}, false);
}

TEST_F(OptimizerSQLTests, DDLSqlTest) {
  // Test creating new table
  string query = "CREATE TABLE test2(a INT PRIMARY KEY, b INT, c INT);";
  TestingSQLUtil::ExecuteSQLQueryWithOptimizer(
      optimizer, query, result, tuple_descriptor, rows_changed, error_message);

  auto table = catalog::Catalog::GetInstance()->GetTableWithName(
      DEFAULT_DB_NAME, "test2");
  EXPECT_NE(nullptr, table);
  auto cols = table->GetSchema()->GetColumns();
  EXPECT_EQ(3, cols.size());
  EXPECT_EQ("a", cols[0].column_name);
  EXPECT_EQ(true, cols[0].is_primary_);
  EXPECT_EQ(type::Type::INTEGER, cols[0].GetType());
  EXPECT_EQ("b", cols[1].column_name);
  EXPECT_EQ(type::Type::INTEGER, cols[1].GetType());
  EXPECT_EQ("c", cols[2].column_name);
  EXPECT_EQ(type::Type::INTEGER, cols[2].GetType());

  // Test dropping existing table
  query = "DROP TABLE test2";
  TestingSQLUtil::ExecuteSQLQueryWithOptimizer(
      optimizer, query, result, tuple_descriptor, rows_changed, error_message);
  try {
    catalog::Catalog::GetInstance()->GetTableWithName(DEFAULT_DB_NAME, "test2");
    EXPECT_TRUE(false);
  } catch (Exception &e) {
    LOG_INFO("Correct! Exception(%s) catched", e.what());
  }
}

TEST_F(OptimizerSQLTests, GroupByTest) {
  // Insert additional tuples to test group by
  TestingSQLUtil::ExecuteSQLQuery("INSERT INTO test VALUES (5, 11, 000);");
  TestingSQLUtil::ExecuteSQLQuery("INSERT INTO test VALUES (6, 22, 333);");

  // Test basic case
  TestUtil("SELECT b FROM test GROUP BY b having b=11 or b=22", {"22", "11"},
           false);

  // Test Aggregate function: COUNT(*)
  TestUtil("SELECT COUNT(*) FROM test GROUP BY b", {"1", "1", "2", "2"}, false);

  // Test Aggregate function: COUNT(a)
  TestUtil("SELECT COUNT(a) FROM test GROUP BY b", {"1", "1", "2", "2"}, false);

  // Test basic case
  TestUtil("SELECT b FROM test GROUP BY b having b=11 or b=22", {"22", "11"},
           false);

  // Test Aggregate function: COUNT(*)
  TestUtil("SELECT COUNT(*) FROM test GROUP BY b", {"1", "1", "2", "2"}, false);

  // Test Aggregate function: COUNT(a)
  TestUtil("SELECT COUNT(a) FROM test GROUP BY b", {"1", "1", "2", "2"}, false);

  // Test group by with having
  TestUtil("SELECT AVG(a), b FROM test GROUP BY b having b=22", {"3.5", "22"},
           false);

  // Test Aggregate function: MIN(b)
  TestUtil("SELECT MIN(a), b FROM test GROUP BY b having b=22", {"1", "22"},
           false);

  // Test Aggregate function: MAX(b)
  TestUtil("SELECT MAX(a), b FROM test GROUP BY b having b=22", {"6", "22"},
           false);

  // Test group by combined with ORDER BY
  TestUtil("SELECT b FROM test GROUP BY b ORDER BY b", {"0", "11", "22", "33"},
           true);

  // Test complex expression in aggregation
  TestUtil("SELECT b, MAX(a + c) FROM test GROUP BY b ORDER BY b",
           {"0", "559", "11", "5", "22", "339", "33", "447"}, true);

  // Test complex expression in select list and order by complex expr
  TestUtil("SELECT b + c, SUM(c * a) FROM test GROUP BY b,c ORDER BY b + c",
           {"11", "0", "355", "2331", "477", "1332", "555", "2220"}, true);

  // Test Plain aggregation without group by
  TestUtil("SELECT SUM(c * a) FROM test", {"5883"}, false);
}

TEST_F(OptimizerSQLTests, SelectDistinctTest) {
//  TestingSQLUtil::ExecuteSQLQuery("INSERT INTO test VALUES (1, 22, 333);");
//  TestingSQLUtil::ExecuteSQLQuery("INSERT INTO test VALUES (2, 11, 000);");
//  TestingSQLUtil::ExecuteSQLQuery("INSERT INTO test VALUES (3, 33, 444);");
//  TestingSQLUtil::ExecuteSQLQuery("INSERT INTO test VALUES (4, 00, 555);");
  TestingSQLUtil::ExecuteSQLQuery("INSERT INTO test VALUES (5, 00, 555);");
  TestingSQLUtil::ExecuteSQLQuery("INSERT INTO test VALUES (6, 22, 333);");

  // Test DISTINCT and GROUP BY have the same columns. Avoid additional HashPlan
  TestUtil("SELECT DISTINCT b,c FROM test GROUP BY b,c",
           {"0", "555", "33", "444", "11", "0", "22", "333"}, false,
           {PlanNodeType::AGGREGATE_V2, PlanNodeType::SEQSCAN});

  // Test GROUP BY cannot satisfied DISTINCT
  TestUtil("SELECT DISTINCT b FROM test GROUP BY b,c",
           {"22", "11", "0", "33"}, false, {PlanNodeType::HASH,
             PlanNodeType::AGGREGATE_V2, PlanNodeType::SEQSCAN});

  TestingSQLUtil::ExecuteSQLQuery("INSERT INTO test VALUES (7, 00, 444);");

  // Test distinct with order by
  TestUtil("SELECT DISTINCT b FROM test ORDER BY b", {"0", "11", "22", "33"},
           true);

  // Test distinct with complex order by
  TestUtil("SELECT DISTINCT b, c FROM test ORDER BY 10 * b + c", {"11", "0",
                                                                  "0", "444", "22", "333", "0", "555", "33", "444"}, true);

  // Test distinct with limit and star expression
  TestUtil("SELECT DISTINCT * FROM test ORDER BY a + 10 * b + c LIMIT 3",
           {"2", "11", "0", "7", "0", "444", "1", "22", "333"}, true);

  // Insert additional tuples to test distinct with group by
  TestingSQLUtil::ExecuteSQLQuery("INSERT INTO test VALUES (5, 11, 000);");
  TestingSQLUtil::ExecuteSQLQuery("INSERT INTO test VALUES (6, 22, 333);");

  // Test distinct and group by complex expression
  TestUtil("SELECT DISTINCT b + c FROM test GROUP BY b + c ORDER BY b + c",
           {"11", "355", "444", "477", "555"}, true);
}
}  // namespace test
}  // namespace peloton
<commit_msg>Change TestUtil to output more information<commit_after>//===----------------------------------------------------------------------===//
//
//                         Peloton
//
// optimizer_sql_test.cpp
//
// Identification: test/sql/optimizer_sql_test.cpp
//
// Copyright (c) 2015-16, Carnegie Mellon University Database Group
//
//===----------------------------------------------------------------------===//

#include <memory>

#include "sql/testing_sql_util.h"
#include "catalog/catalog.h"
#include "common/harness.h"
#include "executor/create_executor.h"
#include "optimizer/optimizer.h"
#include "optimizer/simple_optimizer.h"
#include "planner/create_plan.h"
#include "planner/order_by_plan.h"

using std::vector;
using std::unordered_set;
using std::string;
using std::unique_ptr;
using std::shared_ptr;

namespace peloton {
namespace test {

class OptimizerSQLTests : public PelotonTest {
 protected:
  virtual void SetUp() override {
    // Call parent virtual function first
    PelotonTest::SetUp();

    // Create test database
    CreateAndLoadTable();
    optimizer.reset(new optimizer::Optimizer());
  }

  virtual void TearDown() override {
    // Destroy test database
    auto &txn_manager = concurrency::TransactionManagerFactory::GetInstance();
    auto txn = txn_manager.BeginTransaction();
    catalog::Catalog::GetInstance()->DropDatabaseWithName(DEFAULT_DB_NAME, txn);
    txn_manager.CommitTransaction(txn);

    // Call parent virtual function
    PelotonTest::TearDown();
  }

  /*** Helper functions **/
  void CreateAndLoadTable() {
    // Create database
    catalog::Catalog::GetInstance()->CreateDatabase(DEFAULT_DB_NAME, nullptr);

    // Create a table first
    TestingSQLUtil::ExecuteSQLQuery(
        "CREATE TABLE test(a INT PRIMARY KEY, b INT, c INT);");

    // Insert tuples into table
    TestingSQLUtil::ExecuteSQLQuery("INSERT INTO test VALUES (1, 22, 333);");
    TestingSQLUtil::ExecuteSQLQuery("INSERT INTO test VALUES (2, 11, 000);");
    TestingSQLUtil::ExecuteSQLQuery("INSERT INTO test VALUES (3, 33, 444);");
    TestingSQLUtil::ExecuteSQLQuery("INSERT INTO test VALUES (4, 00, 555);");
  }

  void TestUtil(string query, vector<string> ref_result, bool ordered,
                vector<PlanNodeType> expected_plans={}) {
    LOG_DEBUG("Running Query \"%s\"", query.c_str());

    // Check Plan Nodes are correct if provided
    if (expected_plans.size() > 0) {
      auto plan = TestingSQLUtil::GeneratePlanWithOptimizer(optimizer, query);
      auto plan_ptr = plan.get();
      vector<PlanNodeType> actual_plans;
      while (true) {
        actual_plans.push_back(plan_ptr->GetPlanNodeType());
        if (plan_ptr->GetChildren().size() == 0)
          break;
        plan_ptr = plan_ptr->GetChildren()[0].get();
      }
      EXPECT_EQ(actual_plans, expected_plans);
    }

    // Check plan execution results are correct
    TestingSQLUtil::ExecuteSQLQueryWithOptimizer(optimizer, query, result,
                                                 tuple_descriptor, rows_changed,
                                                 error_message);
    EXPECT_EQ(ref_result.size(), result.size());
    vector<string> actual_result;
    if (ordered) {
      // If deterministic, do comparision with expected result in order
      for (unsigned i = 0; i < result.size(); i++)
        actual_result.push_back(TestingSQLUtil::GetResultValueAsString(result, i));
      EXPECT_EQ(actual_result, ref_result);
    } else {
      // If non-deterministic, make sure they have the same set of value
      unordered_set<string> ref_set(ref_result.begin(), ref_result.end());
      for (unsigned i = 0; i < result.size(); i++) {
        string result_str = TestingSQLUtil::GetResultValueAsString(result, i);
        EXPECT_TRUE(ref_set.find(result_str) != ref_set.end());
      }
    }
  }

 protected:
  unique_ptr<optimizer::AbstractOptimizer> optimizer;
  vector<StatementResult> result;
  vector<FieldInfo> tuple_descriptor;
  string error_message;
  int rows_changed;
};

TEST_F(OptimizerSQLTests, SimpleSelectTest) {
  // Testing select star expression
  TestUtil("SELECT * from test", {"333", "22", "1", "2", "11", "0", "3", "33",
                                  "444", "4", "0", "555"}, false);

  // Testing predicate
  TestUtil("SELECT c, b from test where a=1", {"333", "22"}, false);
}

TEST_F(OptimizerSQLTests, SelectOrderByTest) {
  // Testing order by columns different from select columns
  TestUtil("SELECT b from test order by c", {"11", "22", "33", "0"}, true);

  // Testing order by desc
  TestUtil("SELECT a from test order by c desc", {"4", "3", "1", "2"}, true);

  // Testing order by complex expression
  TestUtil("SELECT * from test order by a + c", {"2", "11", "0", "1", "22",
                                                 "333", "3", "33", "444", "4", "0", "555"}, true);
}

TEST_F(OptimizerSQLTests, SelectLimitTest) {
  // Test limit with default offset
  TestUtil("SELECT b FROM test ORDER BY b LIMIT 3", {"0", "11", "22"}, true);

  // Test limit with offset
  TestUtil("SELECT b FROM test ORDER BY b LIMIT 2 OFFSET 2", {"22", "33"}, true);
}

TEST_F(OptimizerSQLTests, SelectProjectionTest) {
  // Test complex expression projection
  TestUtil("SELECT a * 5 + b, -1 + c from test", {"27", "332", "48", "443",
                                                  "21", "-1", "20", "554"}, false);

  // Test complex expression in select and order by
  TestUtil("SELECT a * 5 + b - c FROM test ORDER BY a * 10 + b", {"21", "-306",
                                                                  "-535", "-396"}, true);

  // Test mixing up select simple columns with complex expression
  TestUtil("SELECT a, a + c FROM test ORDER BY a * 3 * b DESC, b + c / 5 ASC",
           {"3", "447", "2", "2", "1", "334", "4", "559"}, true);
}

TEST_F(OptimizerSQLTests, DeleteSqlTest) {
  // TODO: Test for index scan

  // Delete with predicates
  string query = "DELETE FROM test WHERE a = 1 and c = 333";
  TestingSQLUtil::ExecuteSQLQueryWithOptimizer(
      optimizer, query, result, tuple_descriptor, rows_changed, error_message);
  EXPECT_EQ(1, rows_changed);
  TestingSQLUtil::ExecuteSQLQueryWithOptimizer(optimizer, "SELECT * FROM test",
                                               result, tuple_descriptor,
                                               rows_changed, error_message);
  EXPECT_EQ(9, result.size());

  // Delete with predicates
  query = "DELETE FROM test WHERE b = 33";
  TestingSQLUtil::ExecuteSQLQueryWithOptimizer(
      optimizer, query, result, tuple_descriptor, rows_changed, error_message);
  EXPECT_EQ(1, rows_changed);
  TestingSQLUtil::ExecuteSQLQueryWithOptimizer(optimizer, "SELECT * FROM test",
                                               result, tuple_descriptor,
                                               rows_changed, error_message);
  EXPECT_EQ(6, result.size());

  // Delete with false predicates
  query = "DELETE FROM test WHERE b = 123";
  TestingSQLUtil::ExecuteSQLQueryWithOptimizer(
      optimizer, query, result, tuple_descriptor, rows_changed, error_message);
  EXPECT_EQ(0, rows_changed);
  TestingSQLUtil::ExecuteSQLQueryWithOptimizer(optimizer, "SELECT * FROM test",
                                               result, tuple_descriptor,
                                               rows_changed, error_message);
  EXPECT_EQ(6, result.size());

  // Full deletion
  query = "DELETE FROM test";
  TestingSQLUtil::ExecuteSQLQueryWithOptimizer(
      optimizer, query, result, tuple_descriptor, rows_changed, error_message);
  EXPECT_EQ(2, rows_changed);
  TestingSQLUtil::ExecuteSQLQueryWithOptimizer(optimizer, "SELECT * FROM test",
                                               result, tuple_descriptor,
                                               rows_changed, error_message);
  EXPECT_EQ(0, result.size());
}

TEST_F(OptimizerSQLTests, UpdateSqlTest) {
  // Test Update with complex expression and predicate
  string query = "UPDATE test SET c = b + 1 WHERE a = 1";
  TestingSQLUtil::ExecuteSQLQueryWithOptimizer(
      optimizer, query, result, tuple_descriptor, rows_changed, error_message);
  EXPECT_EQ(1, rows_changed);
  TestUtil("SELECT c FROM test WHERE a=1", {"23"}, false);
}

TEST_F(OptimizerSQLTests, InsertSqlTest) {
  string query = "INSERT INTO test VALUES (5, 55, 555);";
  TestingSQLUtil::ExecuteSQLQueryWithOptimizer(
      optimizer, query, result, tuple_descriptor, rows_changed, error_message);
  EXPECT_EQ(1, rows_changed);

  // Test the tuple is succesfully inserted
  TestUtil("SELECT * FROM test WHERE a=5", {"5", "55", "555"}, false);
}

TEST_F(OptimizerSQLTests, DDLSqlTest) {
  // Test creating new table
  string query = "CREATE TABLE test2(a INT PRIMARY KEY, b INT, c INT);";
  TestingSQLUtil::ExecuteSQLQueryWithOptimizer(
      optimizer, query, result, tuple_descriptor, rows_changed, error_message);

  auto table = catalog::Catalog::GetInstance()->GetTableWithName(
      DEFAULT_DB_NAME, "test2");
  EXPECT_NE(nullptr, table);
  auto cols = table->GetSchema()->GetColumns();
  EXPECT_EQ(3, cols.size());
  EXPECT_EQ("a", cols[0].column_name);
  EXPECT_EQ(true, cols[0].is_primary_);
  EXPECT_EQ(type::Type::INTEGER, cols[0].GetType());
  EXPECT_EQ("b", cols[1].column_name);
  EXPECT_EQ(type::Type::INTEGER, cols[1].GetType());
  EXPECT_EQ("c", cols[2].column_name);
  EXPECT_EQ(type::Type::INTEGER, cols[2].GetType());

  // Test dropping existing table
  query = "DROP TABLE test2";
  TestingSQLUtil::ExecuteSQLQueryWithOptimizer(
      optimizer, query, result, tuple_descriptor, rows_changed, error_message);
  try {
    catalog::Catalog::GetInstance()->GetTableWithName(DEFAULT_DB_NAME, "test2");
    EXPECT_TRUE(false);
  } catch (Exception &e) {
    LOG_INFO("Correct! Exception(%s) catched", e.what());
  }
}

TEST_F(OptimizerSQLTests, GroupByTest) {
  // Insert additional tuples to test group by
  TestingSQLUtil::ExecuteSQLQuery("INSERT INTO test VALUES (5, 11, 000);");
  TestingSQLUtil::ExecuteSQLQuery("INSERT INTO test VALUES (6, 22, 333);");

  // Test basic case
  TestUtil("SELECT b FROM test GROUP BY b having b=11 or b=22", {"22", "11"},
           false);

  // Test Aggregate function: COUNT(*)
  TestUtil("SELECT COUNT(*) FROM test GROUP BY b", {"1", "1", "2", "2"}, false);

  // Test Aggregate function: COUNT(a)
  TestUtil("SELECT COUNT(a) FROM test GROUP BY b", {"1", "1", "2", "2"}, false);

  // Test basic case
  TestUtil("SELECT b FROM test GROUP BY b having b=11 or b=22", {"22", "11"},
           false);

  // Test Aggregate function: COUNT(*)
  TestUtil("SELECT COUNT(*) FROM test GROUP BY b", {"1", "1", "2", "2"}, false);

  // Test Aggregate function: COUNT(a)
  TestUtil("SELECT COUNT(a) FROM test GROUP BY b", {"1", "1", "2", "2"}, false);

  // Test group by with having
  TestUtil("SELECT AVG(a), b FROM test GROUP BY b having b=22", {"3.5", "22"},
           false);

  // Test Aggregate function: MIN(b)
  TestUtil("SELECT MIN(a), b FROM test GROUP BY b having b=22", {"1", "22"},
           false);

  // Test Aggregate function: MAX(b)
  TestUtil("SELECT MAX(a), b FROM test GROUP BY b having b=22", {"6", "22"},
           false);

  // Test group by combined with ORDER BY
  TestUtil("SELECT b FROM test GROUP BY b ORDER BY b", {"0", "11", "22", "33"},
           true);

  // Test complex expression in aggregation
  TestUtil("SELECT b, MAX(a + c) FROM test GROUP BY b ORDER BY b",
           {"0", "559", "11", "5", "22", "339", "33", "447"}, true);

  // Test complex expression in select list and order by complex expr
  TestUtil("SELECT b + c, SUM(c * a) FROM test GROUP BY b,c ORDER BY b + c",
           {"11", "0", "355", "2331", "477", "1332", "555", "2220"}, true);

  // Test Plain aggregation without group by
  TestUtil("SELECT SUM(c * a) FROM test", {"5883"}, false);

  // Test ORDER BY columns not shown in select list
  TestUtil("SELECT a FROM test GROUP BY a,b ORDER BY a + b",
           {"4", "2", "5", "1", "6", "3"}, true, {PlanNodeType::ORDERBY,
             PlanNodeType::AGGREGATE_V2, PlanNodeType::SEQSCAN});
}

TEST_F(OptimizerSQLTests, SelectDistinctTest) {
//  TestingSQLUtil::ExecuteSQLQuery("INSERT INTO test VALUES (1, 22, 333);");
//  TestingSQLUtil::ExecuteSQLQuery("INSERT INTO test VALUES (2, 11, 000);");
//  TestingSQLUtil::ExecuteSQLQuery("INSERT INTO test VALUES (3, 33, 444);");
//  TestingSQLUtil::ExecuteSQLQuery("INSERT INTO test VALUES (4, 00, 555);");
  TestingSQLUtil::ExecuteSQLQuery("INSERT INTO test VALUES (5, 00, 555);");
  TestingSQLUtil::ExecuteSQLQuery("INSERT INTO test VALUES (6, 22, 333);");

  // Test DISTINCT and GROUP BY have the same columns. Avoid additional HashPlan
  TestUtil("SELECT DISTINCT b,c FROM test GROUP BY b,c",
           {"0", "555", "33", "444", "11", "0", "22", "333"}, false,
           {PlanNodeType::AGGREGATE_V2, PlanNodeType::SEQSCAN});

  // Test GROUP BY cannot satisfied DISTINCT
  TestUtil("SELECT DISTINCT b FROM test GROUP BY b,c",
           {"22", "11", "0", "33"}, false, {PlanNodeType::HASH,
             PlanNodeType::AGGREGATE_V2, PlanNodeType::SEQSCAN});

  TestingSQLUtil::ExecuteSQLQuery("INSERT INTO test VALUES (7, 00, 444);");

  // Test distinct with order by
  TestUtil("SELECT DISTINCT b FROM test ORDER BY b", {"0", "11", "22", "33"},
           true);

  // Test distinct with complex order by
  TestUtil("SELECT DISTINCT b, c FROM test ORDER BY 10 * b + c", {"11", "0",
                                                                  "0", "444", "22", "333", "0", "555", "33", "444"}, true);

  // Test distinct with limit and star expression
  TestUtil("SELECT DISTINCT * FROM test ORDER BY a + 10 * b + c LIMIT 3",
           {"2", "11", "0", "7", "0", "444", "1", "22", "333"}, true);

  // Insert additional tuples to test distinct with group by
  TestingSQLUtil::ExecuteSQLQuery("INSERT INTO test VALUES (5, 11, 000);");
  TestingSQLUtil::ExecuteSQLQuery("INSERT INTO test VALUES (6, 22, 333);");

  // Test distinct and group by complex expression
  TestUtil("SELECT DISTINCT b + c FROM test GROUP BY b + c ORDER BY b + c",
           {"11", "355", "444", "477", "555"}, true);
}
}  // namespace test
}  // namespace peloton
<|endoftext|>


<commit_before>
#include <togo/config.hpp>
#include <togo/types.hpp>
#include <togo/error/assert.hpp>
#include <togo/utility/utility.hpp>
#include <togo/utility/tags.hpp>
#include <togo/utility/traits.hpp>
#include <togo/utility/constraints.hpp>
#include <togo/utility/args.hpp>
#include <togo/math/types.hpp>
#include <togo/math/math.hpp>
#include <togo/log/log.hpp>
#include <togo/log/test.hpp>
#include <togo/log/test_unconfigure.hpp>
#include <togo/memory/types.hpp>
#include <togo/memory/memory.hpp>
#include <togo/memory/jump_block_allocator.hpp>
#include <togo/memory/temp_allocator.hpp>
#include <togo/collection/types.hpp>
#include <togo/collection/fixed_array.hpp>
#include <togo/collection/array.hpp>
#include <togo/collection/queue.hpp>
#include <togo/collection/priority_queue.hpp>
#include <togo/collection/hash_map.hpp>
#include <togo/algorithm/sort.hpp>
#include <togo/string/types.hpp>
#include <togo/string/string.hpp>
#include <togo/hash/types.hpp>
#include <togo/hash/hash.hpp>
#include <togo/hash/hash_combiner.hpp>
#include <togo/system/system.hpp>
#include <togo/filesystem/types.hpp>
#include <togo/filesystem/filesystem.hpp>
#include <togo/filesystem/directory_reader.hpp>
#include <togo/random/types.hpp>
#include <togo/random/random.hpp>
#include <togo/threading/types.hpp>
#include <togo/threading/condvar.hpp>
#include <togo/threading/mutex.hpp>
#include <togo/threading/thread.hpp>
#include <togo/threading/task_manager.hpp>
#include <togo/io/types.hpp>
#include <togo/io/io.hpp>
#include <togo/io/proto.hpp>
#include <togo/io/memory_stream.hpp>
#include <togo/io/file_stream.hpp>
#include <togo/io/object_buffer_type.hpp>
#include <togo/io/object_buffer.hpp>
#include <togo/kvs/types.hpp>
#include <togo/kvs/kvs.hpp>
#include <togo/entity/types.hpp>
#include <togo/entity/entity_manager.hpp>
#include <togo/world/types.hpp>
#include <togo/world/world_manager.hpp>
#include <togo/gfx/types.hpp>
#include <togo/gfx/gfx.hpp>
#include <togo/gfx/gfx/common.hpp>
#include <togo/gfx/display.hpp>
#include <togo/gfx/display/types.hpp>
#include <togo/gfx/renderer.hpp>
#include <togo/gfx/renderer/types.hpp>
#include <togo/gfx/shader_def.hpp>
#include <togo/input/types.hpp>
#include <togo/input/input.hpp>
#include <togo/input/input_buffer.hpp>
#include <togo/resource/types.hpp>
#include <togo/resource/resource.hpp>
#include <togo/resource/resource_handler.hpp>
#include <togo/resource/resource_package.hpp>
#include <togo/resource/resource_manager.hpp>
#include <togo/serialization/types.hpp>
#include <togo/serialization/serializer.hpp>
#include <togo/serialization/support.hpp>
#include <togo/serialization/binary_serializer.hpp>
#include <togo/serialization/fixed_array.hpp>
#include <togo/serialization/array.hpp>
#include <togo/serialization/string.hpp>
#include <togo/serialization/resource/resource_metadata.hpp>
#include <togo/serialization/resource/test_resource.hpp>
#include <togo/serialization/gfx/shader_def.hpp>
#include <togo/serialization/gfx/render_config.hpp>
#include <togo/app/types.hpp>
#include <togo/app/app.hpp>
#include <togo/external/dlmalloc_import.hpp>

signed main() {
	return 0;
}
<commit_msg>test/general/headers: updated.<commit_after>
#include <togo/config.hpp>
#include <togo/types.hpp>
#include <togo/error/assert.hpp>
#include <togo/utility/utility.hpp>
#include <togo/utility/tags.hpp>
#include <togo/utility/traits.hpp>
#include <togo/utility/constraints.hpp>
#include <togo/utility/args.hpp>
#include <togo/math/types.hpp>
#include <togo/math/math.hpp>
#include <togo/log/log.hpp>
#include <togo/log/test.hpp>
#include <togo/log/test_unconfigure.hpp>
#include <togo/memory/types.hpp>
#include <togo/memory/memory.hpp>
#include <togo/memory/jump_block_allocator.hpp>
#include <togo/memory/temp_allocator.hpp>
#include <togo/collection/types.hpp>
#include <togo/collection/fixed_array.hpp>
#include <togo/collection/array.hpp>
#include <togo/collection/queue.hpp>
#include <togo/collection/priority_queue.hpp>
#include <togo/collection/hash_map.hpp>
#include <togo/algorithm/sort.hpp>
#include <togo/string/types.hpp>
#include <togo/string/string.hpp>
#include <togo/hash/types.hpp>
#include <togo/hash/hash.hpp>
#include <togo/hash/hash_combiner.hpp>
#include <togo/system/system.hpp>
#include <togo/filesystem/types.hpp>
#include <togo/filesystem/filesystem.hpp>
#include <togo/filesystem/directory_reader.hpp>
#include <togo/random/types.hpp>
#include <togo/random/random.hpp>
#include <togo/threading/types.hpp>
#include <togo/threading/condvar.hpp>
#include <togo/threading/mutex.hpp>
#include <togo/threading/thread.hpp>
#include <togo/threading/task_manager.hpp>
#include <togo/io/types.hpp>
#include <togo/io/io.hpp>
#include <togo/io/proto.hpp>
#include <togo/io/memory_stream.hpp>
#include <togo/io/file_stream.hpp>
#include <togo/io/object_buffer_type.hpp>
#include <togo/io/object_buffer.hpp>
#include <togo/kvs/types.hpp>
#include <togo/kvs/kvs.hpp>
#include <togo/entity/types.hpp>
#include <togo/entity/entity_manager.hpp>
#include <togo/world/types.hpp>
#include <togo/world/world_manager.hpp>
#include <togo/gfx/types.hpp>
#include <togo/gfx/gfx.hpp>
#include <togo/gfx/gfx/common.hpp>
#include <togo/gfx/display.hpp>
#include <togo/gfx/display/types.hpp>
#include <togo/gfx/command.hpp>
#include <togo/gfx/generator.hpp>
#include <togo/gfx/render_node.hpp>
#include <togo/gfx/renderer.hpp>
#include <togo/gfx/renderer/types.hpp>
#include <togo/gfx/shader_def.hpp>
#include <togo/input/types.hpp>
#include <togo/input/input.hpp>
#include <togo/input/input_buffer.hpp>
#include <togo/resource/types.hpp>
#include <togo/resource/resource.hpp>
#include <togo/resource/resource_handler.hpp>
#include <togo/resource/resource_package.hpp>
#include <togo/resource/resource_manager.hpp>
#include <togo/serialization/types.hpp>
#include <togo/serialization/serializer.hpp>
#include <togo/serialization/support.hpp>
#include <togo/serialization/binary_serializer.hpp>
#include <togo/serialization/fixed_array.hpp>
#include <togo/serialization/array.hpp>
#include <togo/serialization/string.hpp>
#include <togo/serialization/resource/resource_metadata.hpp>
#include <togo/serialization/resource/test_resource.hpp>
#include <togo/serialization/gfx/shader_def.hpp>
#include <togo/serialization/gfx/render_config.hpp>
#include <togo/app/types.hpp>
#include <togo/app/app.hpp>
#include <togo/external/dlmalloc_import.hpp>

signed main() {
	return 0;
}
<|endoftext|>


<commit_before>/**********************************************************************
 * $Id$
 *
 * GEOS - Geometry Engine Open Source
 * http://geos.refractions.net
 *
 * Copyright (C) 2001-2002 Vivid Solutions Inc.
 *
 * This is free software; you can redistribute and/or modify it under
 * the terms of the GNU Lesser General Public Licence as published
 * by the Free Software Foundation.
 * See the COPYING file for more information.
 *
 **********************************************************************
 * $Log$
 * Revision 1.27  2004/05/03 17:15:38  strk
 * leaks on exception fixed.
 *
 * Revision 1.26  2004/04/23 00:02:18  strk
 * const-correctness changes
 *
 * Revision 1.25  2004/04/20 13:24:15  strk
 * More leaks removed.
 *
 * Revision 1.24  2004/04/20 10:14:20  strk
 * Memory leaks removed.
 *
 * Revision 1.23  2004/04/19 15:14:46  strk
 * Added missing virtual destructor in SpatialIndex class.
 * Memory leaks fixes. Const and throw specifications added.
 *
 * Revision 1.22  2004/04/19 12:51:01  strk
 * Memory leaks fixes. Throw specifications added.
 *
 * Revision 1.21  2004/04/16 14:09:17  strk
 * Leaks fixes
 *
 * Revision 1.20  2004/04/16 11:04:24  strk
 * Memory leaks plugged on exception thrown
 *
 * Revision 1.19  2004/04/16 10:00:08  strk
 * Memory leak fixed.
 *
 * Revision 1.18  2004/04/14 09:11:57  strk
 * endCapStyle was never set in BufferOp contructor
 *
 * Revision 1.17  2004/04/14 08:38:31  strk
 * BufferOp constructor missed to set argGeom
 *
 * Revision 1.16  2004/04/10 22:41:25  ybychkov
 * "precision" upgraded to JTS 1.4
 *
 * Revision 1.15  2004/04/10 08:40:01  ybychkov
 * "operation/buffer" upgraded to JTS 1.4
 *
 *
 **********************************************************************/

#include "../../headers/opBuffer.h"
#include "../../headers/precision.h"

namespace geos {

/**
* Compute a reasonable scale factor to limit the precision of
* a given combination of Geometry and buffer distance->
* The scale factor is based on a heuristic->
*
* @param g the Geometry being buffered
* @param distance the buffer distance
* @param maxPrecisionDigits the mzx # of digits that should be allowed by
*          the precision determined by the computed scale factor
*
* @return a scale factor that allows a reasonable amount of precision for the buffer computation
*/
double BufferOp::precisionScaleFactor(Geometry *g,double distance,int maxPrecisionDigits){
	Envelope *env=g->getEnvelopeInternal();
	double envSize=max(env->getHeight(), env->getWidth());
	delete env;
	double expandByDistance=distance > 0.0 ? distance : 0.0;
	double bufEnvSize=envSize + 2 * expandByDistance;
	// the smallest power of 10 greater than the buffer envelope
	int bufEnvLog10=(int) (log(bufEnvSize) / log(10.0) + 1.0);
	int minUnitLog10=bufEnvLog10 - maxPrecisionDigits;
	// scale factor is inverse of min Unit size, so flip sign of exponent
	double scaleFactor=pow(10.0,-minUnitLog10);
	return scaleFactor;
}

/**
* Computes the buffer of a geometry for a given buffer distance->
*
* @param g the geometry to buffer
* @param distance the buffer distance
* @return the buffer of the input geometry
*/
Geometry* BufferOp::bufferOp(Geometry *g, double distance){
	BufferOp *gBuf=new BufferOp(g);
	Geometry* geomBuf;
	try {
		geomBuf=gBuf->getResultGeometry(distance);
	} catch (...) {
		delete gBuf;
		throw;
	}
	delete gBuf;
	return geomBuf;
}

/**
* Comutes the buffer for a geometry for a given buffer distance
* and accuracy of approximation->
*
* @param g the geometry to buffer
* @param distance the buffer distance
* @param quadrantSegments the number of segments used to approximate a quarter circle
* @return the buffer of the input geometry
*
*/
Geometry*
BufferOp::bufferOp(Geometry *g, double distance, int quadrantSegments)
{
	BufferOp *bufOp=new BufferOp(g);
	Geometry *geomBuf;
	bufOp->setQuadrantSegments(quadrantSegments);
	try {
		geomBuf=bufOp->getResultGeometry(distance);
	} catch (...) {
		delete bufOp;
		throw;
	}
	delete bufOp;
	return geomBuf;
}

int BufferOp::MAX_PRECISION_DIGITS=12;
/**
* Initializes a buffer computation for the given geometry
*
* @param g the geometry to buffer
*/
BufferOp::BufferOp(Geometry *g) {
	quadrantSegments=OffsetCurveBuilder::DEFAULT_QUADRANT_SEGMENTS;
	endCapStyle=BufferOp::CAP_ROUND;
	argGeom = g;
	resultGeometry=NULL;
	saveException=NULL;
}

/**
* Specifies the end cap style of the generated buffer->
* The styles supported are {@link CAP_ROUND}, {@link CAP_BUTT}, and {@link CAP_SQUARE}->
* The default is CAP_ROUND->
*
* @param endCapStyle the end cap style to specify
*/
void BufferOp::setEndCapStyle(int nEndCapStyle){
	endCapStyle=nEndCapStyle;
}

/**
* Specifies the end cap style of the generated buffer->
* The styles supported are {@link CAP_ROUND}, {@link CAP_BUTT}, and {@link CAP_SQUARE}->
* The default is CAP_ROUND->
*
* @param endCapStyle the end cap style to specify
*/
void BufferOp::setQuadrantSegments(int nQuadrantSegments){
	quadrantSegments=nQuadrantSegments;
}

/**
* Returns the buffer computed for a geometry for a given buffer distance->
*
* @param g the geometry to buffer
* @param distance the buffer distance
* @return the buffer of the input geometry
*/
Geometry* BufferOp::getResultGeometry(double nDistance){
	distance=nDistance;
	computeGeometry();
	return resultGeometry;
}

/**
* Comutes the buffer for a geometry for a given buffer distance
* and accuracy of approximation->
*
* @param g the geometry to buffer
* @param distance the buffer distance
* @param quadrantSegments the number of segments used to approximate a quarter circle
* @return the buffer of the input geometry
*
* @deprecated use setQuadrantSegments instead
*/
Geometry* BufferOp::getResultGeometry(double nDistance, int nQuadrantSegments){
	distance=nDistance;
	setQuadrantSegments(nQuadrantSegments);
	computeGeometry();
	return resultGeometry;
}

void
BufferOp::computeGeometry()
{
	bufferOriginalPrecision();
	if (resultGeometry!=NULL) return;
	// try and compute with decreasing precision
	for (int precDigits=MAX_PRECISION_DIGITS; precDigits >= 0; precDigits--) 	{
		try {
			bufferFixedPrecision(precDigits);
		} catch (TopologyException *ex) {
			delete saveException;
			saveException=ex;
			// don't propagate the exception - it will be detected by fact that resultGeometry is null
		} catch (...) {
			throw;
		}

		if (resultGeometry!=NULL)
		{
			// debug
			//if ( saveException ) cerr<<saveException->toString()<<endl;
			delete saveException;
			return;
		}
	}
	// tried everything - have to bail
	throw saveException;
	//return resultGeometry;
}

void
BufferOp::bufferOriginalPrecision()
{
	BufferBuilder *bufBuilder=new BufferBuilder();
	//cerr<<"computing with original precision"<<endl;
	try {
		bufBuilder->setQuadrantSegments(quadrantSegments);
		bufBuilder->setEndCapStyle(endCapStyle);
		resultGeometry=bufBuilder->buffer(argGeom, distance);
	} catch (TopologyException *ex) {
		// bufBuilder will be deleted below
		delete saveException;
		saveException=ex;
	} catch (...) {
		// Unexpected!
		delete bufBuilder;
		throw;
	}
	delete bufBuilder;
}

void
BufferOp::bufferFixedPrecision(int precisionDigits)
{
	double sizeBasedScaleFactor=precisionScaleFactor(argGeom, distance, precisionDigits);
	PrecisionModel *fixedPM=new PrecisionModel(sizeBasedScaleFactor);
	// don't change the precision model of the Geometry, just reduce the precision
	SimpleGeometryPrecisionReducer *reducer=new SimpleGeometryPrecisionReducer(fixedPM);
	Geometry* reducedGeom=reducer->reduce(argGeom);
	//cerr<<"recomputing with precision scale factor="<<sizeBasedScaleFactor<<" (precision digits "<<precisionDigits<<")"<<endl;
	BufferBuilder *bufBuilder=new BufferBuilder();
	bufBuilder->setWorkingPrecisionModel(fixedPM);
	bufBuilder->setQuadrantSegments(quadrantSegments);

	// this may throw an exception, if robustness errors are encountered
	try {
		resultGeometry=bufBuilder->buffer(reducedGeom, distance);
	} catch (...) {
		delete bufBuilder;
		delete reducer;
		delete fixedPM;
		delete reducedGeom;
		throw;
	}
	delete bufBuilder;
	delete reducer;
	delete fixedPM;
	delete reducedGeom;
}

}
<commit_msg>Reduced dynamic allocations in bufferOriginalPrecision and bufferFixedPrecision.<commit_after>/**********************************************************************
 * $Id$
 *
 * GEOS - Geometry Engine Open Source
 * http://geos.refractions.net
 *
 * Copyright (C) 2001-2002 Vivid Solutions Inc.
 *
 * This is free software; you can redistribute and/or modify it under
 * the terms of the GNU Lesser General Public Licence as published
 * by the Free Software Foundation.
 * See the COPYING file for more information.
 *
 **********************************************************************
 * $Log$
 * Revision 1.28  2004/05/05 10:03:49  strk
 * Reduced dynamic allocations in bufferOriginalPrecision and bufferFixedPrecision.
 *
 * Revision 1.27  2004/05/03 17:15:38  strk
 * leaks on exception fixed.
 *
 * Revision 1.26  2004/04/23 00:02:18  strk
 * const-correctness changes
 *
 * Revision 1.25  2004/04/20 13:24:15  strk
 * More leaks removed.
 *
 * Revision 1.24  2004/04/20 10:14:20  strk
 * Memory leaks removed.
 *
 * Revision 1.23  2004/04/19 15:14:46  strk
 * Added missing virtual destructor in SpatialIndex class.
 * Memory leaks fixes. Const and throw specifications added.
 *
 * Revision 1.22  2004/04/19 12:51:01  strk
 * Memory leaks fixes. Throw specifications added.
 *
 * Revision 1.21  2004/04/16 14:09:17  strk
 * Leaks fixes
 *
 * Revision 1.20  2004/04/16 11:04:24  strk
 * Memory leaks plugged on exception thrown
 *
 * Revision 1.19  2004/04/16 10:00:08  strk
 * Memory leak fixed.
 *
 * Revision 1.18  2004/04/14 09:11:57  strk
 * endCapStyle was never set in BufferOp contructor
 *
 * Revision 1.17  2004/04/14 08:38:31  strk
 * BufferOp constructor missed to set argGeom
 *
 * Revision 1.16  2004/04/10 22:41:25  ybychkov
 * "precision" upgraded to JTS 1.4
 *
 * Revision 1.15  2004/04/10 08:40:01  ybychkov
 * "operation/buffer" upgraded to JTS 1.4
 *
 *
 **********************************************************************/

#include "../../headers/opBuffer.h"
#include "../../headers/precision.h"

namespace geos {

/**
* Compute a reasonable scale factor to limit the precision of
* a given combination of Geometry and buffer distance->
* The scale factor is based on a heuristic->
*
* @param g the Geometry being buffered
* @param distance the buffer distance
* @param maxPrecisionDigits the mzx # of digits that should be allowed by
*          the precision determined by the computed scale factor
*
* @return a scale factor that allows a reasonable amount of precision for the buffer computation
*/
double BufferOp::precisionScaleFactor(Geometry *g,double distance,int maxPrecisionDigits){
	Envelope *env=g->getEnvelopeInternal();
	double envSize=max(env->getHeight(), env->getWidth());
	delete env;
	double expandByDistance=distance > 0.0 ? distance : 0.0;
	double bufEnvSize=envSize + 2 * expandByDistance;
	// the smallest power of 10 greater than the buffer envelope
	int bufEnvLog10=(int) (log(bufEnvSize) / log(10.0) + 1.0);
	int minUnitLog10=bufEnvLog10 - maxPrecisionDigits;
	// scale factor is inverse of min Unit size, so flip sign of exponent
	double scaleFactor=pow(10.0,-minUnitLog10);
	return scaleFactor;
}

/**
* Computes the buffer of a geometry for a given buffer distance->
*
* @param g the geometry to buffer
* @param distance the buffer distance
* @return the buffer of the input geometry
*/
Geometry* BufferOp::bufferOp(Geometry *g, double distance){
	BufferOp *gBuf=new BufferOp(g);
	Geometry* geomBuf;
	try {
		geomBuf=gBuf->getResultGeometry(distance);
	} catch (...) {
		delete gBuf;
		throw;
	}
	delete gBuf;
	return geomBuf;
}

/**
* Comutes the buffer for a geometry for a given buffer distance
* and accuracy of approximation->
*
* @param g the geometry to buffer
* @param distance the buffer distance
* @param quadrantSegments the number of segments used to approximate a quarter circle
* @return the buffer of the input geometry
*
*/
Geometry*
BufferOp::bufferOp(Geometry *g, double distance, int quadrantSegments)
{
	BufferOp *bufOp=new BufferOp(g);
	Geometry *geomBuf;
	bufOp->setQuadrantSegments(quadrantSegments);
	try {
		geomBuf=bufOp->getResultGeometry(distance);
	} catch (...) {
		delete bufOp;
		throw;
	}
	delete bufOp;
	return geomBuf;
}

int BufferOp::MAX_PRECISION_DIGITS=12;
/**
* Initializes a buffer computation for the given geometry
*
* @param g the geometry to buffer
*/
BufferOp::BufferOp(Geometry *g) {
	quadrantSegments=OffsetCurveBuilder::DEFAULT_QUADRANT_SEGMENTS;
	endCapStyle=BufferOp::CAP_ROUND;
	argGeom = g;
	resultGeometry=NULL;
	saveException=NULL;
}

/**
* Specifies the end cap style of the generated buffer->
* The styles supported are {@link CAP_ROUND}, {@link CAP_BUTT}, and {@link CAP_SQUARE}->
* The default is CAP_ROUND->
*
* @param endCapStyle the end cap style to specify
*/
void BufferOp::setEndCapStyle(int nEndCapStyle){
	endCapStyle=nEndCapStyle;
}

/**
* Specifies the end cap style of the generated buffer->
* The styles supported are {@link CAP_ROUND}, {@link CAP_BUTT}, and {@link CAP_SQUARE}->
* The default is CAP_ROUND->
*
* @param endCapStyle the end cap style to specify
*/
void BufferOp::setQuadrantSegments(int nQuadrantSegments){
	quadrantSegments=nQuadrantSegments;
}

/**
* Returns the buffer computed for a geometry for a given buffer distance->
*
* @param g the geometry to buffer
* @param distance the buffer distance
* @return the buffer of the input geometry
*/
Geometry* BufferOp::getResultGeometry(double nDistance){
	distance=nDistance;
	computeGeometry();
	return resultGeometry;
}

/**
* Comutes the buffer for a geometry for a given buffer distance
* and accuracy of approximation->
*
* @param g the geometry to buffer
* @param distance the buffer distance
* @param quadrantSegments the number of segments used to approximate a quarter circle
* @return the buffer of the input geometry
*
* @deprecated use setQuadrantSegments instead
*/
Geometry* BufferOp::getResultGeometry(double nDistance, int nQuadrantSegments){
	distance=nDistance;
	setQuadrantSegments(nQuadrantSegments);
	computeGeometry();
	return resultGeometry;
}

void
BufferOp::computeGeometry()
{
	bufferOriginalPrecision();
	if (resultGeometry!=NULL) return;
	// try and compute with decreasing precision
	for (int precDigits=MAX_PRECISION_DIGITS; precDigits >= 0; precDigits--) 	{
		try {
			bufferFixedPrecision(precDigits);
		} catch (TopologyException *ex) {
			delete saveException;
			saveException=ex;
			// don't propagate the exception - it will be detected by fact that resultGeometry is null
		} catch (...) {
			throw;
		}

		if (resultGeometry!=NULL)
		{
			// debug
			//if ( saveException ) cerr<<saveException->toString()<<endl;
			delete saveException;
			return;
		}
	}
	// tried everything - have to bail
	throw saveException;
	//return resultGeometry;
}

void
BufferOp::bufferOriginalPrecision()
{
	BufferBuilder bufBuilder=BufferBuilder();
	bufBuilder.setQuadrantSegments(quadrantSegments);
	bufBuilder.setEndCapStyle(endCapStyle);

	//cerr<<"computing with original precision"<<endl;
	try {
		resultGeometry=bufBuilder.buffer(argGeom, distance);
	} catch (TopologyException *ex) {
		//cerr<<ex->toString()<<endl;
		delete saveException;
		saveException=ex;
		return;
	}
	//cerr<<"done"<<endl;
}

void
BufferOp::bufferFixedPrecision(int precisionDigits)
{
	double sizeBasedScaleFactor=precisionScaleFactor(argGeom, distance, precisionDigits);
	PrecisionModel fixedPM=PrecisionModel(sizeBasedScaleFactor);
	// don't change the precision model of the Geometry, just reduce the precision
	SimpleGeometryPrecisionReducer reducer=SimpleGeometryPrecisionReducer(&fixedPM);
	Geometry* reducedGeom=reducer.reduce(argGeom);
	//cerr<<"recomputing with precision scale factor="<<sizeBasedScaleFactor<<" (precision digits "<<precisionDigits<<")"<<endl;
	BufferBuilder bufBuilder=BufferBuilder();
	bufBuilder.setWorkingPrecisionModel(&fixedPM);
	bufBuilder.setQuadrantSegments(quadrantSegments);

	// this may throw an exception, if robustness errors are encountered
	try {
		resultGeometry=bufBuilder.buffer(reducedGeom, distance);
	} catch (...) {
		delete reducedGeom;
		throw;
	}
	delete reducedGeom;
}

}
<|endoftext|>


<commit_before>// Copyright (C) 2012 Rhys Ulerich
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 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 General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.

#include "ar.hpp"

#include <algorithm>
#include <cstdlib>
#include <iterator>
#include <limits>
#include <list>
#include <vector>

#include <octave/oct.h>
#include <octave/oct-map.h>

/** @file
 * A GNU Octave wrapper estimating the best AR(p) model given signal input.
 * Compare \ref arsel.cpp.
 */

// Compile-time defaults in the code also appearing in the help message
#define DEFAULT_SUBMEAN  true
#define DEFAULT_ABSRHO   true
#define DEFAULT_MAXORDER 512
#define STRINGIFY(x) STRINGIFY_HELPER(x)
#define STRINGIFY_HELPER(x) #x

DEFUN_DLD(
    arsel, args, nargout,
    "\t[A, mu, sigma2eps, eff_var, eff_N, T0] = arsel (d, submean, absrho, maxorder)\n"
    "\tAutomatically fit an autoregressive model to an input signal.\n"
    "\t\n"
    "\tUse ar::burg_method and ar::best_model<CIC<Burg<MeanHandling > > to\n"
    "\tobtain the most likely autoregressive process for input signal d.\n"
    "\tThe sample mean of d will be subtracted whenever submean is true.\n"
    "\tThe absolute value of the autocorrelation function will be used\n"
    "\tin computing the decorrelation time T0 whenever absrho is true.\n"
    "\tModel orders zero through min(columns(d), maxorder) will be considered.\n"
    "\t\n"
    "\tThe filter()-ready process parameters are returned in A, the sample mean\n"
    "\tin mu, and the innovation variance \\sigma^2_\\epsilon in sigma2eps.\n"
    "\tGiven the observed autocorrelation structure, a decorrelation time T0 is\n"
    "\tcomputed and used to estimate the effective signal variance eff_var.\n"
    "\tThe number of effectively independent samples is returned in eff_N.\n"
    "\t\n"
    "\tOne may simulate N samples from the fitted process by calling:\n"
    "\t\n"
    "\t\tx = mu + filter([1], A, sqrt(sigma2eps)*randn(N,1));\n"
    "\t\n"
    "\tWhen not supplied, submean defaults to " STRINGIFY(DEFAULT_SUBMEAN) ".\n"
    "\tWhen not supplied, absrho defaults to " STRINGIFY(DEFAULT_ABSRHO) ".\n"
    "\tWhen not supplied, maxorder defaults to " STRINGIFY(DEFAULT_MAXORDER) ".\n"
    "\tWhen no outputs are requested, a struct with all outputs is returned.\n"
)
{
    std::size_t maxorder = DEFAULT_MAXORDER;
    bool        absrho   = DEFAULT_ABSRHO;
    bool        submean  = DEFAULT_SUBMEAN;
    RowVector   data;
    switch (args.length())
    {
        case 4:
            maxorder = args(3).ulong_value();
        case 3:
            absrho = args(2).bool_value();
        case 2:
            submean = args(1).bool_value();
        case 1:
            data = args(0).row_vector_value();
            break;
        default:
            error("Invalid call to arsel.  Correct usage is: ");
        case 0:
            print_usage();
            return octave_value();
    }

    // How much data will we be processing?
    const size_t N = data.dims().numel();
    if (!N)
    {
        error("arsel: input signal d must have nonzero length");
        return octave_value();
    }

    // Use burg_method to estimate a hierarchy of AR models from input data
    double mu;
    std::vector<double> params, sigma2e, gain, autocor;
    params .reserve(maxorder*(maxorder + 1)/2);
    sigma2e.reserve(maxorder + 1);
    gain   .reserve(maxorder + 1);
    autocor.reserve(maxorder + 1);
    ar::burg_method(data.fortran_vec(), data.fortran_vec() + N,
                    mu, maxorder,
                    std::back_inserter(params),
                    std::back_inserter(sigma2e),
                    std::back_inserter(gain),
                    std::back_inserter(autocor),
                    submean, /* output hierarchy? */ true);

    // Keep only best model according to CIC accounting for subtract_mean.
    if (submean)
    {
        ar::best_model<ar::CIC<ar::Burg<ar::mean_subtracted> > >(
                N, params, sigma2e, gain, autocor);
    }
    else
    {
        ar::best_model<ar::CIC<ar::Burg<ar::mean_retained> > >(
                N, params, sigma2e, gain, autocor);
    }

    // Compute decorrelation time from the estimated autocorrelation model
    double T0 = std::numeric_limits<double>::quiet_NaN();
    if (nargout == 0 || nargout > 2)
    {
        ar::predictor<double> p = ar::autocorrelation(
                params.begin(), params.end(), gain[0], autocor.begin());
        T0 = ar::decorrelation_time(N, p, absrho);
    }

    // Prepare autoregressive process parameters for Octave's filter function
    RowVector A(params.size() + 1, /* leading one */ 1);
    std::copy(params.begin(), params.end(), A.fortran_vec() + 1);

    // Prepare output: [A, mu, sigma2eps, eff_var, eff_N, T0]
    // Unbiased effective variance expression from [Trenberth1984]
    octave_value_list result;
    std::list<std::string> names;
    switch (nargout == 0 ? std::numeric_limits<int>::max() : nargout)
    {
        default:
            if (nargout) warning("arsel: too many output values requested");
        case 6:
            names.push_front("T0");
            result.prepend(T0);
        case 5:
            names.push_front("eff_N");
            result.prepend(N / T0);
        case 4:
            names.push_front("eff_var");
            result.prepend((N*gain[0]*sigma2e[0]) / (N - T0));
        case 3:
            names.push_front("sigma2eps");
            result.prepend(sigma2e[0]);
        case 2:
            names.push_front("mu");
            result.prepend(mu);
        case 1:
            names.push_front("A");
            result.prepend(A);
            break;
        case 0:
            panic_impossible();
    }

    // Either return requested results in assignment-friendly fashion
    // or, when nothing was requested, return a struct to likely display.
    if (nargout)
    {
        result.stash_name_tags(names);
    }
    else // nargout == 0
    {
        Octave_map m;
        int i = 0;
        while (names.size()) {
            m.assign(names.front(), result(i++));
            names.pop_front();
        }
        result.resize(1);
        result(0) = m;
    }

    // Provide no results whenever an error was detected
    if (error_state)
    {
        warning("arsel: error detected; no results returned");
        result.resize(0);
    }

    return result;
}
<commit_msg>Progress on batching within arsel<commit_after>// Copyright (C) 2012 Rhys Ulerich
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 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 General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.

#include "ar.hpp"

#include <algorithm>
#include <cstdlib>
#include <iterator>
#include <limits>
#include <list>
#include <vector>

#include <octave/oct.h>
#include <octave/oct-map.h>
#include <octave/ov-struct.h>
#include <octave/Cell.h>

/** @file
 * A GNU Octave wrapper estimating the best AR(p) model given signal input.
 * Compare \ref arsel.cpp.
 */

// Compile-time defaults in the code also appearing in the help message
#define DEFAULT_SUBMEAN  true
#define DEFAULT_ABSRHO   true
#define DEFAULT_MAXORDER 512
#define STRINGIFY(x) STRINGIFY_HELPER(x)
#define STRINGIFY_HELPER(x) #x

DEFUN_DLD(
    arsel, args, nargout,
    "\tM = arsel (d, submean, absrho, maxorder)\n"
    "\tAutomatically fit autoregressive models to input signals.\n"
    "\t\n"
    "\tUse ar::burg_method and ar::best_model<CIC<Burg<MeahHandling> > to\n"
    "\tfit an autoregressive process for signals contained in the rows of d.\n"
    "\tSample means will be subtracted whenever submean is true.  Model\n"
    "\torders zero through min(columns(d), maxorder) will be considered.\n"
    "\tA structure is returned where each field either contains a result\n"
    "\tindexable by the signal number (i.e. the row indices of input matrix\n"
    "\td) or it contains a single scalar applicable to all signals.\n"
    "\t\n"
    "\tThe number of samples in d (i.e. the number of rows) is returned\n"
    "\tin field 'N'.  The filter()-ready process parameters are returned\n"
    "\tin field 'A', the sample mean in 'mu', and the innovation variance\n"
    "\t\\sigma^2_\\epsilon in 'sigma2eps'.  The process gains are returned\n"
    "\tin 'gain' and the autocorrelation boundary conditions in 'autocor'\n"
    "\tfor lags zero through the model order, inclusive.\n"
    "\t\n"
    "\tGiven the observed autocorrelation structure, a decorrelation time\n"
    "\t'T0' is computed and used to estimate the effective signal variance\n"
    "\t'eff_var'.  The number of effectively independent samples is returned\n"
    "\tin 'eff_N'.  These effective values are combined to estimate the\n"
    "\tsampling error (i.e. the standard deviation of the sample mean)\n"
    "\tas field 'mu_sigma'.  The absolute value of the autocorrelation\n"
    "\tfunction will be used in computing the decorrelation times whenever\n"
    "\tabsrho is true.\n"
    "\t\n"
    "\tOne may simulate N samples from a fitted process analogously to\n"
    "\t\n"
    "\t\tx = mu + filter([1], A, sqrt(sigma2eps)*randn(N,1));\n"
    "\t\n"
    "\tWhen omitted, submean defaults to " STRINGIFY(DEFAULT_SUBMEAN) ".\n"
    "\tWhen omitted, absrho defaults to " STRINGIFY(DEFAULT_ABSRHO) ".\n"
    "\tWhen omitted, maxorder defaults to " STRINGIFY(DEFAULT_MAXORDER) ".\n"
)
{
    std::size_t maxorder = DEFAULT_MAXORDER;
    bool        absrho   = DEFAULT_ABSRHO;
    bool        submean  = DEFAULT_SUBMEAN;
    Matrix      data;
    switch (args.length())
    {
        case 4: maxorder = args(3).ulong_value();
        case 3: absrho   = args(2).bool_value();
        case 2: submean  = args(1).bool_value();
        case 1: data     = args(0).matrix_value();
                if (!error_state) break;
        default:
            error("Invalid call to arsel.  Correct usage is: ");
        case 0:
            print_usage();
            return octave_value();
    }

    const octave_idx_type M = data.rows();  // Number of signals
    const octave_idx_type N = data.cols();  // Samples per signal

    // Prepare per-signal storage locations to return to caller
    Cell         _A        (dim_vector(M,1));
    Cell         _autocor  (dim_vector(M,1));
    ColumnVector _eff_N    (M);
    ColumnVector _eff_var  (M);
    ColumnVector _gain     (M);
    ColumnVector _mu       (M);
    ColumnVector _mu_sigma (M);
    ColumnVector _sigma2eps(M);
    ColumnVector _T0       (M);

    // Prepare vectors to capture burg_method() output
    std::vector<double> params, sigma2e, gain, autocor;
    params .reserve(maxorder*(maxorder + 1)/2);
    sigma2e.reserve(maxorder + 1);
    gain   .reserve(maxorder + 1);
    autocor.reserve(maxorder + 1);

    // Prepare repeatedly-used working storage for burg_method()
    std::vector<double> f, b, Ak, ac;

    // Process each signal in turn...
    for (octave_idx_type i = 0; i < M; ++i)
    {
        // Use burg_method to estimate a hierarchy of AR models from input data
        params .clear();
        sigma2e.clear();
        gain   .clear();
        autocor.clear();
        ar::strided_adaptor<double*> signal_begin(&data(i,0), N);
        ar::strided_adaptor<double*> signal_end  (&data(i,N), N);
        ar::burg_method(signal_begin, signal_end, _mu(i), maxorder,
                        std::back_inserter(params),
                        std::back_inserter(sigma2e),
                        std::back_inserter(gain),
                        std::back_inserter(autocor),
                        submean, /* output hierarchy? */ true, f, b, Ak, ac);

        // Keep only best model according to CIC accounting for subtract_mean.
        // TODO Permit specifying the criterion as a function argument.
        if (submean)
        {
            ar::best_model<ar::CIC<ar::Burg<ar::mean_subtracted> > >(
                    N, params, sigma2e, gain, autocor);
        }
        else
        {
            ar::best_model<ar::CIC<ar::Burg<ar::mean_retained> > >(
                    N, params, sigma2e, gain, autocor);
        }

        // Compute decorrelation time from the estimated autocorrelation model
        ar::predictor<double> p = ar::autocorrelation(
                params.begin(), params.end(), gain[0], autocor.begin());
        _T0(i) = ar::decorrelation_time(N, p, absrho);

        // Filter()-ready process parameters in field 'A' with leading one
        {
            RowVector t(params.size() + 1);
            t(0) = 1;
            std::copy(params.begin(), params.end(), t.fortran_vec() + 1);
            _A(i) = t;
        }

        // Field 'sigma2eps'
        _sigma2eps(i) = sigma2e[0];

        // Field 'gain'
        _gain(i) = gain[0];

        // Field 'autocor'
        {
            RowVector t(autocor.size());
            std::copy(autocor.begin(), autocor.end(), t.fortran_vec());
            _autocor.assign(i, t);
        }

        // Field 'eff_var'
        // Unbiased effective variance expression from [Trenberth1984]
        _eff_var(i) = (N*gain[0]*sigma2e[0]) / (N - _T0(i));

        // Field 'eff_N'
        _eff_N(i) = N / _T0(i);

        // Field 'mu_sigma'
        // Variance of the sample mean using effective quantities
        _mu_sigma(i) = std::sqrt(_eff_var(i) / _eff_N(i));

        // Permit user to interrupt the computations at this time
        OCTAVE_QUIT;
    }

    // Provide no results whenever an error was detected
    if (error_state)
    {
        warning("arsel: error detected; no results returned");
        return octave_value_list();
    }

    // Build map containing return fields
    Octave_map retval;
    retval.assign("A",         octave_value(_A));
    retval.assign("absrho",    octave_value(absrho));
    retval.assign("autocor",   octave_value(_autocor));
    retval.assign("eff_N",     _eff_N);
    retval.assign("eff_var",   _eff_var);
    retval.assign("gain",      _gain);
    retval.assign("maxorder",  octave_value(maxorder));
    retval.assign("mu",        _mu);
    retval.assign("mu_sigma",  _mu_sigma);
    retval.assign("N",         octave_value(N));
    retval.assign("sigma2eps", _sigma2eps);
    retval.assign("submean",   octave_value(submean));
    retval.assign("T0",        _T0);

    return octave_value_list(retval);
}
<|endoftext|>


<commit_before>
#include <sstream>

#include "flame/base.h"
#include "pyflame.h"

#define TRY PyMachine *machine = reinterpret_cast<PyMachine*>(raw); try

namespace {

struct PyMachine {
    PyObject_HEAD

    PyObject *weak;
    Machine *machine;
};

static
int PyMachine_init(PyObject *raw, PyObject *args, PyObject *kws)
{
    TRY {
        assert(!machine->weak);

        std::auto_ptr<Config> C(PyGLPSParse2Config(raw, args, kws));

        machine->machine = new Machine(*C);

        return 0;
    } CATCH3(key_error, KeyError, -1)
      CATCH3(std::invalid_argument, ValueError, -1)
      CATCH3(std::exception, RuntimeError, -1)
}

static
void PyMachine_free(PyObject *raw)
{
    TRY {
        std::auto_ptr<Machine> S(machine->machine);
        machine->machine = NULL;

        if(machine->weak)
            PyObject_ClearWeakRefs(raw);

        Py_TYPE(raw)->tp_free(raw);
    } CATCH2V(std::exception, RuntimeError)
}

static
PyObject *PyMachine_str(PyObject *raw)
{
    TRY {
        std::ostringstream strm;
        strm << *(machine->machine);
        return PyString_FromString(strm.str().c_str());
    } CATCH()
}

static
PyObject *PyMachine_conf(PyObject *raw, PyObject *args, PyObject *kws)
{
    TRY {
        PyObject *pyindex = Py_None;
        const char *pnames[] = {"index", NULL};
        if(!PyArg_ParseTupleAndKeywords(args, kws, "|O", (char**)pnames, &pyindex))
            return NULL;

        Config C;
        if(pyindex==Py_None) {
            C = machine->machine->conf();
        } else if(PyNumber_Check(pyindex)) {
            PyRef<> pylong(PyNumber_Long(pyindex));
            long index = PyLong_AsLong(pylong.py());
            if(index<0 || (unsigned long)index>=machine->machine->size())
                return PyErr_Format(PyExc_IndexError, "Element index out of range");
            C = (*machine->machine)[index]->conf();
        } else {
            return PyErr_Format(PyExc_ValueError, "'index' must be an integer or None");
        }
        C.flatten();

        return conf2dict(&C);
    } CATCH()
}

static
PyObject *PyMachine_allocState(PyObject *raw, PyObject *args, PyObject *kws)
{
    TRY {
        PyObject *d = Py_None, *W = Py_False;
        const char *pnames[] = {"config", "inherit", NULL};
        if(!PyArg_ParseTupleAndKeywords(args, kws, "|OO", (char**)pnames, &d, &W))
            return NULL;

        Config C;
        if(d==Py_None) {
            C = machine->machine->conf();
        } else if(PyDict_Check(d)) {
            if(PyObject_IsTrue(W)) {
                C = machine->machine->conf();
                C.push_scope();
            }
            PyRef<> list(PyMapping_Items(d));
            List2Config(C, list.py());
        } else {
            return PyErr_Format(PyExc_ValueError, "allocState() needs config=None or {}");
        }
        std::auto_ptr<StateBase> state(machine->machine->allocState(C));
        PyObject *ret = wrapstate(state.get());
        state.release();
        return ret;
    } CATCH()
}

struct PyStoreObserver : public Observer
{
    PyRef<> list;
    PyStoreObserver()
        :list(PyList_New(0))
    {}
    virtual ~PyStoreObserver() {}
    virtual void view(const ElementVoid* elem, const StateBase* state)
    {
        PyRef<> tuple(PyTuple_New(2));
        std::auto_ptr<StateBase> tmpstate(state->clone());
        PyRef<> statecopy(wrapstate(tmpstate.get()));
        tmpstate.release();

        PyTuple_SET_ITEM(tuple.py(), 0, PyInt_FromSize_t(elem->index));
        PyTuple_SET_ITEM(tuple.py(), 1, statecopy.release());
        if(PyList_Append(list.py(), tuple.py()))
            throw std::runtime_error(""); // a py exception is active
    }
};

struct PyScopedObserver
{
    Machine *machine;
    std::vector<size_t> observed;
    PyScopedObserver(Machine *m) : machine(m) {}
    ~PyScopedObserver() {
        for(size_t i=0; i<machine->size(); i++) {
            (*machine)[i]->set_observer(NULL);
        }
    }
    void observe(size_t i, Observer *o)
    {
        if(i>=machine->size())
            throw std::runtime_error("element index out of range");
        observed.push_back(i);
        (*machine)[i]->set_observer(o);
    }
};

static
PyObject *PyMachine_propagate(PyObject *raw, PyObject *args, PyObject *kws)
{

    TRY {
        PyObject *state, *toobserv = Py_None;
        unsigned long start = 0, max = (unsigned long)-1;
        const char *pnames[] = {"state", "start", "max", "observe", NULL};
        if(!PyArg_ParseTupleAndKeywords(args, kws, "O|kkO", (char**)pnames, &state, &start, &max, &toobserv))
            return NULL;

        PyStoreObserver observer;
        PyScopedObserver observing(machine->machine);

        if(toobserv!=Py_None) {
            PyRef<> iter(PyObject_GetIter(toobserv)), item;

            while(item.reset(PyIter_Next(iter.py()), PyRef<>::allow_null())) {
                Py_ssize_t num = PyNumber_AsSsize_t(item.py(), PyExc_ValueError);
                if(PyErr_Occurred())
                    throw std::runtime_error(""); // caller will get active python exception

                observing.observe(num, &observer);

            }
        }

        machine->machine->propagate(unwrapstate(state), start, max);
        if(toobserv) {
            return observer.list.release();
        } else {
            Py_RETURN_NONE;
        }
    } CATCH2(std::invalid_argument, ValueError)
    CATCH()
}

static
PyObject *PyMachine_reconfigure(PyObject *raw, PyObject *args, PyObject *kws)
{
    TRY{
        unsigned long idx;
        PyObject *conf, *replace = Py_False;
        const char *pnames[] = {"index", "config", "replace", NULL};
        if(!PyArg_ParseTupleAndKeywords(args, kws, "kO!|O", (char**)pnames, &idx, &PyDict_Type, &conf, &replace))
            return NULL;

        if(idx>=machine->machine->size())
            return PyErr_Format(PyExc_ValueError, "invalid element index %lu", idx);

        Config newconf;
        if(!PyObject_IsTrue(replace))
            newconf = (*machine->machine)[idx]->conf();

        PyRef<> list(PyMapping_Items(conf));
        List2Config(newconf, list.py(), 3); // set depth=3 to prevent recursion

        machine->machine->reconfigure(idx, newconf);

        Py_RETURN_NONE;
    } CATCH2(std::invalid_argument, ValueError)
    CATCH()
}

static
PyObject *PyMachine_find(PyObject *raw, PyObject *args, PyObject *kws)
{
    TRY{
        const char *ename = NULL, *etype = NULL;
        const char *pnames[] = {"name", "type", NULL};
        if(!PyArg_ParseTupleAndKeywords(args, kws, "|zz", (char**)pnames, &ename, &etype))
            return NULL;

        PyRef<> ret(PyList_New(0));

        std::pair<Machine::lookup_iterator, Machine::lookup_iterator> range;

        if(ename && etype) {
            return PyErr_Format(PyExc_ValueError, "only one of 'ename' or 'etype' may be given");
        } else if(ename) {
            range = machine->machine->equal_range(ename);
        } else if(etype) {
            range = machine->machine->equal_range_type(etype);
        } else {
            range = machine->machine->all_range();
        }

        for(; range.first!=range.second; ++range.first) {
            ElementVoid *elem = *range.first;

            PyRef<> pyidx(PyInt_FromLong(elem->index));

            if(PyList_Append(ret.py(), pyidx.py()))
                return NULL;
        }

        return ret.release();
    }CATCH()
}

static
Py_ssize_t PyMachine_len(PyObject *raw)
{
    TRY{
        return machine->machine->size();
    }CATCH1(-1)
}

static PyMethodDef PyMachine_methods[] = {
    {"conf", (PyCFunction)&PyMachine_conf, METH_VARARGS|METH_KEYWORDS,
     "conf() -> {} Machine config\n"
     "conf(index) -> {} Element config"},
    {"allocState", (PyCFunction)&PyMachine_allocState, METH_VARARGS|METH_KEYWORDS,
     "allocState() -> State\n"
     "allocState({'variable':int|str}) -> State\n"
     "Allocate a new State based on this Machine's configuration."
     "  Optionally provide additional configuration"},
    {"propagate", (PyCFunction)&PyMachine_propagate, METH_VARARGS|METH_KEYWORDS,
     "propagate(State, start=0, max=-1, observe=None)\n"
     "propagate(State, start=0, max=-1, observe=[1,4,...]) -> [(index,State), ...]\n"
     "Propagate the provided State through the simulation.\n"
     "\n"
     "start and max selects through which element the State will be passed.\n"
     "\n"
     "observe may be None or an iterable yielding element indicies.\n"
     "In the second form propagate() returns a list of tuples with the output State of the selected elements."
    },
    {"reconfigure", (PyCFunction)&PyMachine_reconfigure, METH_VARARGS|METH_KEYWORDS,
     "reconfigure(index, {'variable':int|str})\n"
     "Change the configuration of an element."},
    {"find", (PyCFunction)&PyMachine_find, METH_VARARGS|METH_KEYWORDS,
    "find(ename=None, etype=None) -> [int]\n"
    "Return a list of element indices for element name or type matching the given string."},
    {NULL, NULL, 0, NULL}
};

static PySequenceMethods PyMachine_seq = {
    &PyMachine_len
};

static PyTypeObject PyMachineType = {
#if PY_MAJOR_VERSION >= 3
    PyVarObject_HEAD_INIT(NULL, 0)
#else
    PyObject_HEAD_INIT(NULL)
    0,
#endif
    "flame._internal.Machine",
    sizeof(PyMachine),
};

} // namespace

static const char pymdoc[] =
        "Machine(config, path=None, extra=None)\n"
        "Machine(config, path='/directry/', extra={'variable':float|str}})\n"
        "\n"
        "A Machine() the primary interface to the FLAME simulation engine.\n"
        "\n"
        "The 'config' argument may be a file-like object (with read())"
        " or a buffer which will be parsed with the GLPS parser (see GLPSParser::parse).\n"
        " Or it may be a dictionary.\n"
        "\n"
        ">>> with open('some.lat', 'rb') as F:\n"
        "      M = Machine(F)\n"
        ">>>\n"
        ;

int registerModMachine(PyObject *mod)
{
    PyMachineType.tp_doc = pymdoc;
    PyMachineType.tp_str = &PyMachine_str;

    PyMachineType.tp_new = &PyType_GenericNew;
    PyMachineType.tp_init = &PyMachine_init;
    PyMachineType.tp_dealloc = &PyMachine_free;

    PyMachineType.tp_weaklistoffset = offsetof(PyMachine, weak);

    PyMachineType.tp_flags = Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE;
    PyMachineType.tp_methods = PyMachine_methods;
    PyMachineType.tp_as_sequence = &PyMachine_seq;

    if(PyType_Ready(&PyMachineType))
        return -1;

    Py_INCREF((PyObject*)&PyMachineType);
    if(PyModule_AddObject(mod, "Machine", (PyObject*)&PyMachineType)) {
        Py_DECREF((PyObject*)&PyMachineType);
        return -1;
    }

    return 0;
}
<commit_msg>help message correction about machine.find() command<commit_after>
#include <sstream>

#include "flame/base.h"
#include "pyflame.h"

#define TRY PyMachine *machine = reinterpret_cast<PyMachine*>(raw); try

namespace {

struct PyMachine {
    PyObject_HEAD

    PyObject *weak;
    Machine *machine;
};

static
int PyMachine_init(PyObject *raw, PyObject *args, PyObject *kws)
{
    TRY {
        assert(!machine->weak);

        std::auto_ptr<Config> C(PyGLPSParse2Config(raw, args, kws));

        machine->machine = new Machine(*C);

        return 0;
    } CATCH3(key_error, KeyError, -1)
      CATCH3(std::invalid_argument, ValueError, -1)
      CATCH3(std::exception, RuntimeError, -1)
}

static
void PyMachine_free(PyObject *raw)
{
    TRY {
        std::auto_ptr<Machine> S(machine->machine);
        machine->machine = NULL;

        if(machine->weak)
            PyObject_ClearWeakRefs(raw);

        Py_TYPE(raw)->tp_free(raw);
    } CATCH2V(std::exception, RuntimeError)
}

static
PyObject *PyMachine_str(PyObject *raw)
{
    TRY {
        std::ostringstream strm;
        strm << *(machine->machine);
        return PyString_FromString(strm.str().c_str());
    } CATCH()
}

static
PyObject *PyMachine_conf(PyObject *raw, PyObject *args, PyObject *kws)
{
    TRY {
        PyObject *pyindex = Py_None;
        const char *pnames[] = {"index", NULL};
        if(!PyArg_ParseTupleAndKeywords(args, kws, "|O", (char**)pnames, &pyindex))
            return NULL;

        Config C;
        if(pyindex==Py_None) {
            C = machine->machine->conf();
        } else if(PyNumber_Check(pyindex)) {
            PyRef<> pylong(PyNumber_Long(pyindex));
            long index = PyLong_AsLong(pylong.py());
            if(index<0 || (unsigned long)index>=machine->machine->size())
                return PyErr_Format(PyExc_IndexError, "Element index out of range");
            C = (*machine->machine)[index]->conf();
        } else {
            return PyErr_Format(PyExc_ValueError, "'index' must be an integer or None");
        }
        C.flatten();

        return conf2dict(&C);
    } CATCH()
}

static
PyObject *PyMachine_allocState(PyObject *raw, PyObject *args, PyObject *kws)
{
    TRY {
        PyObject *d = Py_None, *W = Py_False;
        const char *pnames[] = {"config", "inherit", NULL};
        if(!PyArg_ParseTupleAndKeywords(args, kws, "|OO", (char**)pnames, &d, &W))
            return NULL;

        Config C;
        if(d==Py_None) {
            C = machine->machine->conf();
        } else if(PyDict_Check(d)) {
            if(PyObject_IsTrue(W)) {
                C = machine->machine->conf();
                C.push_scope();
            }
            PyRef<> list(PyMapping_Items(d));
            List2Config(C, list.py());
        } else {
            return PyErr_Format(PyExc_ValueError, "allocState() needs config=None or {}");
        }
        std::auto_ptr<StateBase> state(machine->machine->allocState(C));
        PyObject *ret = wrapstate(state.get());
        state.release();
        return ret;
    } CATCH()
}

struct PyStoreObserver : public Observer
{
    PyRef<> list;
    PyStoreObserver()
        :list(PyList_New(0))
    {}
    virtual ~PyStoreObserver() {}
    virtual void view(const ElementVoid* elem, const StateBase* state)
    {
        PyRef<> tuple(PyTuple_New(2));
        std::auto_ptr<StateBase> tmpstate(state->clone());
        PyRef<> statecopy(wrapstate(tmpstate.get()));
        tmpstate.release();

        PyTuple_SET_ITEM(tuple.py(), 0, PyInt_FromSize_t(elem->index));
        PyTuple_SET_ITEM(tuple.py(), 1, statecopy.release());
        if(PyList_Append(list.py(), tuple.py()))
            throw std::runtime_error(""); // a py exception is active
    }
};

struct PyScopedObserver
{
    Machine *machine;
    std::vector<size_t> observed;
    PyScopedObserver(Machine *m) : machine(m) {}
    ~PyScopedObserver() {
        for(size_t i=0; i<machine->size(); i++) {
            (*machine)[i]->set_observer(NULL);
        }
    }
    void observe(size_t i, Observer *o)
    {
        if(i>=machine->size())
            throw std::runtime_error("element index out of range");
        observed.push_back(i);
        (*machine)[i]->set_observer(o);
    }
};

static
PyObject *PyMachine_propagate(PyObject *raw, PyObject *args, PyObject *kws)
{

    TRY {
        PyObject *state, *toobserv = Py_None;
        unsigned long start = 0, max = (unsigned long)-1;
        const char *pnames[] = {"state", "start", "max", "observe", NULL};
        if(!PyArg_ParseTupleAndKeywords(args, kws, "O|kkO", (char**)pnames, &state, &start, &max, &toobserv))
            return NULL;

        PyStoreObserver observer;
        PyScopedObserver observing(machine->machine);

        if(toobserv!=Py_None) {
            PyRef<> iter(PyObject_GetIter(toobserv)), item;

            while(item.reset(PyIter_Next(iter.py()), PyRef<>::allow_null())) {
                Py_ssize_t num = PyNumber_AsSsize_t(item.py(), PyExc_ValueError);
                if(PyErr_Occurred())
                    throw std::runtime_error(""); // caller will get active python exception

                observing.observe(num, &observer);

            }
        }

        machine->machine->propagate(unwrapstate(state), start, max);
        if(toobserv) {
            return observer.list.release();
        } else {
            Py_RETURN_NONE;
        }
    } CATCH2(std::invalid_argument, ValueError)
    CATCH()
}

static
PyObject *PyMachine_reconfigure(PyObject *raw, PyObject *args, PyObject *kws)
{
    TRY{
        unsigned long idx;
        PyObject *conf, *replace = Py_False;
        const char *pnames[] = {"index", "config", "replace", NULL};
        if(!PyArg_ParseTupleAndKeywords(args, kws, "kO!|O", (char**)pnames, &idx, &PyDict_Type, &conf, &replace))
            return NULL;

        if(idx>=machine->machine->size())
            return PyErr_Format(PyExc_ValueError, "invalid element index %lu", idx);

        Config newconf;
        if(!PyObject_IsTrue(replace))
            newconf = (*machine->machine)[idx]->conf();

        PyRef<> list(PyMapping_Items(conf));
        List2Config(newconf, list.py(), 3); // set depth=3 to prevent recursion

        machine->machine->reconfigure(idx, newconf);

        Py_RETURN_NONE;
    } CATCH2(std::invalid_argument, ValueError)
    CATCH()
}

static
PyObject *PyMachine_find(PyObject *raw, PyObject *args, PyObject *kws)
{
    TRY{
        const char *ename = NULL, *etype = NULL;
        const char *pnames[] = {"name", "type", NULL};
        if(!PyArg_ParseTupleAndKeywords(args, kws, "|zz", (char**)pnames, &ename, &etype))
            return NULL;

        PyRef<> ret(PyList_New(0));

        std::pair<Machine::lookup_iterator, Machine::lookup_iterator> range;

        if(ename && etype) {
            return PyErr_Format(PyExc_ValueError, "only one of 'name' or 'type' may be given");
        } else if(ename) {
            range = machine->machine->equal_range(ename);
        } else if(etype) {
            range = machine->machine->equal_range_type(etype);
        } else {
            range = machine->machine->all_range();
        }

        for(; range.first!=range.second; ++range.first) {
            ElementVoid *elem = *range.first;

            PyRef<> pyidx(PyInt_FromLong(elem->index));

            if(PyList_Append(ret.py(), pyidx.py()))
                return NULL;
        }

        return ret.release();
    }CATCH()
}

static
Py_ssize_t PyMachine_len(PyObject *raw)
{
    TRY{
        return machine->machine->size();
    }CATCH1(-1)
}

static PyMethodDef PyMachine_methods[] = {
    {"conf", (PyCFunction)&PyMachine_conf, METH_VARARGS|METH_KEYWORDS,
     "conf() -> {} Machine config\n"
     "conf(index) -> {} Element config"},
    {"allocState", (PyCFunction)&PyMachine_allocState, METH_VARARGS|METH_KEYWORDS,
     "allocState() -> State\n"
     "allocState({'variable':int|str}) -> State\n"
     "Allocate a new State based on this Machine's configuration."
     "  Optionally provide additional configuration"},
    {"propagate", (PyCFunction)&PyMachine_propagate, METH_VARARGS|METH_KEYWORDS,
     "propagate(State, start=0, max=-1, observe=None)\n"
     "propagate(State, start=0, max=-1, observe=[1,4,...]) -> [(index,State), ...]\n"
     "Propagate the provided State through the simulation.\n"
     "\n"
     "start and max selects through which element the State will be passed.\n"
     "\n"
     "observe may be None or an iterable yielding element indicies.\n"
     "In the second form propagate() returns a list of tuples with the output State of the selected elements."
    },
    {"reconfigure", (PyCFunction)&PyMachine_reconfigure, METH_VARARGS|METH_KEYWORDS,
     "reconfigure(index, {'variable':int|str})\n"
     "Change the configuration of an element."},
    {"find", (PyCFunction)&PyMachine_find, METH_VARARGS|METH_KEYWORDS,
    "find(name=None, type=None) -> [int]\n"
    "Return a list of element indices for element name or type matching the given string."},
    {NULL, NULL, 0, NULL}
};

static PySequenceMethods PyMachine_seq = {
    &PyMachine_len
};

static PyTypeObject PyMachineType = {
#if PY_MAJOR_VERSION >= 3
    PyVarObject_HEAD_INIT(NULL, 0)
#else
    PyObject_HEAD_INIT(NULL)
    0,
#endif
    "flame._internal.Machine",
    sizeof(PyMachine),
};

} // namespace

static const char pymdoc[] =
        "Machine(config, path=None, extra=None)\n"
        "Machine(config, path='/directry/', extra={'variable':float|str}})\n"
        "\n"
        "A Machine() the primary interface to the FLAME simulation engine.\n"
        "\n"
        "The 'config' argument may be a file-like object (with read())"
        " or a buffer which will be parsed with the GLPS parser (see GLPSParser::parse).\n"
        " Or it may be a dictionary.\n"
        "\n"
        ">>> with open('some.lat', 'rb') as F:\n"
        "      M = Machine(F)\n"
        ">>>\n"
        ;

int registerModMachine(PyObject *mod)
{
    PyMachineType.tp_doc = pymdoc;
    PyMachineType.tp_str = &PyMachine_str;

    PyMachineType.tp_new = &PyType_GenericNew;
    PyMachineType.tp_init = &PyMachine_init;
    PyMachineType.tp_dealloc = &PyMachine_free;

    PyMachineType.tp_weaklistoffset = offsetof(PyMachine, weak);

    PyMachineType.tp_flags = Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE;
    PyMachineType.tp_methods = PyMachine_methods;
    PyMachineType.tp_as_sequence = &PyMachine_seq;

    if(PyType_Ready(&PyMachineType))
        return -1;

    Py_INCREF((PyObject*)&PyMachineType);
    if(PyModule_AddObject(mod, "Machine", (PyObject*)&PyMachineType)) {
        Py_DECREF((PyObject*)&PyMachineType);
        return -1;
    }

    return 0;
}
<|endoftext|>


<commit_before>// Copyright (c) 2012, Steinwurf ApS
// 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 Steinwurf ApS 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 Steinwurf ApS 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 <gtest/gtest.h>

#include <sak/buffer.hpp>

TEST(TestBuffer, construct)
{
    sak::buffer b;
    EXPECT_EQ(0U, b.size());
}

TEST(TestBuffer, resize)
{
    sak::buffer b;
    b.resize(100);
    EXPECT_EQ(100U, b.size());
}

TEST(TestBuffer, resize_and_copy)
{
    sak::buffer b1;
    b1.resize(100);
    EXPECT_EQ(100U, b1.size());

    sak::buffer b2 = b1;
    EXPECT_EQ(100U, b2.size());
}

TEST(TestBuffer, append_to_empty_with_size)
{
    std::vector<uint8_t> data(32);
    EXPECT_EQ(32U, data.size());

    sak::buffer b;
    EXPECT_EQ(0U, b.size());

    b.append(&data[0], static_cast<uint32_t>(data.size()));
    EXPECT_EQ(data.size(), b.size());
}

TEST(TestBuffer, append_to_empty_with_pointers)
{
    std::vector<uint8_t> data(32);
    EXPECT_EQ(32U, data.size());

    sak::buffer b;
    EXPECT_EQ(b.size(), 0U);

    b.append(&data[0], &data[0] + data.size());
    EXPECT_EQ(b.size(), data.size());
}

TEST(TestBuffer, append_to_empty_with_storage)
{
    std::vector<uint8_t> data(32);
    EXPECT_EQ(32U, data.size());

    sak::buffer b;
    EXPECT_EQ(b.size(), 0U);

    b.append(sak::storage(data));
    EXPECT_EQ(b.size(), data.size());
}

// TEST(TestBuffer, append_to_initialized)
// {
//     {
//         sak::buffer b(10);
//         EXPECT_EQ(b.size(), 0U);
//
//         std::vector<uint8_t> data(32, 'x');
//         b.append(&data[0], static_cast<uint32_t>(data.size()));
//         EXPECT_EQ(b.size(), data.size());
//     }
//
//     {
//         sak::buffer b(10);
//         EXPECT_EQ(b.size(), 0U);
//
//         std::vector<uint8_t> data(32, 'x');
//         b.append(&data[0], &data[0] + data.size());
//         EXPECT_EQ(b.size(), data.size());
//     }
//
//     {
//         sak::buffer b(10);
//         EXPECT_EQ(b.size(), 0U);
//
//         std::vector<uint8_t> data(32, 'x');
//         b.append(sak::storage(data));
//         EXPECT_EQ(b.size(), data.size());
//     }
//
// }

TEST(TestBuffer, resize_and_clear)
{
    sak::buffer b(100);
    EXPECT_EQ(0U, b.size());
    std::fill_n(b.data(), b.size(), 'x');

    b.resize(10);
    EXPECT_EQ(10U, b.size());
    std::fill_n(b.data(), b.size(), 'x');

    b.resize(101);
    EXPECT_EQ(101U, b.size());
    std::fill_n(b.data(), b.size(), 'x');

    b.clear();
    EXPECT_EQ(0U, b.size());

    b.resize(0);
    EXPECT_EQ(0U, b.size());

    b.resize(102);
    EXPECT_EQ(102U, b.size());
    std::fill_n(b.data(), b.size(), 'x');

    b.resize(0);
    EXPECT_EQ(0U, b.size());

    b.clear();
    EXPECT_EQ(0U, b.size());
}
<commit_msg>All tests on in test_buffer.cpp<commit_after>// Copyright (c) 2012, Steinwurf ApS
// 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 Steinwurf ApS 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 Steinwurf ApS 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 <gtest/gtest.h>

#include <sak/buffer.hpp>

TEST(TestBuffer, construct)
{
    sak::buffer b;
    EXPECT_EQ(0U, b.size());
}

TEST(TestBuffer, resize)
{
    sak::buffer b;
    b.resize(100);
    EXPECT_EQ(100U, b.size());
}

TEST(TestBuffer, resize_and_copy)
{
    sak::buffer b1;
    b1.resize(100);
    EXPECT_EQ(100U, b1.size());

    sak::buffer b2 = b1;
    EXPECT_EQ(100U, b2.size());
}

TEST(TestBuffer, append_to_empty_with_size)
{
    std::vector<uint8_t> data(32);
    EXPECT_EQ(32U, data.size());

    sak::buffer b;
    EXPECT_EQ(0U, b.size());

    b.append(&data[0], static_cast<uint32_t>(data.size()));
    EXPECT_EQ(data.size(), b.size());
}

TEST(TestBuffer, append_to_empty_with_pointers)
{
    std::vector<uint8_t> data(32);
    EXPECT_EQ(32U, data.size());

    sak::buffer b;
    EXPECT_EQ(b.size(), 0U);

    b.append(&data[0], &data[0] + data.size());
    EXPECT_EQ(b.size(), data.size());
}

TEST(TestBuffer, append_to_empty_with_storage)
{
    std::vector<uint8_t> data(32);
    EXPECT_EQ(32U, data.size());

    sak::buffer b;
    EXPECT_EQ(b.size(), 0U);

    b.append(sak::storage(data));
    EXPECT_EQ(b.size(), data.size());
}

TEST(TestBuffer, append_to_initialized)
{
    {
        sak::buffer b(10);
        EXPECT_EQ(b.size(), 0U);

        std::vector<uint8_t> data(32, 'x');
        EXPECT_EQ(32U, data.size());
        b.append(&data[0], static_cast<uint32_t>(data.size()));
        EXPECT_EQ(b.size(), data.size());
    }

    {
        sak::buffer b(10);
        EXPECT_EQ(b.size(), 0U);

        std::vector<uint8_t> data(32, 'x');
        EXPECT_EQ(32U, data.size());
        b.append(&data[0], &data[0] + data.size());
        EXPECT_EQ(b.size(), data.size());
    }

    {
        sak::buffer b(10);
        EXPECT_EQ(b.size(), 0U);

        std::vector<uint8_t> data(32, 'x');
        EXPECT_EQ(32U, data.size());
        b.append(sak::storage(data));
        EXPECT_EQ(b.size(), data.size());
    }
}

TEST(TestBuffer, resize_and_clear)
{
    sak::buffer b(100);
    EXPECT_EQ(0U, b.size());
    std::fill_n(b.data(), b.size(), 'x');

    b.resize(10);
    EXPECT_EQ(10U, b.size());
    std::fill_n(b.data(), b.size(), 'x');

    b.resize(101);
    EXPECT_EQ(101U, b.size());
    std::fill_n(b.data(), b.size(), 'x');

    b.clear();
    EXPECT_EQ(0U, b.size());

    b.resize(0);
    EXPECT_EQ(0U, b.size());

    b.resize(102);
    EXPECT_EQ(102U, b.size());
    std::fill_n(b.data(), b.size(), 'x');

    b.resize(0);
    EXPECT_EQ(0U, b.size());

    b.clear();
    EXPECT_EQ(0U, b.size());
}
<|endoftext|>