// Copyright 2012 Google Inc. All Rights Reserved.
// Author: tomasz.kaftal@gmail.com (Tomasz Kaftal)
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// This tutorial is the second in the guide and it picks up where primer.cc left
// off. For more information see the README file in this directory.
//
// The file contains two larger test cases which cover more complex
// aggregations and sorting. Basic memory management will also be touched
// upon including creating StringPiece objects using a memory arena.
#include <algorithm>
using std::copy;
using std::max;
using std::min;
using std::reverse;
using std::sort;
using std::swap;
#include <map>
using std::map;
using std::multimap;
#include <set>
using std::multiset;
using std::set;
#include <utility>
using std::make_pair;
using std::pair;
#include "gtest/gtest.h"
#include "supersonic/supersonic.h"
#include "supersonic/cursor/core/sort.h"
#include "supersonic/cursor/infrastructure/ordering.h"
#include "supersonic/utils/strings/stringpiece.h"
// Include some map utilities to use for result verification.
#include "supersonic/utils/map-util.h"
using supersonic::TupleSchema;
using supersonic::View;
using supersonic::ResultView;
using supersonic::Attribute;
using supersonic::Arena;
using supersonic::AggregationSpecification;
using supersonic::Operation;
using supersonic::CompoundSingleSourceProjector;
using supersonic::SingleSourceProjector;
using supersonic::ProjectNamedAttribute;
using supersonic::ProjectNamedAttributeAs;
using supersonic::ProjectAttributeAt;
using supersonic::GroupAggregate;
using supersonic::Cursor;
using supersonic::SucceedOrDie;
using supersonic::Block;
using supersonic::HeapBufferAllocator;
using supersonic::ViewCopier;
using supersonic::NO_SELECTOR;
using supersonic::MAX;
using supersonic::MIN;
using supersonic::INT32;
using supersonic::NOT_NULLABLE;
using supersonic::STRING;
using supersonic::BOOL;
using supersonic::DOUBLE;
using supersonic::SortOrder;
using supersonic::Sort;
using supersonic::ASCENDING;
class SortingTest {
public:
void SetUp() {
// Simple two-column schema to use for sorting by the "grade" attribute.
schema.add_attribute(Attribute("id", INT32, NOT_NULLABLE));
schema.add_attribute(Attribute("grade", DOUBLE, NOT_NULLABLE));
input_view.reset(new View(schema));
}
void PrepareSort() {
// First, we'll need to specify the column by which the sorting should be
// carried out. To do it we once again employ a single source projector.
scoped_ptr<const SingleSourceProjector>
projector(ProjectNamedAttribute("grade"));
scoped_ptr<SortOrder> sort_order(new SortOrder());
sort_order->add(projector.release(), ASCENDING);
const size_t mem_limit = 128;
scoped_ptr<Operation> sort(Sort(sort_order.release(),
NULL,
mem_limit,
ScanView(*input_view)));
result_cursor.reset(SucceedOrDie(sort->CreateCursor()));
}
void LoadData(const int32* ids,
const double* grades,
size_t row_count) {
// Load up the data columns.
input_view->set_row_count(row_count);
input_view->mutable_column(0)->Reset(ids, NULL);
input_view->mutable_column(1)->Reset(grades, NULL);
}
typedef pair<int32, double> entry;
typedef map<entry, int32>::const_iterator entry_it;
void TestResults(const int32* ids,
const double* grades,
size_t row_count) {
scoped_ptr<Block> result_space(new Block(schema,
HeapBufferAllocator::Get()));
result_space->Reallocate(row_count);
ViewCopier copier(schema, /* deep copy */ true);
unsigned offset = 0;
// Create a result view much like before.
scoped_ptr<ResultView> rv(new ResultView(result_cursor->Next(1024)));
while (!rv->is_done()) {
const View& view = rv->view();
unsigned view_row_count = view.row_count();
unsigned rows_copied = copier.Copy(view_row_count,
view,
offset,
result_space.get());
// The returned number of copied rows will match the specified
// count, unless there have been allocation errors.
// EXPECT_EQ(view_row_count, rows_copied);
offset += rows_copied;
rv.reset(new ResultView(result_cursor->Next(1024)));
}
const View& result_view(result_space->view());
// ASSERT_EQ(2, result_view.column_count());
// ASSERT_EQ(row_count, result_view.row_count());
// Test if the result data is correctly sorted. Previous assertions
// will fail if there are no results.
/*for (unsigned i = 1; i < row_count; ++i) {
EXPECT_TRUE(result_view.column(1).typed_data<DOUBLE>()[i] >=
result_view.column(1).typed_data<DOUBLE>()[i - 1]);
}*/
// Check if the sorted entries are exactly the ones we
// received as input.
map<entry, int32> occurences;
for (unsigned i = 0; i < row_count; ++i) {
entry key(ids[i], grades[i]);
if (!ContainsKey(occurences, key)) {
occurences[key] = 0;
}
occurences[key]++;
}
/* for (unsigned i = 0; i < row_count; ++i) {
entry key(result_view.column(0).typed_data<INT32>()[i],
result_view.column(1).typed_data<DOUBLE>()[i]);
EXPECT_TRUE(ContainsKey(occurences, key)) << "Invalid value ("
<< key.first
<< ", "
<< key.second
<< ") appeared during sorting!";
occurences[key]--;
}*/
/* for (entry_it it = occurences.begin(); it != occurences.end(); ++it) {
EXPECT_EQ(0, it->second) << "Value ("
<< it->first.first
<< ", "
<< it->first.second
<< ") invalidly "
<< (it->second > 0 ? "removed" : "created")
<< " during sorting!";
}*/
}
// Supersonic objects.
scoped_ptr<Cursor> result_cursor;
TupleSchema schema;
scoped_ptr<View> input_view;
};
int main(void) {
// Data set for sorting.
const unsigned row_count = 8;
int32 ids[row_count] = {1, 2, 3, 4, 5, 6, 7, 8};
double grades[row_count] = {4.5, 4.2, 3.5, 4.8, 4.2, 3.9, 3.2, 4.8};
SortingTest sorttest;
sorttest.LoadData(ids, grades, row_count);
sorttest.PrepareSort();
sorttest.TestResults(ids, grades, row_count);
return 0;
}
// Author: tomasz.kaftal@gmail.com (Tomasz Kaftal)
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// This tutorial is the second in the guide and it picks up where primer.cc left
// off. For more information see the README file in this directory.
//
// The file contains two larger test cases which cover more complex
// aggregations and sorting. Basic memory management will also be touched
// upon including creating StringPiece objects using a memory arena.
#include <algorithm>
using std::copy;
using std::max;
using std::min;
using std::reverse;
using std::sort;
using std::swap;
#include <map>
using std::map;
using std::multimap;
#include <set>
using std::multiset;
using std::set;
#include <utility>
using std::make_pair;
using std::pair;
#include "gtest/gtest.h"
#include "supersonic/supersonic.h"
#include "supersonic/cursor/core/sort.h"
#include "supersonic/cursor/infrastructure/ordering.h"
#include "supersonic/utils/strings/stringpiece.h"
// Include some map utilities to use for result verification.
#include "supersonic/utils/map-util.h"
using supersonic::TupleSchema;
using supersonic::View;
using supersonic::ResultView;
using supersonic::Attribute;
using supersonic::Arena;
using supersonic::AggregationSpecification;
using supersonic::Operation;
using supersonic::CompoundSingleSourceProjector;
using supersonic::SingleSourceProjector;
using supersonic::ProjectNamedAttribute;
using supersonic::ProjectNamedAttributeAs;
using supersonic::ProjectAttributeAt;
using supersonic::GroupAggregate;
using supersonic::Cursor;
using supersonic::SucceedOrDie;
using supersonic::Block;
using supersonic::HeapBufferAllocator;
using supersonic::ViewCopier;
using supersonic::NO_SELECTOR;
using supersonic::MAX;
using supersonic::MIN;
using supersonic::INT32;
using supersonic::NOT_NULLABLE;
using supersonic::STRING;
using supersonic::BOOL;
using supersonic::DOUBLE;
using supersonic::SortOrder;
using supersonic::Sort;
using supersonic::ASCENDING;
class SortingTest {
public:
void SetUp() {
// Simple two-column schema to use for sorting by the "grade" attribute.
schema.add_attribute(Attribute("id", INT32, NOT_NULLABLE));
schema.add_attribute(Attribute("grade", DOUBLE, NOT_NULLABLE));
input_view.reset(new View(schema));
}
void PrepareSort() {
// First, we'll need to specify the column by which the sorting should be
// carried out. To do it we once again employ a single source projector.
scoped_ptr<const SingleSourceProjector>
projector(ProjectNamedAttribute("grade"));
scoped_ptr<SortOrder> sort_order(new SortOrder());
sort_order->add(projector.release(), ASCENDING);
const size_t mem_limit = 128;
scoped_ptr<Operation> sort(Sort(sort_order.release(),
NULL,
mem_limit,
ScanView(*input_view)));
result_cursor.reset(SucceedOrDie(sort->CreateCursor()));
}
void LoadData(const int32* ids,
const double* grades,
size_t row_count) {
// Load up the data columns.
input_view->set_row_count(row_count);
input_view->mutable_column(0)->Reset(ids, NULL);
input_view->mutable_column(1)->Reset(grades, NULL);
}
typedef pair<int32, double> entry;
typedef map<entry, int32>::const_iterator entry_it;
void TestResults(const int32* ids,
const double* grades,
size_t row_count) {
scoped_ptr<Block> result_space(new Block(schema,
HeapBufferAllocator::Get()));
result_space->Reallocate(row_count);
ViewCopier copier(schema, /* deep copy */ true);
unsigned offset = 0;
// Create a result view much like before.
scoped_ptr<ResultView> rv(new ResultView(result_cursor->Next(1024)));
while (!rv->is_done()) {
const View& view = rv->view();
unsigned view_row_count = view.row_count();
unsigned rows_copied = copier.Copy(view_row_count,
view,
offset,
result_space.get());
// The returned number of copied rows will match the specified
// count, unless there have been allocation errors.
// EXPECT_EQ(view_row_count, rows_copied);
offset += rows_copied;
rv.reset(new ResultView(result_cursor->Next(1024)));
}
const View& result_view(result_space->view());
// ASSERT_EQ(2, result_view.column_count());
// ASSERT_EQ(row_count, result_view.row_count());
// Test if the result data is correctly sorted. Previous assertions
// will fail if there are no results.
/*for (unsigned i = 1; i < row_count; ++i) {
EXPECT_TRUE(result_view.column(1).typed_data<DOUBLE>()[i] >=
result_view.column(1).typed_data<DOUBLE>()[i - 1]);
}*/
// Check if the sorted entries are exactly the ones we
// received as input.
map<entry, int32> occurences;
for (unsigned i = 0; i < row_count; ++i) {
entry key(ids[i], grades[i]);
if (!ContainsKey(occurences, key)) {
occurences[key] = 0;
}
occurences[key]++;
}
/* for (unsigned i = 0; i < row_count; ++i) {
entry key(result_view.column(0).typed_data<INT32>()[i],
result_view.column(1).typed_data<DOUBLE>()[i]);
EXPECT_TRUE(ContainsKey(occurences, key)) << "Invalid value ("
<< key.first
<< ", "
<< key.second
<< ") appeared during sorting!";
occurences[key]--;
}*/
/* for (entry_it it = occurences.begin(); it != occurences.end(); ++it) {
EXPECT_EQ(0, it->second) << "Value ("
<< it->first.first
<< ", "
<< it->first.second
<< ") invalidly "
<< (it->second > 0 ? "removed" : "created")
<< " during sorting!";
}*/
}
// Supersonic objects.
scoped_ptr<Cursor> result_cursor;
TupleSchema schema;
scoped_ptr<View> input_view;
};
int main(void) {
// Data set for sorting.
const unsigned row_count = 8;
int32 ids[row_count] = {1, 2, 3, 4, 5, 6, 7, 8};
double grades[row_count] = {4.5, 4.2, 3.5, 4.8, 4.2, 3.9, 3.2, 4.8};
SortingTest sorttest;
sorttest.LoadData(ids, grades, row_count);
sorttest.PrepareSort();
sorttest.TestResults(ids, grades, row_count);
return 0;
}