     std::vector<int> data(dev_vec.size());
     thrust::copy(dev_vec.begin(), dev_vec.end(), data.begin());
     return data;
+}
+
+gdf_error
+call_gdf_single_column_test(
+        const std::vector<int> &l,
+        const std::vector<int> &r,
+        thrust::device_vector<int> &dl,
+        thrust::device_vector<int> &dr,
+        thrust::device_vector<int> &out_left_pos,
+        thrust::device_vector<int> &out_right_pos,
+        const std::function<gdf_error(gdf_column *, gdf_column *,
+            gdf_join_result_type **)> &f) {
+    dl.resize(l.size()); dr.resize(r.size());
+    thrust::copy(l.begin(), l.end(), dl.begin());
+    thrust::copy(r.begin(), r.end(), dr.begin());
+
+    gdf_column gdl = create_gdf_column(dl);
+    gdf_column gdr = create_gdf_column(dr);
+
+    gdf_join_result_type *out;
+    gdf_error err = f(&gdl, &gdr, &out);
+    size_t len = gdf_join_result_size(out);
+    size_t hlen = len/2;
+    int* out_ptr = reinterpret_cast<int*>(gdf_join_result_data(out));
+    thrust::device_vector<int> out_data(out_ptr, out_ptr + len);
+
+    thrust::sort_by_key(out_data.begin() + hlen, out_data.end(), out_data.begin());
+    thrust::sort_by_key(out_data.begin(), out_data.begin() + hlen, out_data.begin() + hlen);
+    out_left_pos.resize(hlen);
+    out_right_pos.resize(hlen);
+    thrust::copy(out_data.begin(), out_data.begin() + out_left_pos.size(), out_left_pos.begin());
+    thrust::copy(out_data.begin() + out_right_pos.size(), out_data.end(), out_right_pos.begin());
+
+    return err;
 }
 
 gdf_error

#include <cstdlib>
#include <iostream>
#include <vector>

#include <thrust/device_vector.h>
#include <thrust/sort.h>

    ASSERT_EQ(err, GDF_SUCCESS);
}

TEST(join_TEST, gdf_inner_join) {
    std::vector<int> l{0, 0, 1, 2, 3};
    std::vector<int> r{0, 1, 2, 2, 3};
    thrust::device_vector<int> dl, dr, l_pos, r_pos;

    auto err = call_gdf_single_column_test(l, r, dl, dr, l_pos, r_pos, gdf_inner_join_generic);

    thrust::device_vector<int> l_idx(l_pos.size());
    thrust::device_vector<int> r_idx(r_pos.size());

    thrust::gather(l_pos.begin(), l_pos.end(), dl.begin(), l_idx.begin());
    thrust::gather(r_pos.begin(), r_pos.end(), dr.begin(), r_idx.begin());

    EXPECT_THAT(host_vec(l_idx), ElementsAreArray(host_vec(r_idx)));
    EXPECT_THAT(host_vec(l_pos), ElementsAre(0, 1, 2, 3, 3, 4));
    EXPECT_THAT(host_vec(r_pos), ElementsAre(0, 0, 1, 2, 3, 4));

    ASSERT_EQ(err, GDF_SUCCESS);
}

TEST(join_TEST, gdf_left_join) {
    std::vector<int> l{0, 0, 4, 5, 5};
    std::vector<int> r{0, 0, 2, 3, 5};
    thrust::device_vector<int> dl, dr, l_pos, r_pos;

    auto err = call_gdf_single_column_test(l, r, dl, dr, l_pos, r_pos, gdf_left_join_generic);

    thrust::device_vector<int> l_idx(l_pos.size());
    thrust::device_vector<int> r_idx(r_pos.size());

    thrust::gather(l_pos.begin(), l_pos.end(), dl.begin(), l_idx.begin());
    thrust::gather(r_pos.begin(), r_pos.end(), dr.begin(), r_idx.begin());

    EXPECT_THAT(host_vec(l_idx), ElementsAre(0, 0, 0, 0, 4, 5, 5));
    EXPECT_THAT(host_vec(l_pos), ElementsAre(0, 0, 1, 1, 2, 3, 4));
    EXPECT_THAT(host_vec(r_pos), ElementsAre(0, 1, 0, 1, -1, 4, 4));

    ASSERT_EQ(err, GDF_SUCCESS);
}
