diff --git a/cpp/src/search/search.cu b/cpp/src/search/search.cu index 051d302c710..7c37b8e4d75 100644 --- a/cpp/src/search/search.cu +++ b/cpp/src/search/search.cu @@ -18,6 +18,7 @@ #include #include #include +#include #include #include #include @@ -25,10 +26,12 @@ #include #include #include +#include #include #include +#include #include #include @@ -75,71 +78,56 @@ std::unique_ptr search_ordered(table_view const& t, rmm::cuda_stream_view stream, rmm::mr::device_memory_resource* mr) { + CUDF_EXPECTS( + column_order.empty() or static_cast(t.num_columns()) == column_order.size(), + "Mismatch between number of columns and column order."); + CUDF_EXPECTS( + null_precedence.empty() or static_cast(t.num_columns()) == null_precedence.size(), + "Mismatch between number of columns and null precedence."); + // Allocate result column - std::unique_ptr result = make_numeric_column( + auto result = make_numeric_column( data_type{type_to_id()}, values.num_rows(), mask_state::UNALLOCATED, stream, mr); - - mutable_column_view result_view = result.get()->mutable_view(); + auto const result_out = result->mutable_view().data(); // Handle empty inputs if (t.num_rows() == 0) { - CUDA_TRY(cudaMemsetAsync( - result_view.data(), 0, values.num_rows() * sizeof(size_type), stream.value())); + CUDA_TRY(cudaMemsetAsync(result_out, 0, values.num_rows() * sizeof(size_type), stream.value())); return result; } - if (not column_order.empty()) { - CUDF_EXPECTS(static_cast(t.num_columns()) == column_order.size(), - "Mismatch between number of columns and column order."); - } - - if (not null_precedence.empty()) { - CUDF_EXPECTS(static_cast(t.num_columns()) == null_precedence.size(), - "Mismatch between number of columns and null precedence."); - } - // This utility will ensure all corresponding dictionary columns have matching keys. // It will return any new dictionary columns created as well as updated table_views. - auto matched = dictionary::detail::match_dictionaries({t, values}, stream); - auto d_t = table_device_view::create(matched.second.front(), stream); - auto d_values = table_device_view::create(matched.second.back(), stream); - auto count_it = thrust::make_counting_iterator(0); - - rmm::device_vector d_column_order(column_order.begin(), column_order.end()); - rmm::device_vector d_null_precedence(null_precedence.begin(), null_precedence.end()); - + auto const matched = dictionary::detail::match_dictionaries({t, values}, stream); + + // 0-table_view, 1-column_order, 2-null_precedence, 3-validity_columns + auto const t_flattened = + structs::detail::flatten_nested_columns(matched.second.front(), column_order, null_precedence); + auto const values_flattened = + structs::detail::flatten_nested_columns(matched.second.back(), {}, {}); + + auto const t_d = table_device_view::create(std::get<0>(t_flattened), stream); + auto const values_d = table_device_view::create(std::get<0>(values_flattened), stream); + auto const& lhs = find_first ? *t_d : *values_d; + auto const& rhs = find_first ? *values_d : *t_d; + + auto const& column_order_flattened = std::get<1>(t_flattened); + auto const& null_precedence_flattened = std::get<2>(t_flattened); + auto const column_order_dv = detail::make_device_uvector_async(column_order_flattened, stream); + auto const null_precedence_dv = + detail::make_device_uvector_async(null_precedence_flattened, stream); + + auto const count_it = thrust::make_counting_iterator(0); if (has_nulls(t) or has_nulls(values)) { - auto ineq_op = - (find_first) - ? row_lexicographic_comparator( - *d_t, *d_values, d_column_order.data().get(), d_null_precedence.data().get()) - : row_lexicographic_comparator( - *d_values, *d_t, d_column_order.data().get(), d_null_precedence.data().get()); - - launch_search(count_it, - count_it, - t.num_rows(), - values.num_rows(), - result_view.data(), - ineq_op, - find_first, - stream); + auto const comp = row_lexicographic_comparator( + lhs, rhs, column_order_dv.data(), null_precedence_dv.data()); + launch_search( + count_it, count_it, t.num_rows(), values.num_rows(), result_out, comp, find_first, stream); } else { - auto ineq_op = - (find_first) - ? row_lexicographic_comparator( - *d_t, *d_values, d_column_order.data().get(), d_null_precedence.data().get()) - : row_lexicographic_comparator( - *d_values, *d_t, d_column_order.data().get(), d_null_precedence.data().get()); - - launch_search(count_it, - count_it, - t.num_rows(), - values.num_rows(), - result_view.data(), - ineq_op, - find_first, - stream); + auto const comp = row_lexicographic_comparator( + lhs, rhs, column_order_dv.data(), null_precedence_dv.data()); + launch_search( + count_it, count_it, t.num_rows(), values.num_rows(), result_out, comp, find_first, stream); } return result; diff --git a/cpp/tests/CMakeLists.txt b/cpp/tests/CMakeLists.txt index 342ec9145fd..489fc193175 100644 --- a/cpp/tests/CMakeLists.txt +++ b/cpp/tests/CMakeLists.txt @@ -280,7 +280,10 @@ ConfigureTest(FILLING_TEST ################################################################################################### # - search test ----------------------------------------------------------------------------------- -ConfigureTest(SEARCH_TEST search/search_test.cpp) +ConfigureTest(SEARCH_TEST + search/search_dictionary_test.cpp + search/search_struct_test.cpp + search/search_test.cpp) ################################################################################################### # - reshape test ---------------------------------------------------------------------------------- diff --git a/cpp/tests/search/search_dictionary_test.cpp b/cpp/tests/search/search_dictionary_test.cpp new file mode 100644 index 00000000000..6b1caa5ed6f --- /dev/null +++ b/cpp/tests/search/search_dictionary_test.cpp @@ -0,0 +1,107 @@ +/* + * Copyright (c) 2019-2021, NVIDIA CORPORATION. + * + * 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. + */ + +#include +#include +#include +#include + +#include + +struct DictionarySearchTest : public cudf::test::BaseFixture { +}; + +using cudf::numeric_scalar; +using cudf::size_type; +using cudf::string_scalar; +using cudf::test::fixed_width_column_wrapper; + +TEST_F(DictionarySearchTest, search_dictionary) +{ + cudf::test::dictionary_column_wrapper input( + {"", "", "10", "10", "20", "20", "30", "40"}, {0, 0, 1, 1, 1, 1, 1, 1}); + cudf::test::dictionary_column_wrapper values( + {"", "08", "10", "11", "30", "32", "90"}, {0, 1, 1, 1, 1, 1, 1}); + + auto result = cudf::upper_bound({cudf::table_view{{input}}}, + {cudf::table_view{{values}}}, + {cudf::order::ASCENDING}, + {cudf::null_order::BEFORE}); + fixed_width_column_wrapper expect_upper{2, 2, 4, 4, 7, 7, 8}; + CUDF_TEST_EXPECT_COLUMNS_EQUAL(*result, expect_upper); + + result = cudf::lower_bound({cudf::table_view{{input}}}, + {cudf::table_view{{values}}}, + {cudf::order::ASCENDING}, + {cudf::null_order::BEFORE}); + fixed_width_column_wrapper expect_lower{0, 2, 2, 4, 6, 7, 8}; + CUDF_TEST_EXPECT_COLUMNS_EQUAL(*result, expect_lower); +} + +TEST_F(DictionarySearchTest, search_table_dictionary) +{ + fixed_width_column_wrapper column_0{{10, 10, 20, 20, 20, 20, 20, 20, 20, 50, 30}, + {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0}}; + fixed_width_column_wrapper column_1{{5.0, 6.0, .5, .5, .5, .5, .7, .7, .7, .7, .5}, + {1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1}}; + cudf::test::dictionary_column_wrapper column_2{ + {90, 95, 77, 78, 79, 76, 61, 62, 63, 41, 50}, {1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1}}; + cudf::table_view input({column_0, column_1, column_2}); + + fixed_width_column_wrapper values_0{{10, 40, 20}, {1, 0, 1}}; + fixed_width_column_wrapper values_1{{6., .5, .5}, {0, 1, 1}}; + cudf::test::dictionary_column_wrapper values_2{{95, 50, 77}, {1, 1, 0}}; + cudf::table_view values({values_0, values_1, values_2}); + + std::vector order_flags{ + {cudf::order::ASCENDING, cudf::order::ASCENDING, cudf::order::DESCENDING}}; + std::vector null_order_flags{ + {cudf::null_order::AFTER, cudf::null_order::AFTER, cudf::null_order::AFTER}}; + + auto result = cudf::lower_bound(input, values, order_flags, null_order_flags); + fixed_width_column_wrapper expect_lower{1, 10, 2}; + CUDF_TEST_EXPECT_COLUMNS_EQUAL(*result, expect_lower); + + result = cudf::upper_bound(input, values, order_flags, null_order_flags); + fixed_width_column_wrapper expect_upper{2, 11, 6}; + CUDF_TEST_EXPECT_COLUMNS_EQUAL(*result, expect_upper); +} + +TEST_F(DictionarySearchTest, contains_dictionary) +{ + cudf::test::dictionary_column_wrapper column( + {"00", "00", "17", "17", "23", "23", "29"}); + EXPECT_TRUE(cudf::contains(column, string_scalar{"23"})); + EXPECT_FALSE(cudf::contains(column, string_scalar{"28"})); + + cudf::test::dictionary_column_wrapper needles({"00", "17", "23", "27"}); + fixed_width_column_wrapper expect{1, 1, 1, 1, 1, 1, 0}; + auto result = cudf::contains(column, needles); + CUDF_TEST_EXPECT_COLUMNS_EQUAL(*result, expect); +} + +TEST_F(DictionarySearchTest, contains_nullable_dictionary) +{ + cudf::test::dictionary_column_wrapper column({0, 0, 17, 17, 23, 23, 29}, + {1, 0, 1, 1, 1, 1, 1}); + EXPECT_TRUE(cudf::contains(column, numeric_scalar{23})); + EXPECT_FALSE(cudf::contains(column, numeric_scalar{28})); + + cudf::test::dictionary_column_wrapper needles({0, 17, 23, 27}); + fixed_width_column_wrapper expect({1, 0, 1, 1, 1, 1, 0}, {1, 0, 1, 1, 1, 1, 1}); + auto result = cudf::contains(column, needles); + CUDF_TEST_EXPECT_COLUMNS_EQUAL(*result, expect); +} diff --git a/cpp/tests/search/search_struct_test.cpp b/cpp/tests/search/search_struct_test.cpp new file mode 100644 index 00000000000..50c326269a0 --- /dev/null +++ b/cpp/tests/search/search_struct_test.cpp @@ -0,0 +1,222 @@ +/* + * Copyright (c) 2021, NVIDIA CORPORATION. + * + * 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. + */ + +#include +#include +#include +#include +#include + +#include +#include +#include + +using bools_col = cudf::test::fixed_width_column_wrapper; +using int32s_col = cudf::test::fixed_width_column_wrapper; +using structs_col = cudf::test::structs_column_wrapper; +using strings_col = cudf::test::strings_column_wrapper; + +constexpr bool print_all{false}; // For debugging only +constexpr int32_t null{0}; // Mark for null child elements +constexpr int32_t XXX{0}; // Mark for null struct elements + +template +struct TypedStructSearchTest : public cudf::test::BaseFixture { +}; + +using TestTypes = cudf::test::Concat; + +TYPED_TEST_CASE(TypedStructSearchTest, TestTypes); + +namespace { +auto search_bounds(cudf::column_view const& t_col_view, + std::unique_ptr const& values_col, + std::vector const& column_orders = {cudf::order::ASCENDING}, + std::vector const& null_precedence = { + cudf::null_order::BEFORE}) +{ + auto const t = cudf::table_view{std::vector{t_col_view}}; + auto const values = cudf::table_view{std::vector{values_col->view()}}; + auto result_lower_bound = cudf::lower_bound(t, values, column_orders, null_precedence); + auto result_upper_bound = cudf::upper_bound(t, values, column_orders, null_precedence); + return std::make_pair(std::move(result_lower_bound), std::move(result_upper_bound)); +} + +auto search_bounds(std::unique_ptr const& t_col, + std::unique_ptr const& values_col, + std::vector const& column_orders = {cudf::order::ASCENDING}, + std::vector const& null_precedence = { + cudf::null_order::BEFORE}) +{ + return search_bounds(t_col->view(), values_col, column_orders, null_precedence); +} + +auto null_at(cudf::size_type idx) { return cudf::test::iterator_with_null_at(idx); } + +} // namespace + +// Test case when all input columns are empty +TYPED_TEST(TypedStructSearchTest, EmptyInputTest) +{ + using col_wrapper = cudf::test::fixed_width_column_wrapper; + + auto child_col_t = col_wrapper{}; + auto const structs_t = structs_col{{child_col_t}, std::vector{}}.release(); + + auto child_col_values = col_wrapper{}; + auto const structs_values = structs_col{{child_col_values}, std::vector{}}.release(); + + auto const results = search_bounds(structs_t, structs_values); + auto const expected = int32s_col{}; + CUDF_TEST_EXPECT_COLUMNS_EQUAL(expected, results.first->view(), print_all); + CUDF_TEST_EXPECT_COLUMNS_EQUAL(expected, results.second->view(), print_all); +} + +TYPED_TEST(TypedStructSearchTest, TrivialInputTests) +{ + using col_wrapper = cudf::test::fixed_width_column_wrapper; + + auto child_col_t = col_wrapper{10, 20, 30, 40, 50}; + auto const structs_t = structs_col{{child_col_t}}.release(); + + auto child_col_values1 = col_wrapper{0, 1, 2, 3, 4}; + auto const structs_values1 = structs_col{{child_col_values1}}.release(); + + auto child_col_values2 = col_wrapper{100, 101, 102, 103, 104}; + auto const structs_values2 = structs_col{{child_col_values2}}.release(); + + auto const results1 = search_bounds(structs_t, structs_values1); + auto const expected1 = int32s_col{0, 0, 0, 0, 0}; + CUDF_TEST_EXPECT_COLUMNS_EQUAL(expected1, results1.first->view(), print_all); + CUDF_TEST_EXPECT_COLUMNS_EQUAL(expected1, results1.second->view(), print_all); + + auto const results2 = search_bounds(structs_t, structs_values2); + auto const expected2 = int32s_col{5, 5, 5, 5, 5}; + CUDF_TEST_EXPECT_COLUMNS_EQUAL(expected2, results2.first->view(), print_all); + CUDF_TEST_EXPECT_COLUMNS_EQUAL(expected2, results2.second->view(), print_all); +} + +TYPED_TEST(TypedStructSearchTest, SlicedColumnInputTests) +{ + using col_wrapper = cudf::test::fixed_width_column_wrapper; + + auto child_col_values = col_wrapper{0, 1, 2, 3, 4, 5}; + auto const structs_values = structs_col{child_col_values}.release(); + + auto child_col_t = col_wrapper{0, 1, 2, 2, 2, 2, 3, 3, 4, 4}; + auto const structs_t_original = structs_col{child_col_t}.release(); + + auto structs_t = cudf::slice(structs_t_original->view(), {0, 10})[0]; // the entire column t + auto results = search_bounds(structs_t, structs_values); + auto expected_lower_bound = int32s_col{0, 1, 2, 6, 8, 10}; + auto expected_upper_bound = int32s_col{1, 2, 6, 8, 10, 10}; + CUDF_TEST_EXPECT_COLUMNS_EQUAL(expected_lower_bound, results.first->view(), print_all); + CUDF_TEST_EXPECT_COLUMNS_EQUAL(expected_upper_bound, results.second->view(), print_all); + + structs_t = cudf::slice(structs_t_original->view(), {0, 5})[0]; + results = search_bounds(structs_t, structs_values); + expected_lower_bound = int32s_col{0, 1, 2, 5, 5, 5}; + expected_upper_bound = int32s_col{1, 2, 5, 5, 5, 5}; + CUDF_TEST_EXPECT_COLUMNS_EQUAL(expected_lower_bound, results.first->view(), print_all); + CUDF_TEST_EXPECT_COLUMNS_EQUAL(expected_upper_bound, results.second->view(), print_all); + + structs_t = cudf::slice(structs_t_original->view(), {5, 10})[0]; + results = search_bounds(structs_t, structs_values); + expected_lower_bound = int32s_col{0, 0, 0, 1, 3, 5}; + expected_upper_bound = int32s_col{0, 0, 1, 3, 5, 5}; + CUDF_TEST_EXPECT_COLUMNS_EQUAL(expected_lower_bound, results.first->view(), print_all); + CUDF_TEST_EXPECT_COLUMNS_EQUAL(expected_upper_bound, results.second->view(), print_all); +} + +TYPED_TEST(TypedStructSearchTest, SimpleInputWithNullsTests) +{ + using col_wrapper = cudf::test::fixed_width_column_wrapper; + + auto child_col_values = col_wrapper{{1, null, 70, XXX, 2, 100}, null_at(1)}; + auto const structs_values = structs_col{{child_col_values}, null_at(3)}.release(); + + // Sorted asc, nulls first + auto child_col_t = col_wrapper{{XXX, null, 0, 1, 2, 2, 2, 2, 3, 3, 4}, null_at(1)}; + auto structs_t = structs_col{{child_col_t}, null_at(0)}.release(); + + auto results = + search_bounds(structs_t, structs_values, {cudf::order::ASCENDING}, {cudf::null_order::BEFORE}); + auto expected_lower_bound = int32s_col{3, 1, 11, 0, 4, 11}; + auto expected_upper_bound = int32s_col{4, 2, 11, 1, 8, 11}; + CUDF_TEST_EXPECT_COLUMNS_EQUAL(expected_lower_bound, results.first->view(), print_all); + CUDF_TEST_EXPECT_COLUMNS_EQUAL(expected_upper_bound, results.second->view(), print_all); + + // Sorted asc, nulls last + child_col_t = col_wrapper{{0, 1, 2, 2, 2, 2, 3, 3, 4, null, XXX}, null_at(9)}; + structs_t = structs_col{{child_col_t}, null_at(10)}.release(); + results = + search_bounds(structs_t, structs_values, {cudf::order::ASCENDING}, {cudf::null_order::AFTER}); + expected_lower_bound = int32s_col{1, 0, 10, 10, 2, 10}; + expected_upper_bound = int32s_col{2, 0, 10, 11, 6, 10}; + CUDF_TEST_EXPECT_COLUMNS_EQUAL(expected_lower_bound, results.first->view(), print_all); + CUDF_TEST_EXPECT_COLUMNS_EQUAL(expected_upper_bound, results.second->view(), print_all); + + // Sorted dsc, nulls first + child_col_t = col_wrapper{{XXX, null, 4, 3, 3, 2, 2, 2, 2, 1, 0}, null_at(1)}; + structs_t = structs_col{{child_col_t}, null_at(0)}.release(); + results = + search_bounds(structs_t, structs_values, {cudf::order::DESCENDING}, {cudf::null_order::BEFORE}); + expected_lower_bound = int32s_col{9, 11, 0, 11, 5, 0}; + expected_upper_bound = int32s_col{10, 11, 0, 11, 9, 0}; + CUDF_TEST_EXPECT_COLUMNS_EQUAL(expected_lower_bound, results.first->view(), print_all); + CUDF_TEST_EXPECT_COLUMNS_EQUAL(expected_upper_bound, results.second->view(), print_all); + + // Sorted dsc, nulls last + child_col_t = col_wrapper{{4, 3, 3, 2, 2, 2, 2, 1, 0, null, XXX}, null_at(9)}; + structs_t = structs_col{{child_col_t}, null_at(10)}.release(); + results = + search_bounds(structs_t, structs_values, {cudf::order::DESCENDING}, {cudf::null_order::AFTER}); + expected_lower_bound = int32s_col{7, 11, 0, 0, 3, 0}; + expected_upper_bound = int32s_col{8, 11, 0, 0, 7, 0}; + CUDF_TEST_EXPECT_COLUMNS_EQUAL(expected_lower_bound, results.first->view(), print_all); + CUDF_TEST_EXPECT_COLUMNS_EQUAL(expected_upper_bound, results.second->view(), print_all); +} + +TYPED_TEST(TypedStructSearchTest, ComplexStructTest) +{ + // Testing on struct. + using col_wrapper = cudf::test::fixed_width_column_wrapper; + + auto names_column_t = + strings_col{"Cherry", "Kiwi", "Lemon", "Newton", "Tomato", /*NULL*/ "Washington"}; + auto ages_column_t = col_wrapper{{5, 10, 15, 20, null, XXX}, null_at(4)}; + auto is_human_col_t = bools_col{false, false, false, false, false, /*NULL*/ true}; + + auto const structs_t = + structs_col{{names_column_t, ages_column_t, is_human_col_t}, null_at(5)}.release(); + + auto names_column_values = strings_col{"Bagel", "Tomato", "Lemonade", /*NULL*/ "Donut", "Butter"}; + auto ages_column_values = col_wrapper{{10, null, 15, XXX, 17}, null_at(1)}; + auto is_human_col_values = bools_col{false, false, true, /*NULL*/ true, true}; + auto const structs_values = + structs_col{{names_column_values, ages_column_values, is_human_col_values}, null_at(3)} + .release(); + + auto const results = + search_bounds(structs_t, structs_values, {cudf::order::ASCENDING}, {cudf::null_order::AFTER}); + auto const expected_lower_bound = int32s_col{0, 4, 3, 5, 0}; + auto const expected_upper_bound = int32s_col{0, 5, 3, 6, 0}; + CUDF_TEST_EXPECT_COLUMNS_EQUAL(expected_lower_bound, results.first->view(), print_all); + CUDF_TEST_EXPECT_COLUMNS_EQUAL(expected_upper_bound, results.second->view(), print_all); +} diff --git a/cpp/tests/search/search_test.cpp b/cpp/tests/search/search_test.cpp index f5136f321da..bf52c2609c4 100644 --- a/cpp/tests/search/search_test.cpp +++ b/cpp/tests/search/search_test.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, NVIDIA CORPORATION. + * Copyright (c) 2019-2021, NVIDIA CORPORATION. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -1618,83 +1618,6 @@ TEST_F(SearchTest, contains_nullable_column_false_string) ASSERT_EQ(result, expect); } -TEST_F(SearchTest, search_dictionary) -{ - cudf::test::dictionary_column_wrapper input( - {"", "", "10", "10", "20", "20", "30", "40"}, {0, 0, 1, 1, 1, 1, 1, 1}); - cudf::test::dictionary_column_wrapper values( - {"", "08", "10", "11", "30", "32", "90"}, {0, 1, 1, 1, 1, 1, 1}); - - auto result = cudf::upper_bound({cudf::table_view{{input}}}, - {cudf::table_view{{values}}}, - {cudf::order::ASCENDING}, - {cudf::null_order::BEFORE}); - fixed_width_column_wrapper expect_upper{2, 2, 4, 4, 7, 7, 8}; - CUDF_TEST_EXPECT_COLUMNS_EQUAL(*result, expect_upper); - - result = cudf::lower_bound({cudf::table_view{{input}}}, - {cudf::table_view{{values}}}, - {cudf::order::ASCENDING}, - {cudf::null_order::BEFORE}); - fixed_width_column_wrapper expect_lower{0, 2, 2, 4, 6, 7, 8}; - CUDF_TEST_EXPECT_COLUMNS_EQUAL(*result, expect_lower); -} - -TEST_F(SearchTest, search_table_dictionary) -{ - fixed_width_column_wrapper column_0{{10, 10, 20, 20, 20, 20, 20, 20, 20, 50, 30}, - {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0}}; - fixed_width_column_wrapper column_1{{5.0, 6.0, .5, .5, .5, .5, .7, .7, .7, .7, .5}, - {1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1}}; - cudf::test::dictionary_column_wrapper column_2{ - {90, 95, 77, 78, 79, 76, 61, 62, 63, 41, 50}, {1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1}}; - cudf::table_view input({column_0, column_1, column_2}); - - fixed_width_column_wrapper values_0{{10, 40, 20}, {1, 0, 1}}; - fixed_width_column_wrapper values_1{{6., .5, .5}, {0, 1, 1}}; - cudf::test::dictionary_column_wrapper values_2{{95, 50, 77}, {1, 1, 0}}; - cudf::table_view values({values_0, values_1, values_2}); - - std::vector order_flags{ - {cudf::order::ASCENDING, cudf::order::ASCENDING, cudf::order::DESCENDING}}; - std::vector null_order_flags{ - {cudf::null_order::AFTER, cudf::null_order::AFTER, cudf::null_order::AFTER}}; - - auto result = cudf::lower_bound(input, values, order_flags, null_order_flags); - fixed_width_column_wrapper expect_lower{1, 10, 2}; - CUDF_TEST_EXPECT_COLUMNS_EQUAL(*result, expect_lower); - - result = cudf::upper_bound(input, values, order_flags, null_order_flags); - fixed_width_column_wrapper expect_upper{2, 11, 6}; - CUDF_TEST_EXPECT_COLUMNS_EQUAL(*result, expect_upper); -} - -TEST_F(SearchTest, contains_dictionary) -{ - cudf::test::dictionary_column_wrapper column( - {"00", "00", "17", "17", "23", "23", "29"}); - EXPECT_TRUE(cudf::contains(column, string_scalar{"23"})); - EXPECT_FALSE(cudf::contains(column, string_scalar{"28"})); - - cudf::test::dictionary_column_wrapper needles({"00", "17", "23", "27"}); - fixed_width_column_wrapper expect{1, 1, 1, 1, 1, 1, 0}; - auto result = cudf::contains(column, needles); - CUDF_TEST_EXPECT_COLUMNS_EQUAL(*result, expect); -} - -TEST_F(SearchTest, contains_nullable_dictionary) -{ - cudf::test::dictionary_column_wrapper column({0, 0, 17, 17, 23, 23, 29}, - {1, 0, 1, 1, 1, 1, 1}); - EXPECT_TRUE(cudf::contains(column, numeric_scalar{23})); - EXPECT_FALSE(cudf::contains(column, numeric_scalar{28})); - - cudf::test::dictionary_column_wrapper needles({0, 17, 23, 27}); - fixed_width_column_wrapper expect({1, 0, 1, 1, 1, 1, 0}, {1, 0, 1, 1, 1, 1, 1}); - auto result = cudf::contains(column, needles); - CUDF_TEST_EXPECT_COLUMNS_EQUAL(*result, expect); -} - TEST_F(SearchTest, multi_contains_some) { using element_type = int64_t;