/** Copyright 2020 Alibaba Group Holding Limited.
 *
 * 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.
 */
#ifndef ENGINES_HQPS_ENGINE_OPERATOR_PROP_UTILS_H_
#define ENGINES_HQPS_ENGINE_OPERATOR_PROP_UTILS_H_

#include <array>
#include <string>
#include <tuple>
#include <vector>

#include "grape/utils/bitset.h"

namespace gs {

// forward declare context
template <typename HEAD_T, int cur_alias, int base_tag, typename... ALIAS_SETS>
class Context;

// forward declare flat_edge_set
template <typename VID_T, typename LabelT, typename T>
class FlatEdgeSet;

template <typename VID_T, typename LabelT, typename EDATA_T>
class SingleLabelEdgeSet;

// forward declare general_edge_set
template <size_t N, typename GI, typename VID_T, typename LabelT, typename... T>
class GeneralEdgeSet;

// UntypedEdgeSet
template <typename VID_T, typename LabelT, typename SUB_GRAPH_T>
class UnTypedEdgeSet;

// forward declare keyed_row_vertex_set
template <typename LabelT, typename KEY_T, typename VID_T, typename... T>
class KeyedRowVertexSetImpl;

// forward declare row_vertex_set
template <typename LabelT, typename VID_T, typename... T>
class RowVertexSetImpl;

// forward declare two_label_vertex_set
template <typename VID_T, typename LabelT, typename... T>
class TwoLabelVertexSetImpl;

template <typename VID_T, typename LabelT, typename... SET_T>
class GeneralVertexSet;

template <typename GI, typename PropTupleT>
struct MultiPropGetterT;

template <typename GI, typename... T>
struct MultiPropGetterT<GI, std::tuple<T...>> {};

template <typename T>
class Collection;

template <typename GRAPH_INTERFACE, typename LabelT, typename... T>
static auto get_prop_getter_from_named_property(
    const GRAPH_INTERFACE& graph, const LabelT& label,
    const std::tuple<NamedProperty<T>...>& named_property) {
  std::array<std::string, sizeof...(T)> prop_names;
  int i = 0;
  std::apply(
      [&prop_names, &i](auto&... named_prop) {
        ((prop_names[i++] = named_prop.name), ...);
      },
      named_property);
  return graph.template GetMultiPropGetter<T...>(label, prop_names);
}

//// Get one property getter for one label
template <typename GRAPH_INTERFACE, typename LabelT, typename... PropT>
static auto get_prop_getter_from_selectors(
    const GRAPH_INTERFACE& graph, const LabelT& label,
    const std::tuple<PropertySelector<PropT>...>& selectors) {
  std::array<std::string, sizeof...(PropT)> prop_names;
  int i = 0;
  std::apply(
      [&prop_names, &i](auto&... named_prop) {
        ((prop_names[i++] = named_prop.prop_name_), ...);
      },
      selectors);
  return graph.template GetMultiPropGetter<PropT...>(label, prop_names);
}
/// get single property getter for one label
template <typename GRAPH_INTERFACE, typename LabelT, typename PropT>
static auto get_single_prop_getter_from_selector(
    const GRAPH_INTERFACE& graph, const LabelT& label,
    const PropertySelector<PropT>& selector) {
  auto prop_name = selector.prop_name_;
  return graph.template GetSinglePropGetter<PropT>(label, prop_name);
}

// Get prop getters from Selector.
template <typename GRAPH_INTERFACE, typename LabelT, size_t num_labels,
          typename... SELECTOR, size_t... Is>
static auto get_prop_getters_from_selectors_impl(
    const GRAPH_INTERFACE& graph, const std::array<LabelT, num_labels>& labels,
    std::tuple<SELECTOR...> selectors, std::index_sequence<Is...>) {
  using prop_getter_t = typename GRAPH_INTERFACE::template multi_prop_getter_t<
      typename SELECTOR::prop_t...>;
  std::array<prop_getter_t, num_labels> prop_getter_array{
      get_prop_getter_from_selectors(graph, labels[Is], selectors)...};
  return prop_getter_array;
}

// Get prop getters from Selector, with label vector.
template <typename GRAPH_INTERFACE, typename LabelT, typename... SELECTOR,
          size_t... Is>
static auto get_prop_getters_from_selectors_impl_label_vec(
    const GRAPH_INTERFACE& graph, const std::vector<LabelT>& labels,
    std::tuple<SELECTOR...> selectors) {
  using prop_getter_t = typename GRAPH_INTERFACE::template multi_prop_getter_t<
      typename SELECTOR::prop_t...>;
  std::vector<prop_getter_t> prop_getter_array;
  for (size_t i = 0; i < labels.size(); ++i) {
    prop_getter_array.emplace_back(
        get_prop_getter_from_selectors(graph, labels[i], selectors));
  };
  return prop_getter_array;
}

template <typename GRAPH_INTERFACE, typename LabelT, size_t num_labels,
          typename... SELECTOR>
static auto get_prop_getters_from_selectors(
    const GRAPH_INTERFACE& graph, const std::array<LabelT, num_labels>& labels,
    std::tuple<SELECTOR...> named_property) {
  return get_prop_getters_from_selectors_impl(
      graph, labels, named_property, std::make_index_sequence<num_labels>{});
}

template <typename GRAPH_INTERFACE, typename LabelT, typename... SELECTOR>
static auto get_prop_getters_from_selectors(
    const GRAPH_INTERFACE& graph, const std::vector<LabelT>& labels,
    std::tuple<SELECTOR...> named_property) {
  return get_prop_getters_from_selectors_impl_label_vec(graph, labels,
                                                        named_property);
}

///////////////////////// prop getter for vertex set
//////////////////////////////

template <int tag_id, typename T>
class VertexInnerIdGetter {
 public:
  using prop_element_t = T;
  VertexInnerIdGetter() {}

  T get_view(const T& ele) const { return ele; }

  template <typename ALL_ELE_T>
  inline auto get_from_all_element(const ALL_ELE_T& all_ele) const {
    return get_view(gs::get_from_tuple<tag_id>(all_ele));
  }

  template <typename ALL_IND_ELE_T>
  inline auto get_from_all_index_element(const ALL_IND_ELE_T& ind_ele) const {
    return get_view(gs::get_from_tuple<tag_id>(ind_ele));
  }
};

template <int tag_id, typename T>
class CollectionInnerIdGetter {
 public:
  using prop_element_t = T;
  CollectionInnerIdGetter() {}

  T get_view(const T& ele) const { return ele; }

  T get_view(const std::tuple<T>& ele) const { return std::get<0>(ele); }

  T get_view(const std::tuple<size_t, T>& ele) const {
    return std::get<1>(ele);
  }

  template <typename ALL_ELE_T>
  inline auto get_from_all_element(const ALL_ELE_T& all_ele) const {
    return get_view(gs::get_from_tuple<tag_id>(all_ele));
  }

  template <typename ALL_IND_ELE_T>
  inline auto get_from_all_index_element(const ALL_IND_ELE_T& ind_ele) const {
    return get_view(std::get<1>(gs::get_from_tuple<tag_id>(ind_ele)));
  }
};

template <int tag_id, typename VID_T>
class VertexGlobalIdGetter {
 public:
  using prop_element_t = GlobalId;
  VertexGlobalIdGetter(const std::vector<VID_T>& vids,
                       std::vector<LabelKey>&& label_vec)
      : vids_(vids), label_vec_(std::move(label_vec)) {}

  GlobalId get_view(const std::tuple<size_t, int32_t, VID_T>& ele) const {
    return GlobalId(label_vec_[std::get<0>(ele)].label_id,
                    vids_[std::get<0>(ele)]);
  }

  // two label set
  GlobalId get_view(const std::tuple<size_t, VID_T>& ele) const {
    return GlobalId(label_vec_[std::get<0>(ele)].label_id,
                    vids_[std::get<0>(ele)]);
  }

  // general vertex set
  template <typename... T>
  GlobalId get_view(
      const std::tuple<size_t, size_t, VID_T, std::tuple<T...>>& ele) const {
    return GlobalId(std::get<1>(ele), std::get<2>(ele));
  }

  template <typename ALL_ELE_T>
  inline auto get_from_all_element(const ALL_ELE_T& all_ele) const {
    return gs::get_from_tuple<tag_id>(all_ele);
  }

  template <typename ALL_IND_ELE_T>
  inline auto get_from_all_index_element(
      const ALL_IND_ELE_T& all_ind_ele) const {
    return get_view(gs::get_from_tuple<tag_id>(all_ind_ele));
  }

 private:
  const std::vector<VID_T>& vids_;
  std::vector<LabelKey> label_vec_;
};

template <int tag_id>
class VertexLabelGetter {
 public:
  using prop_element_t = LabelKey;
  VertexLabelGetter(const std::vector<LabelKey>&& label_keys)
      : label_keys(std::move(label_keys)) {}

  template <typename IND_ELE>
  LabelKey get_view(const IND_ELE& ele) const {
    auto index_ = std::get<0>(ele);
    if (index_ >= label_keys.size()) {
      LOG(FATAL) << "index out of range";
    }
    return label_keys[index_];
  }

  template <typename ALL_ELE_T>
  inline auto get_from_all_element(const ALL_ELE_T& all_ele) const {
    return gs::get_from_tuple<tag_id>(all_ele).label();
  }

  template <typename ALL_IND_ELE_T>
  inline auto get_from_all_index_element(const ALL_IND_ELE_T& ind_ele) const {
    return get_view(gs::get_from_tuple<tag_id>(ind_ele));
  }

 private:
  std::vector<LabelKey> label_keys;
};

template <int tag_id>
class EdgeLabelGetter {
 public:
  using prop_element_t = LabelKey;
  EdgeLabelGetter(const std::vector<LabelKey>&& label_keys)
      : label_keys(std::move(label_keys)) {}

  template <typename IND_ELE>
  LabelKey get_view(const IND_ELE& ele) const {
    auto index_ = std::get<0>(ele);
    if (index_ >= label_keys.size()) {
      LOG(FATAL) << "index out of range";
    }
    return label_keys[index_];
  }

  template <typename ALL_ELE_T>
  inline auto get_from_all_element(const ALL_ELE_T& all_ele) const {
    return get_view(gs::get_from_tuple<tag_id>(all_ele));
  }

  template <typename ALL_IND_ELE_T>
  inline auto get_from_all_index_element(const ALL_IND_ELE_T& all_ele) const {
    return get_view(gs::get_from_tuple<tag_id>(all_ele));
  }

 private:
  std::vector<LabelKey> label_keys;
};

template <int tag_id, typename VID_T, typename... DATA_T>
class InnerIdDataGetter {
 public:
  using prop_element_t = std::tuple<VID_T, std::tuple<DATA_T...>>;
  InnerIdDataGetter(const std::vector<VID_T>& vids,
                    const std::vector<std::tuple<DATA_T...>>& data)
      : vids_(vids), data_(data) {}

  std::tuple<VID_T, std::tuple<DATA_T...>> get_view(
      const std::tuple<size_t, VID_T>& ele) const {
    auto vid = std::get<1>(ele);
    auto idx = std::get<0>(ele);
    CHECK(vid == vids_[idx]);
    return std::make_tuple(vid, std::get<0>(data_[idx]));
  }

  template <typename TUPLE_T>
  auto get_from_all_element(const TUPLE_T& tuple) const {
    auto ind_ele = gs::get_from_tuple<tag_id>(tuple);
    return ind_ele;
  }

 private:
  const std::vector<VID_T>& vids_;
  const std::vector<std::tuple<DATA_T...>>& data_;
};

template <int tag_id, typename PROP_GETTER_T, typename IND_ELE_T>
class GeneralVertexSetPropGetter {
 public:
  using prop_element_t = typename PROP_GETTER_T::value_type;
  using ELE_TUPLE_T = GlobalId;
  GeneralVertexSetPropGetter(std::vector<PROP_GETTER_T>&& getters,
                             const std::vector<grape::Bitset>& bitset)
      : getters_(std::move(getters)), bitset_(bitset) {}

  inline auto get_view(const IND_ELE_T& ind_ele) const {
    auto vid = std::get<2>(ind_ele);
    auto label_ind = std::get<1>(ind_ele);
    CHECK(std::get<0>(ind_ele) < bitset_[label_ind].cardinality());
    CHECK(bitset_[label_ind].get_bit(std::get<0>(ind_ele)));
    return getters_[label_ind].get_view(vid);
  }

  inline auto get_view(const ELE_TUPLE_T& ele) const {
    CHECK(ele.label_id() < bitset_.size());
    return getters_[ele.label_id()].get_view(ele.vid());
  }

  inline auto get_view() const { return get_view(ind_ele_); }

  template <typename TUPLE_T>
  auto get_from_all_element(const TUPLE_T& tuple) const {
    auto ind_ele = gs::get_from_tuple<tag_id>(tuple);
    return get_view(ind_ele);
  }

  template <typename ALL_IND_TUPLE_T>
  auto get_from_all_index_element(const ALL_IND_TUPLE_T& tuple) const {
    auto ind_ele = gs::get_from_tuple<tag_id>(tuple);
    return get_view(ind_ele);
  }

 private:
  IND_ELE_T ind_ele_;
  std::vector<PROP_GETTER_T> getters_;
  const std::vector<grape::Bitset>& bitset_;
};

// For EdgeSetInnerIdGetter, we return a wrapped EdgeObject.
template <int tag_id, typename VID_T, typename EDATA_T>
class EdgeSetInnerIdGetter {
 public:
  using prop_element_t = Edge<VID_T, grape::EmptyType>;
  EdgeSetInnerIdGetter() {}

  template <typename ALL_ELE_T>
  inline auto get_from_all_element(const ALL_ELE_T& all_ele) const {
    auto& tuple = gs::get_from_tuple<tag_id>(all_ele);
    using cur_tuple_t =
        std::remove_const_t<std::remove_reference_t<decltype(tuple)>>;
    if constexpr ((std::tuple_size_v<cur_tuple_t>) > 2) {
      auto src_vid = std::get<0>(tuple);
      auto dst_vid = std::get<1>(tuple);
      return Edge<VID_T, grape::EmptyType>(src_vid, dst_vid);
    } else if constexpr (std::tuple_size_v<cur_tuple_t> == 2) {
      auto& actual_tuple = std::get<1>(tuple);
      auto src_vid = std::get<0>(actual_tuple);
      auto dst_vid = std::get<1>(actual_tuple);
      return Edge<VID_T, grape::EmptyType>(src_vid, dst_vid);
    } else {
      LOG(FATAL) << "tuple size error";
    }
  }

  template <typename ALL_IND_ELE_T>
  inline auto get_from_all_index_element(const ALL_IND_ELE_T& ind_ele) const {
    return get_from_all_element(ind_ele);
  }
};

template <int tag_id, typename T>
class CollectionPropGetter {
 public:
  using prop_element_t = T;
  CollectionPropGetter() {}

  inline auto get_view(const std::tuple<size_t, T>& ele) const {
    return std::get<1>(ele);
  }

  inline auto get_view(const T& ele) const { return ele; }

  inline auto get_view() const { return std::get<1>(ind_ele_); }

  template <typename ALL_ELE_T>
  inline auto get_from_all_element(const ALL_ELE_T& all_ele) const {
    return get_view(gs::get_from_tuple<tag_id>(all_ele));
  }

  template <typename ALL_IND_ELE_T>
  inline auto get_from_all_index_element(const ALL_IND_ELE_T& all_ele) {
    return get_view(gs::get_from_tuple<tag_id>(all_ele));
  }

  template <typename ALL_IND_ELE_T>
  inline void set_ind_ele(const ALL_IND_ELE_T& ind_ele) {
    ind_ele_ = ind_ele;
  }

 private:
  std::tuple<size_t, T> ind_ele_;
};

// Specialize for LabelKey
template <int tag_id>
class CollectionPropGetter<tag_id, LabelKey> {
 public:
  using prop_element_t = LabelKey;
  CollectionPropGetter() {}

  inline auto get_view(const std::tuple<size_t, LabelKey>& ele) const {
    return std::get<1>(ele).label_id;
  }

  inline auto get_view() const { return std::get<1>(ind_ele_); }

  template <typename ALL_ELE_T>
  inline auto get_from_all_element(const ALL_ELE_T& all_ele) const {
    return get_view(gs::get_from_tuple<tag_id>(all_ele));
  }

  template <typename ALL_IND_ELE_T>
  inline auto get_from_all_index_element(const ALL_IND_ELE_T& all_ele) const {
    return get_view(gs::get_from_tuple<tag_id>(all_ele));
  }

  template <typename ALL_IND_ELE_T>
  inline void set_ind_ele(const ALL_IND_ELE_T& ind_ele) {
    ind_ele_ = ind_ele;
  }

 private:
  std::tuple<size_t, LabelKey> ind_ele_;
};

// specialize for collection with only one column
template <int tag_id, typename T>
class CollectionPropGetter<tag_id, std::tuple<T>> {
 public:
  using prop_element_t = T;
  CollectionPropGetter() {}

  inline T get_view(const std::tuple<size_t, T>& ele) const {
    return std::get<1>(ele);
  }

  inline T get_view() const { return std::get<1>(ind_ele_); }

  template <typename ALL_ELE_T>
  inline auto get_from_all_element(const ALL_ELE_T& all_ele) const {
    return gs::get_from_tuple<tag_id>(all_ele);
  }

  template <typename ALL_IND_ELE_T>
  inline auto get_from_all_index_element(const ALL_IND_ELE_T& ind_ele) const {
    return get_view(gs::get_from_tuple<tag_id>(ind_ele));
  }

  template <typename ALL_IND_ELE_T>
  inline void set_ind_ele(const ALL_IND_ELE_T& ind_ele) {
    ind_ele_ = ind_ele;
  }

 private:
  std::tuple<size_t, T> ind_ele_;
};

template <int tag_id, typename index_ele_tuple_t>
class FlatEdgeSetPropGetter {
 public:
  using prop_element_t = typename std::tuple_element_t<
      0, typename std::tuple_element_t<
             2, typename std::tuple_element_t<1, index_ele_tuple_t>>>;
  FlatEdgeSetPropGetter() {}

  inline auto get_view(const index_ele_tuple_t& ind_ele) const {
    return std::get<0>(std::get<2>(std::get<1>(ind_ele)));
  }

  inline auto get_view()
      const {  // const std::tuple<size_t, int32_t, VID_T>& ind_ele
    return std::get<0>(std::get<2>(std::get<1>(ind_ele_)));
  }

  template <typename ALL_ELE_T>
  inline auto get_from_all_element(const ALL_ELE_T& all_ele) const {
    auto& my_ele = gs::get_from_tuple<tag_id>(all_ele);
    return get_view(my_ele);
  }

  template <typename ALL_IND_ELE_T>
  inline auto get_from_all_index_element(const ALL_IND_ELE_T& ind_ele) const {
    return get_view(gs::get_from_tuple<tag_id>(ind_ele));
  }

  template <typename ALL_IND_ELE_T>
  inline void set_ind_ele(const ALL_IND_ELE_T& ind_ele) {
    ind_ele_ = gs::get_from_tuple<tag_id>(ind_ele);
  }

 private:
  index_ele_tuple_t ind_ele_;
};

template <int tag_id, typename PropT>
class UnTypedEdgeSetPropGetter {
 public:
  using index_ele_tuple_t = std::tuple<size_t, vid_t, vid_t, Any>;
  using prop_element_t = PropT;
  UnTypedEdgeSetPropGetter() {}

  inline auto get_view(const index_ele_tuple_t& ind_ele) const {
    PropT prop;
    ConvertAny<PropT>::to(std::get<3>(ind_ele), prop);
    return prop;
  }

  template <typename ALL_ELE_T>
  inline auto get_from_all_element(const ALL_ELE_T& all_ele) const {
    auto& my_ele = gs::get_from_tuple<tag_id>(all_ele);
    return get_view(my_ele);
  }
};

template <int tag_id, typename index_ele_tuple_t>
class GeneralEdgeSetPropGetter {
 public:
  using adj_iterator_t = typename std::tuple_element_t<2, index_ele_tuple_t>;
  using prop_element_t = typename adj_iterator_t::edge_property_t;
  GeneralEdgeSetPropGetter() {}

  inline auto get_view(const index_ele_tuple_t& ind_ele) const {
    return std::get<0>(std::get<2>(ind_ele).properties());
  }

  inline auto get_view()
      const {  // const std::tuple<size_t, int32_t, VID_T>& ind_ele
    return std::get<0>(std::get<2>(ind_ele_).properties());
  }

  template <typename ALL_ELE_T>
  inline auto get_from_all_element(const ALL_ELE_T& all_ele) const {
    auto& my_ele = gs::get_from_tuple<tag_id>(all_ele);
    return get_view(my_ele);
  }

  template <typename ALL_IND_ELE_T>
  inline auto get_from_all_index_element(const ALL_IND_ELE_T& all_ele) const {
    return get_view(gs::get_from_tuple<tag_id>(all_ele));
  }

  template <typename ALL_IND_ELE_T>
  inline void set_ind_ele(const ALL_IND_ELE_T& ind_ele) {
    ind_ele_ = gs::get_from_tuple<tag_id>(ind_ele);
  }

 private:
  index_ele_tuple_t ind_ele_;
};

template <int tag_id, typename PROP_GETTER_T, typename IND_ELE_T>
class TwoLabelVertexSetImplPropGetter {
 public:
  using prop_element_t = typename PROP_GETTER_T::value_type;
  TwoLabelVertexSetImplPropGetter(std::array<PROP_GETTER_T, 2>&& getters)
      : getters_(std::move(getters)) {}

  inline auto get_view(const IND_ELE_T& ind_ele) const {
    return getters_[std::get<1>(ind_ele)].get_view(std::get<2>(ind_ele));
  }

  inline auto get_view(const std::pair<int32_t, vid_t>& ind_ele) const {
    return getters_[ind_ele.first].get_view(ind_ele.second);
  }

  inline auto get_view()
      const {  // const std::tuple<size_t, int32_t, VID_T>& ind_ele
    return getters_[std::get<1>(ind_ele_)].get_view(std::get<2>(ind_ele_));
  }

  template <typename ALL_INDEX_ELE_T>
  inline auto get_from_all_element(const ALL_INDEX_ELE_T& all_ele) const {
    auto& my_ele = gs::get_from_tuple<tag_id>(all_ele);
    return get_view(my_ele);
  }

  template <typename ALL_IND_ELE_T>
  inline auto get_from_all_index_element(const ALL_IND_ELE_T& all_ele) const {
    return get_view(gs::get_from_tuple<tag_id>(all_ele));
  }

  template <typename ELE_T>
  inline auto get_from_element(const ELE_T& ele) const {
    return getters_[std::get<0>(ele)].get_view(std::get<1>(ele));
  }

  template <typename ALL_IND_ELE_T>
  inline void set_ind_ele(const ALL_IND_ELE_T& ind_ele) {
    ind_ele_ = gs::get_from_tuple<tag_id>(ind_ele);
  }

 private:
  IND_ELE_T ind_ele_;
  std::array<PROP_GETTER_T, 2> getters_;
};

template <int tag_id, typename PROP_GETTER_T, typename IND_ELE_T>
class RowVertexSetPropGetter {
 public:
  using prop_element_t = typename PROP_GETTER_T::value_type;
  RowVertexSetPropGetter(PROP_GETTER_T&& getter) : getter_(std::move(getter)) {}

  template <typename VID_T>
  inline auto get_view(const std::tuple<size_t, VID_T>& ind_ele) const {
    return getter_.get_view(std::get<1>(ind_ele));
  }

  template <typename VID_T>
  inline auto get_view(const VID_T& vid) const {
    return getter_.get_view(vid);
  }

  inline auto get_view(const GlobalId& global_id) const {
    return getter_.get_view(global_id.vid());
  }

  inline auto get_view() const {
    return getter_.get_view(std::get<1>(ind_ele_));
  }

  template <typename ALL_ELE_T>
  inline auto get_from_all_element(const ALL_ELE_T& all_ele) const {
    auto& my_ele = gs::get_from_tuple<tag_id>(all_ele);
    return get_view(my_ele);
  }

  template <typename ALL_IND_ELE_T>
  inline auto get_from_all_index_element(const ALL_IND_ELE_T& ind_ele) const {
    return get_view(gs::get_from_tuple<tag_id>(ind_ele));
  }

  // get from ele
  template <typename ELE_T>
  inline auto get_from_element(const ELE_T& ele) const {
    return getter_.get_view(ele);
  }

  template <typename ALL_IND_ELE_T>
  inline void set_ind_ele(const ALL_IND_ELE_T& ind_ele) {
    ind_ele_ = gs::get_from_tuple<tag_id>(ind_ele);
  }

 private:
  IND_ELE_T ind_ele_;
  PROP_GETTER_T getter_;
};

template <int tag_id, typename PROP_GETTER_T, typename IND_ELE_T>
class KeyedRowVertexSetPropGetter {
 public:
  using prop_element_t = typename PROP_GETTER_T::value_type;
  KeyedRowVertexSetPropGetter(PROP_GETTER_T&& getter)
      : getter_(std::move(getter)) {}

  template <typename VID_T>
  inline auto get_view(const std::tuple<size_t, VID_T>& ind_ele) const {
    return getter_.get_view(std::get<1>(ind_ele));
  }

  inline auto get_view(const GlobalId& global_id) const {
    return getter_.get_view(global_id.vid());
  }

  inline auto get_view() const {
    return getter_.get_view(std::get<1>(ind_ele_));
  }

  template <typename ALL_ELE_T>
  inline auto get_from_all_element(const ALL_ELE_T& all_ele) const {
    auto& my_ele = gs::get_from_tuple<tag_id>(all_ele);
    return get_view(my_ele);
  }

  template <typename ALL_IND_ELE_T>
  inline auto get_from_all_index_element(const ALL_IND_ELE_T& all_ele) const {
    return get_view(gs::get_from_tuple<tag_id>(all_ele));
  }

  template <typename ALL_IND_ELE_T>
  inline void set_ind_ele(const ALL_IND_ELE_T& ind_ele) {
    ind_ele_ = gs::get_from_tuple<tag_id>(ind_ele);
  }

 private:
  IND_ELE_T ind_ele_;
  PROP_GETTER_T getter_;
};

template <int tag_id, typename IND_ELE_T>
class DistGetter {
 public:
  using prop_element_t = Dist;
  DistGetter(std::vector<Dist>&& dist) : dist_(std::move(dist)) {}

  template <typename VID_T>
  inline auto get_view(const std::tuple<size_t, VID_T>& ind_ele) const {
    return dist_[std::get<0>(ind_ele)];
  }

  inline auto get_view() const { return dist_[std::get<0>(ind_ele_)]; }

  template <typename ALL_IND_ELE_T>
  inline void set_ind_ele(const ALL_IND_ELE_T& ind_ele) {
    ind_ele_ = gs::get_from_tuple<tag_id>(ind_ele);
  }

 private:
  std::vector<Dist> dist_;
  IND_ELE_T ind_ele_;
};

///////////////////////Creating property getter/////////////////////////

template <int tag_id, size_t Is = 0, typename LabelT, typename VID_T,
          typename... T>
static auto get_dist_prop_getter(
    const RowVertexSetImpl<LabelT, VID_T, T...>& set,
    const std::array<std::string, sizeof...(T)>& prop_names) {
  if (prop_names[Is] == "dist" || prop_names[Is] == "Dist") {
    std::vector<Dist> dists;
    auto& data_vec = set.GetDataVec();
    dists.reserve(set.Size());
    for (size_t i = 0; i < data_vec.size(); ++i) {
      dists.emplace_back(Dist(std::get<Is>(data_vec[i])));
    }
    return DistGetter<tag_id, typename RowVertexSetImpl<
                                  LabelT, VID_T, T...>::index_ele_tuple_t>(
        std::move(dists));
  }
  if constexpr (Is + 1 >= sizeof...(T)) {
    LOG(WARNING) << "Property dist not found, using default 0";
    std::vector<Dist> dists;
    auto set_size = set.Size();
    dists.reserve(set_size);
    for (size_t i = 0; i < set_size; ++i) {
      dists.emplace_back(0);
    }
    return DistGetter<tag_id, typename RowVertexSetImpl<
                                  LabelT, VID_T, T...>::index_ele_tuple_t>(
        std::move(dists));
  } else {
    return get_dist_prop_getter<tag_id, Is + 1>(set, prop_names);
  }
}

// getting dist prop for keyed row vertex set
template <int tag_id, size_t Is = 0, typename LabelT, typename KEY_T,
          typename VID_T, typename... T>
static auto get_dist_prop_getter(
    const KeyedRowVertexSetImpl<LabelT, KEY_T, VID_T, T...>& set,
    const std::array<std::string, sizeof...(T)>& prop_names) {
  if (prop_names[Is] == "dist" || prop_names[Is] == "Dist") {
    std::vector<Dist> dists;
    auto& data_vec = set.GetDataVec();
    dists.reserve(set.Size());
    for (size_t i = 0; i < data_vec.size(); ++i) {
      dists.emplace_back(Dist(std::get<Is>(data_vec[i])));
    }
    return DistGetter<tag_id,
                      typename KeyedRowVertexSetImpl<LabelT, KEY_T, VID_T,
                                                     T...>::index_ele_tuple_t>(
        std::move(dists));
  }
  if constexpr (Is + 1 >= sizeof...(T)) {
    LOG(WARNING) << "Property dist not found, using default 0";
    std::vector<Dist> dists;
    auto set_size = set.Size();
    dists.reserve(set_size);
    for (size_t i = 0; i < set_size; ++i) {
      dists.emplace_back(0);
    }
    return DistGetter<tag_id,
                      typename KeyedRowVertexSetImpl<LabelT, KEY_T, VID_T,
                                                     T...>::index_ele_tuple_t>(
        std::move(dists));
  } else {
    return get_dist_prop_getter<tag_id, Is + 1>(set, prop_names);
  }
}

///////////////////// Get LabelKey Property for VertexSet
template <int tag_id, typename prop_t, typename GRAPH_INTERFACE,
          typename NODE_T,
          typename std::enable_if<(std::is_same_v<prop_t, LabelKey> &&
                                   NODE_T::is_vertex_set)>::type* = nullptr>
static auto create_prop_getter_impl(const NODE_T& set,
                                    const GRAPH_INTERFACE& graph,
                                    const std::string& prop_name) {
  auto label_vec = set.GetLabelVec();
  return VertexLabelGetter<tag_id>(std::move(label_vec));
}

template <int tag_id, typename prop_t, typename GRAPH_INTERFACE,
          typename NODE_T,
          typename std::enable_if<(std::is_same_v<prop_t, LabelKey> &&
                                   NODE_T::is_edge_set)>::type* = nullptr>
static auto create_prop_getter_impl(const NODE_T& set,
                                    const GRAPH_INTERFACE& graph,
                                    const std::string& prop_name) {
  auto label_vec = set.GetLabelVec();
  return EdgeLabelGetter<tag_id>(std::move(label_vec));
}

// get for common properties for two_vertex_set
template <int tag_id, typename prop_t, typename GRAPH_INTERFACE,
          typename LabelT, typename VID_T, typename... T,
          typename std::enable_if<(!std::is_same_v<prop_t, Dist> &&
                                   !std::is_same_v<prop_t, LabelKey>)>::type* =
              nullptr>
static auto create_prop_getter_impl(
    const RowVertexSetImpl<LabelT, VID_T, T...>& set,
    const GRAPH_INTERFACE& graph, const std::string& prop_name) {
  using prop_getter_t =
      typename GRAPH_INTERFACE::template single_prop_getter_t<prop_t>;
  // const std::array<std::string, 2>& labels = set.GetLabels();

  auto label = set.GetLabel();
  VLOG(10) << "getting getter for " << prop_name << " for label "
           << gs::to_string(label);
  auto getter = graph.template GetSinglePropGetter<prop_t>(label, prop_name);
  return RowVertexSetPropGetter<
      tag_id, prop_getter_t,
      typename RowVertexSetImpl<LabelT, VID_T, T...>::index_ele_tuple_t>(
      std::move(getter));
}

// get for dist property for row_vertex_set
template <
    int tag_id, typename prop_t, typename GRAPH_INTERFACE, typename LabelT,
    typename VID_T, typename... T,
    typename std::enable_if<std::is_same_v<prop_t, Dist>>::type* = nullptr>
static auto create_prop_getter_impl(
    const RowVertexSetImpl<LabelT, VID_T, T...>& set,
    const GRAPH_INTERFACE& graph, const std::string& prop_name) {
  VLOG(10) << "Getting dist prop getter";
  CHECK(prop_name == "dist" || prop_name == "Dist");
  return get_dist_prop_getter<tag_id>(set, set.GetPropNames());
}

// get dist property for keyed vertex set
template <
    int tag_id, typename prop_t, typename GRAPH_INTERFACE, typename LabelT,
    typename KEY_T, typename VID_T, typename... T,
    typename std::enable_if<std::is_same_v<prop_t, Dist>>::type* = nullptr>
static auto create_prop_getter_impl(
    const KeyedRowVertexSetImpl<LabelT, KEY_T, VID_T, T...>& set,
    const GRAPH_INTERFACE& graph, const std::string& prop_name) {
  VLOG(10) << "Getting dist prop getter";
  CHECK(prop_name == "dist" || prop_name == "Dist");
  return get_dist_prop_getter<tag_id>(set, set.GetPropNames());
}

// get for common properties for two_label_vertex_set
template <int tag_id, typename prop_t, typename GRAPH_INTERFACE, typename VID_T,
          typename LabelT, typename... T,
          typename std::enable_if<!(std::is_same_v<prop_t, LabelKey>)>::type* =
              nullptr>
static auto create_prop_getter_impl(
    const TwoLabelVertexSetImpl<VID_T, LabelT, T...>& set,
    const GRAPH_INTERFACE& graph, const std::string& prop_name) {
  using prop_getter_t =
      typename GRAPH_INTERFACE::template single_prop_getter_t<prop_t>;
  auto& labels = set.GetLabels();
  std::array<std::string, 1> names{prop_name};
  VLOG(10) << "Getting prop labels for " << prop_name << " for labels "
           << std::to_string(labels[0]) << ", " << std::to_string(labels[1]);
  std::array<prop_getter_t, 2> prop_getter{
      graph.template GetSinglePropGetter<prop_t>(labels[0], prop_name),
      graph.template GetSinglePropGetter<prop_t>(labels[1], prop_name)};

  return TwoLabelVertexSetImplPropGetter<
      tag_id, prop_getter_t,
      typename TwoLabelVertexSetImpl<VID_T, LabelT, T...>::index_ele_tuple_t>(
      std::move(prop_getter));
}

// get for common properties for keyed_row_vertex_set
template <int tag_id, typename prop_t, typename GRAPH_INTERFACE,
          typename LabelT, typename KEY_T, typename VID_T, typename... T,
          typename std::enable_if<(!std::is_same_v<prop_t, Dist> &&
                                   !std::is_same_v<prop_t, LabelKey>)>::type* =
              nullptr>
static auto create_prop_getter_impl(
    const KeyedRowVertexSetImpl<LabelT, KEY_T, VID_T, T...>& set,
    const GRAPH_INTERFACE& graph, const std::string& prop_name) {
  using prop_getter_t =
      typename GRAPH_INTERFACE::template single_prop_getter_t<prop_t>;
  // const std::array<std::string, 2>& labels = set.GetLabels();
  auto label = set.GetLabel();

  auto getter = graph.template GetSinglePropGetter<prop_t>(label, prop_name);
  return KeyedRowVertexSetPropGetter<
      tag_id, prop_getter_t,
      typename KeyedRowVertexSetImpl<LabelT, KEY_T, VID_T,
                                     T...>::index_ele_tuple_t>(
      std::move(getter));
}

// get for common properties for keyed_row_vertex_set
template <int tag_id, typename prop_t, typename GRAPH_INTERFACE,
          typename LabelT, typename VID_T, typename... SET_T,
          typename std::enable_if<!(std::is_same_v<prop_t, LabelKey>)>::type* =
              nullptr>
static auto create_prop_getter_impl(
    const GeneralVertexSet<VID_T, LabelT, SET_T...>& set,
    const GRAPH_INTERFACE& graph, const std::string& prop_name) {
  using prop_getter_t =
      typename GRAPH_INTERFACE::template single_prop_getter_t<prop_t>;
  // const std::array<std::string, 2>& labels = set.GetLabels();
  auto labels = set.GetLabels();
  std::vector<prop_getter_t> prop_getters;
  for (size_t i = 0; i < labels.size(); ++i) {
    prop_getters.emplace_back(
        graph.template GetSinglePropGetter<prop_t>(labels[i], prop_name));
  }

  return GeneralVertexSetPropGetter<
      tag_id, prop_getter_t,
      typename GeneralVertexSet<VID_T, LabelT, SET_T...>::index_ele_tuple_t>(
      std::move(prop_getters), set.GetBitsets());
}

// get for common properties for FlatEdgeSet
template <int tag_id, typename prop_t, typename GRAPH_INTERFACE, typename VID_T,
          typename LabelT, typename EDATA_T,
          typename std::enable_if<!(std::is_same_v<prop_t, LabelKey>)>::type* =
              nullptr>
static auto create_prop_getter_impl(
    const FlatEdgeSet<VID_T, LabelT, EDATA_T>& set,
    const GRAPH_INTERFACE& graph, const std::string& prop_name) {
  return FlatEdgeSetPropGetter<
      tag_id,
      typename FlatEdgeSet<VID_T, LabelT, EDATA_T>::index_ele_tuple_t>();
}

// get for common properties for UntypedEdgeSet
template <int tag_id, typename prop_t, typename GRAPH_INTERFACE, typename VID_T,
          typename LabelT, typename SUB_GRAPH_T,
          typename std::enable_if<!(std::is_same_v<prop_t, LabelKey>)>::type* =
              nullptr>
static auto create_prop_getter_impl(
    const UnTypedEdgeSet<VID_T, LabelT, SUB_GRAPH_T>& set,
    const GRAPH_INTERFACE& graph, const std::string& prop_name) {
  return UnTypedEdgeSetPropGetter<tag_id, prop_t>();
}

// get for common properties for Single label edge set.
template <int tag_id, typename prop_t, typename GRAPH_INTERFACE, typename VID_T,
          typename LabelT, typename EDATA_T,
          typename std::enable_if<!(std::is_same_v<prop_t, LabelKey>)>::type* =
              nullptr>
static auto create_prop_getter_impl(
    const SingleLabelEdgeSet<VID_T, LabelT, EDATA_T>& set,
    const GRAPH_INTERFACE& graph, const std::string& prop_name) {
  // single label edge set is a special kind of flat edge set.
  return FlatEdgeSetPropGetter<
      tag_id,
      typename SingleLabelEdgeSet<VID_T, LabelT, EDATA_T>::index_ele_tuple_t>();
}

// get for common properties for GeneralEdgeSet
template <int tag_id, typename prop_t, size_t N, typename GI, typename VID_T,
          typename LabelT, typename... EDATA_T,
          typename std::enable_if<!(std::is_same_v<prop_t, LabelKey>)>::type* =
              nullptr>
static auto create_prop_getter_impl(
    const GeneralEdgeSet<N, GI, VID_T, LabelT, EDATA_T...>& set,
    const GI& graph, const std::string& prop_name) {
  return GeneralEdgeSetPropGetter<
      tag_id, typename GeneralEdgeSet<N, GI, VID_T, LabelT,
                                      EDATA_T...>::index_ele_tuple_t>();
}

// get for common properties for collection
template <int tag_id, typename prop_t, typename GI, typename T>
static auto create_prop_getter_impl(const Collection<T>& set, const GI& graph,
                                    const std::string& prop_name) {
  CHECK(prop_name == "None" || prop_name == "none" || prop_name == "");
  return CollectionPropGetter<tag_id, T>();
}

// create inner id getter for row vertex set with props
template <typename GRAPH_INTERFACE, typename LabelT, typename... SET_Ts,
          int tag_id>
static auto create_prop_getter_from_prop_desc(
    const GRAPH_INTERFACE& graph,
    const RowVertexSetImpl<LabelT, typename GRAPH_INTERFACE::vertex_id_t,
                           SET_Ts...>& set,
    const InnerIdProperty<tag_id>& inner_id_prop) {
  return InnerIdDataGetter<tag_id, typename GRAPH_INTERFACE::vertex_id_t,
                           SET_Ts...>(set.GetVertices(), set.GetDataVec());
}
// create inner id getter for keyed row vertex set without props
template <typename GRAPH_INTERFACE, typename LabelT, int tag_id>
static auto create_prop_getter_from_prop_desc(
    const GRAPH_INTERFACE& graph,
    const RowVertexSetImpl<LabelT, typename GRAPH_INTERFACE::vertex_id_t,
                           grape::EmptyType>& set,
    const InnerIdProperty<tag_id>& inner_id_prop) {
  return VertexInnerIdGetter<tag_id, GlobalId>();
}
// create inner_id getter for two label vertex set
template <typename GRAPH_INTERFACE, typename LabelT, typename... SET_Ts,
          int tag_id>
static auto create_prop_getter_from_prop_desc(
    const GRAPH_INTERFACE& graph,
    const TwoLabelVertexSetImpl<typename GRAPH_INTERFACE::vertex_id_t, LabelT,
                                SET_Ts...>& set,
    const InnerIdProperty<tag_id>& inner_id_prop) {
  return VertexInnerIdGetter<tag_id, GlobalId>(set.GetVertices());
}

// create inner_id getter for two label vertex set
template <typename GRAPH_INTERFACE, typename LabelT, typename... SET_Ts,
          int tag_id>
static auto create_prop_getter_from_prop_desc(
    const GRAPH_INTERFACE& graph,
    const GeneralVertexSet<typename GRAPH_INTERFACE::vertex_id_t, LabelT,
                           SET_Ts...>& set,
    const InnerIdProperty<tag_id>& inner_id_prop) {
  return VertexInnerIdGetter<tag_id, GlobalId>(set.GetVertices());
}

// create inner id getter for collection.
template <typename GRAPH_INTERFACE, typename COL_T, int tag_id>
static auto create_prop_getter_from_prop_desc(
    const GRAPH_INTERFACE& graph, const Collection<COL_T>& set,
    const InnerIdProperty<tag_id>& inner_id_prop) {
  return CollectionInnerIdGetter<tag_id, COL_T>();
}

// create innerId getter for flat edge set.
template <typename GRAPH_INTERFACE, typename VID_T, typename LabelT,
          typename EDATA_T, int tag_id>
static auto create_prop_getter_from_prop_desc(
    const GRAPH_INTERFACE& graph,
    const FlatEdgeSet<VID_T, LabelT, EDATA_T>& set,
    const InnerIdProperty<tag_id>& inner_id_prop) {
  return EdgeSetInnerIdGetter<tag_id, VID_T, EDATA_T>();
}

// create inner id getter for single_label_edge_set.
template <typename GRAPH_INTERFACE, typename VID_T, typename LabelT,
          typename EDATA_T, int tag_id>
static auto create_prop_getter_from_prop_desc(
    const GRAPH_INTERFACE& graph,
    const SingleLabelEdgeSet<VID_T, LabelT, EDATA_T>& set,
    const InnerIdProperty<tag_id>& inner_id_prop) {
  return EdgeSetInnerIdGetter<tag_id, VID_T, EDATA_T>();
}

// create GlobalId getter for vertex_set
template <typename GRAPH_INTERFACE, typename VERTEX_SET_T, int tag_id,
          typename std::enable_if<VERTEX_SET_T::is_vertex_set>::type* = nullptr>
static auto create_global_id_prop_getter_from_prop_desc(
    const GRAPH_INTERFACE& graph, const VERTEX_SET_T& set,
    const GlobalIdProperty<tag_id>& global_id_prop) {
  return VertexGlobalIdGetter<tag_id, typename GRAPH_INTERFACE::vertex_id_t>(
      set.GetVertices(), set.GetLabelVec());
}

// create global id getter for collection
template <typename GRAPH_INTERFACE, typename T, int tag_id>
static auto create_global_id_prop_getter_from_prop_desc(
    const GRAPH_INTERFACE& graph, const Collection<T>& set,
    const GlobalIdProperty<tag_id>& global_id_prop) {
  return CollectionInnerIdGetter<tag_id, T>();
}

// create global id getter for EdgeSet
template <typename GRAPH_INTERFACE, typename EDGE_SET_T, int tag_id,
          typename std::enable_if<EDGE_SET_T::is_edge_set>::type* = nullptr>
static auto create_global_id_prop_getter_from_prop_desc(
    const GRAPH_INTERFACE& graph, const EDGE_SET_T& set,
    const GlobalIdProperty<tag_id>& global_id_prop) {
  return EdgeSetInnerIdGetter<tag_id, typename GRAPH_INTERFACE::vertex_id_t,
                              typename EDGE_SET_T::edata_t>();
}

// get prop for inner id for idKey
template <typename GRAPH_INTERFACE, typename CTX_HEAD_T, int cur_alias,
          int base_tag, typename... CTX_PREV, int tag_id>
static auto create_prop_getter_from_prop_desc(
    const GRAPH_INTERFACE& graph,
    Context<CTX_HEAD_T, cur_alias, base_tag, CTX_PREV...>& ctx,
    const InnerIdProperty<tag_id>& inner_id_prop) {
  auto& set = ctx.template GetNode<tag_id>();
  return create_prop_getter_from_prop_desc(graph, set, inner_id_prop);
}

// get prop for global id for idKey
template <typename GRAPH_INTERFACE, typename CTX_HEAD_T, int cur_alias,
          int base_tag, typename... CTX_PREV, int tag_id>
static auto create_prop_getter_from_prop_desc(
    const GRAPH_INTERFACE& graph,
    Context<CTX_HEAD_T, cur_alias, base_tag, CTX_PREV...>& ctx,
    const GlobalIdProperty<tag_id>& global_id_property) {
  auto& set = ctx.template GetNode<tag_id>();
  return create_global_id_prop_getter_from_prop_desc(graph, set,
                                                     global_id_property);
}

// get prop for common property.
// return a single prop getter
template <typename GRAPH_INTERFACE, typename CTX_T, typename T, int tag_id>
static auto create_prop_getter_from_prop_desc(
    const GRAPH_INTERFACE& graph, CTX_T& ctx,
    const NamedProperty<T, tag_id>& named_property) {
  auto& set = ctx.template GetNode<tag_id>();
  return create_prop_getter_impl<tag_id, T>(set, graph, named_property.name);
}

template <typename GI, typename CTX_T, typename... PROP_DESC, size_t... Is>
static auto create_prop_getters_from_prop_desc(
    const GI& graph, CTX_T& ctx, const std::tuple<PROP_DESC...>& prop_desc,
    std::index_sequence<Is...>) {
  return std::make_tuple(create_prop_getter_from_prop_desc(
      graph, ctx, std::get<Is>(prop_desc))...);
}

template <typename GI, typename CTX_T, typename... PROP_DESC>
static auto create_prop_getters_from_prop_desc(
    const GI& graph, CTX_T& ctx, const std::tuple<PROP_DESC...>& prop_desc) {
  return create_prop_getters_from_prop_desc(
      graph, ctx, prop_desc, std::make_index_sequence<sizeof...(PROP_DESC)>());
}

template <int col_id>
auto create_prop_desc_from_selector(
    const PropertySelector<grape::EmptyType>& selector) {
  return InnerIdProperty<col_id>();
}

// For getting edge_set's edge
template <int col_id, typename VID_T>
auto create_prop_desc_from_selector(
    const PropertySelector<const DefaultEdge<VID_T>&>& selector) {
  return InnerIdProperty<col_id>();
}

template <int col_id>
auto create_prop_desc_from_selector(
    const PropertySelector<GlobalId>& selector) {
  return GlobalIdProperty<col_id>();
}

template <int col_id, typename T>
auto create_prop_desc_from_selector(const PropertySelector<T>& selector) {
  return NamedProperty<T, col_id>(selector.prop_name_);
}

template <int... in_col_id, typename... SELECTOR, size_t... Ind>
auto create_prop_descs_from_selectors(std::integer_sequence<int, in_col_id...>,
                                      const std::tuple<SELECTOR...>& selectors,
                                      std::index_sequence<Ind...>) {
  return std::make_tuple(
      create_prop_desc_from_selector<in_col_id>(std::get<Ind>(selectors))...);
}

template <AggFunc agg_func, typename PropT, int tag_id>
auto create_prop_desc_from_aggregate_prop(
    const AggregateProp<agg_func, std::tuple<PropT>,
                        std::integer_sequence<int32_t, tag_id>>& agg_prop) {
  return create_prop_desc_from_selector<tag_id>(
      std::get<0>(agg_prop.selectors_));
}

template <int... in_col_id, typename... SELECTOR>
auto create_prop_descs_from_selectors(
    const std::tuple<SELECTOR...>& selectors) {
  return create_prop_descs_from_selectors(
      std::integer_sequence<int, in_col_id...>(), selectors,
      std::make_index_sequence<sizeof...(SELECTOR)>());
}

template <typename... GROUP_KEY, size_t... Is>
static auto create_prop_descs_from_group_keys_impl(
    const std::tuple<GROUP_KEY...>& group_keys, std::index_sequence<Is...>) {
  auto tuple = std::make_tuple(std::get<Is>(group_keys).selector_...);
  return create_prop_descs_from_selectors<GROUP_KEY::col_id...>(tuple);
}

template <typename... GROUP_KEY>
static auto create_prop_descs_from_group_keys(
    const std::tuple<GROUP_KEY...>& group_keys) {
  return create_prop_descs_from_group_keys_impl(
      group_keys, std::make_index_sequence<sizeof...(GROUP_KEY)>());
}

template <typename... PropTGetter, typename... IndexEle, size_t... Is>
auto get_prop_from_index_ele_with_prop_getter(
    const std::tuple<PropTGetter...>& prop_getters,
    const std::tuple<IndexEle...>& index_ele, std::index_sequence<Is...>) {
  return std::make_tuple(
      std::get<Is>(prop_getters).get_from_all_index_element(index_ele)...);
}

}  // namespace gs

#endif  // ENGINES_HQPS_ENGINE_OPERATOR_PROP_UTILS_H_