Raw File
itertools.h
/*******************************************************************************
 *
 * TRIQS: a Toolbox for Research in Interacting Quantum Systems
 *
 * Copyright (C) 2018, N. Wentzell, O. Parcollet
 * Copyright (C) 2019, The Simons Foundation
 *   author : N. Wentzell
 *
 * TRIQS 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.
 *
 * TRIQS 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
 * TRIQS. If not, see <http://www.gnu.org/licenses/>.
 *
 ******************************************************************************/
#pragma once
#include <tuple>
#include <vector>
#include <iterator>
#include <exception>

namespace triqs::utility {

  template <class Iter, class Value, class Tag = std::forward_iterator_tag, class Reference = Value &, class Difference = std::ptrdiff_t>
  struct iterator_facade;

  /**
   * A helper for the implementation of forward iterators using CRTP
   *
   * @tparam Iter
   * The Iterator Class to be implemented
   * `Iter` is required to have the following member functions
   * - bool equal(Iter other)
   * - value_type [const] [&] dereference()
   * - void increment()
   */
  template <typename Iter, typename Value, typename Reference, typename Difference>
  struct iterator_facade<Iter, Value, std::forward_iterator_tag, Reference, Difference> {

    private:
    Iter &self() { return static_cast<Iter &>(*this); }
    Iter const &self() const { return static_cast<const Iter &>(*this); }

    public:
    using value_type        = Value;
    using reference         = Reference;
    using pointer           = Value *;
    using difference_type   = Difference;
    using iterator_category = std::forward_iterator_tag;

    Iter &operator++() {
      self().increment();
      return self();
    }

    Iter operator++(int) {
      Iter c = self();
      self().increment();
      return c;
    }

    template <typename U> bool operator==(U const &other) const { return self().equal(other); }
    template <typename U> bool operator!=(U const &other) const { return (!operator==(other)); }

    decltype(auto) operator*() const { return self().dereference(); }
    decltype(auto) operator-> () const { return operator*(); }
  };

  /********************* Enumerate Iterator ********************/

  template <typename Iter> struct enum_iter : iterator_facade<enum_iter<Iter>, std::pair<long, typename std::iterator_traits<Iter>::value_type>> {

    Iter it;
    long i = 0;

    enum_iter(Iter it) : it(std::move(it)) {}

    void increment() {
      ++it;
      ++i;
    }

    bool equal(enum_iter const &other) const { return (it == other.it); }

    decltype(auto) dereference() const { return std::tuple<long, decltype(*it)>{i, *it}; }
  };

  /********************* Transform Iterator ********************/

  template <typename Iter, typename L, typename Value = std::result_of<L(typename std::iterator_traits<Iter>::value_type)>>
  struct transform_iter : iterator_facade<transform_iter<Iter, L>, Value> {

    Iter it;
    mutable L lambda;

    transform_iter(Iter it, L lambda) : it(std::move(it)), lambda(std::move(lambda)) {}

    void increment() { ++it; }

    bool equal(transform_iter const &other) const { return (it == other.it); }

    decltype(auto) dereference() const { return lambda(*it); }
  };

  /********************* Zip Iterator ********************/

  template <typename... It> struct zip_iter : iterator_facade<zip_iter<It...>, std::tuple<typename std::iterator_traits<It>::value_type...>> {

    std::tuple<It...> its;
    zip_iter(std::tuple<It...> its) : its(std::move(its)) {}

    template <typename... U> void nop(U &&... u) {} // do nothing...
    template <size_t... Is> void increment_all(std::index_sequence<Is...>) { nop(++std::get<Is>(its)...); }
    void increment() { increment_all(std::index_sequence_for<It...>{}); }

    bool equal(zip_iter const &other) const { return (its == other.its); }

    template <size_t... Is> auto tuple_map_impl(std::index_sequence<Is...>) const {
      return std::tuple<decltype(*std::get<Is>(its))...>(*std::get<Is>(its)...);
    }

    decltype(auto) dereference() const { return tuple_map_impl(std::index_sequence_for<It...>{}); }
  };

  /********************* Product Iterator ********************/

  namespace details {
    // Sentinel_t, used to denote the end of a product range
    template <typename It> struct sentinel_t { It it; };
    template <typename It> sentinel_t<It> make_sentinel(It &&it) { return {it}; }
  } // namespace details

  template <typename... It> struct prod_iter : iterator_facade<prod_iter<It...>, std::tuple<typename std::iterator_traits<It>::value_type...>> {

    std::tuple<It...> its_begin, its_end;
    std::tuple<It...> its = its_begin;

    prod_iter(std::tuple<It...> its_begin, std::tuple<It...> its_end)
       : its_begin(std::move(its_begin)), its_end(std::move(its_end)){}

    template <int N> void _increment() {
      ++std::get<N>(its);
      if constexpr (N < sizeof...(It) - 1) {
        if (std::get<N>(its) == std::get<N>(its_end)) {
          std::get<N>(its) = std::get<N>(its_begin);
          _increment<N + 1>();
        }
      }
    }
    void increment() { _increment<0>(); }

    bool equal(prod_iter const &other) const { return (its == other.its); }
    template <typename U> bool equal(details::sentinel_t<U> const &s) const { return (s.it == std::get<sizeof...(It) - 1>(its)); }

    template <size_t... Is> auto tuple_map_impl(std::index_sequence<Is...>) const {
      return std::tuple<decltype(*std::get<Is>(its))...>(*std::get<Is>(its)...);
    }
    decltype(auto) dereference() const { return tuple_map_impl(std::index_sequence_for<It...>{}); }
  };

  /********************* The Wrapper Classes representing the adapted ranges ********************/

  namespace details {

    template <typename T, typename L> struct transformed {
      T x;
      L lambda;

      using const_iterator = transform_iter<std::decay_t<decltype(std::cbegin(x))>, std::decay_t<L>>;
      using iterator       = const_iterator;

      const_iterator cbegin() const { return {std::cbegin(x), lambda}; }
      const_iterator begin() const { return cbegin(); }

      const_iterator cend() const { return {std::cend(x), lambda}; }
      const_iterator end() const { return cend(); }
    };

    // ---------------------------------------------

    template <typename T> struct enumerated {
      T x;

      using iterator       = enum_iter<std::decay_t<decltype(std::begin(x))>>;
      using const_iterator = enum_iter<std::decay_t<decltype(std::cbegin(x))>>;

      iterator begin() { return std::begin(x); }
      const_iterator cbegin() const { return std::cbegin(x); }
      const_iterator begin() const { return cbegin(); }

      iterator end() { return std::end(x); }
      const_iterator cend() const { return std::cend(x); }
      const_iterator end() const { return cend(); }
    };

    // ---------------------------------------------

    template <typename... T> struct zipped {
      std::tuple<T...> tu; // T can be a ref.

      using seq_t          = std::index_sequence_for<T...>;
      using iterator       = zip_iter<std::decay_t<decltype(std::begin(std::declval<T &>()))>...>;
      using const_iterator = zip_iter<std::decay_t<decltype(std::cbegin(std::declval<T &>()))>...>;

      template <typename... U> zipped(U &&... ranges) : tu{std::forward<U>(ranges)...} {}

      // Apply function to tuple
      template <typename F, size_t... Is> auto tuple_map(F &&f, std::index_sequence<Is...>) {
        return iterator{std::make_tuple(f(std::get<Is>(tu))...)};
      }
      template <typename F, size_t... Is> auto tuple_map(F &&f, std::index_sequence<Is...>) const {
        return const_iterator{std::make_tuple(f(std::get<Is>(tu))...)};
      }

      iterator begin() {
        return tuple_map([](auto &&x) { return std::begin(x); }, seq_t{});
      }
      const_iterator cbegin() const {
        return tuple_map([](auto &&x) { return std::cbegin(x); }, seq_t{});
      }
      const_iterator begin() const { return cbegin(); }

      iterator end() {
        return tuple_map([](auto &&x) { return std::end(x); }, seq_t{});
      }
      const_iterator cend() const {
        return tuple_map([](auto &&x) { return std::cend(x); }, seq_t{});
      }
      const_iterator end() const { return cend(); }
    };

    // ---------------------------------------------

    template <typename... T> struct multiplied {
      std::tuple<T...> tu; // T can be a ref.

      using iterator       = prod_iter<std::decay_t<decltype(std::begin(std::declval<T &>()))>...>;
      using const_iterator = prod_iter<std::decay_t<decltype(std::cbegin(std::declval<T &>()))>...>;

      template <typename... U> multiplied(U &&... ranges) : tu{std::forward<U>(ranges)...} {}

      template <size_t... Is> auto _begin(std::index_sequence<Is...>) {
        return iterator{std::make_tuple(std::begin(std::get<Is>(tu))...), std::make_tuple(std::end(std::get<Is>(tu))...)};
      }

      template <size_t... Is> auto _cbegin(std::index_sequence<Is...>) const {
        return const_iterator{std::make_tuple(std::cbegin(std::get<Is>(tu))...), std::make_tuple(std::cend(std::get<Is>(tu))...)};
      }

      iterator begin() { return _begin(std::index_sequence_for<T...>{}); }
      const_iterator cbegin() const { return _cbegin(std::index_sequence_for<T...>{}); }
      const_iterator begin() const { return cbegin(); }

      auto end() { return make_sentinel(std::end(std::get<sizeof...(T) - 1>(tu))); }
      auto cend() const { return make_sentinel(std::cend(std::get<sizeof...(T) - 1>(tu))); }
      auto end() const { return cend(); }
    };

    template <typename... T> multiplied(T &&...)->multiplied<std::decay_t<T>...>;

  } // namespace details

  /********************* The range adapting functions ********************/

  /**
   * Transform (lazy)applies a unary lambda function to every
   * element of a range. It returns itself a range.
   *
   * @param range The range that the lambda is applied to
   * @param range The lambda to apply to the range
   */
  template <typename T, typename L> auto transform(T &&range, L &&lambda) {
    return details::transformed<T, L>{std::forward<T>(range), std::forward<L>(lambda)};
  }

  /**
   * Lazy-enumerate a range.
   * This function returns itself a range of tuple<int, T>
   *
   * @param range The range to enumerate
   */
  template <typename T> auto enumerate(T &&range) { return details::enumerated<T>{std::forward<T>(range)}; }

  /**
   * Lazy-zip a range.
   * This function returns itself a range of tuple<T...>
   *
   * @param ranges The ranges to zip. Note: They have to be of equal length!
   */
  template <typename... T> auto zip(T &&... ranges) { return details::zipped<T...>{std::forward<T>(ranges)...}; }
  template <typename... T, typename L> auto zip_with(T &&... ranges, L &&lambda) {
    return transform(zip(std::forward<T>(ranges)...), [lambda](std::tuple<T...> t) { return std::apply(lambda, t); });
  }

  /**
   * Lazy-product of multiple ranges. This function returns itself a range of tuple<T...>.
   * Iterating over it will yield all combinations of the different range values.
   * Note: The ranges are incremented beginning with the leftmost range.
   *
   * @tparam T The types of the different ranges
   * @param ranges The ranges to zip. Note: They have to be of equal length!
   */
  template <typename... T> auto product(T &&... ranges) { return details::multiplied<T...>{std::forward<T>(ranges)...}; }


  /********************* Some factory functions ********************/

  namespace details {
    template <typename T, size_t N, size_t... Is> auto make_product_impl(std::array<T, N> &arr, std::index_sequence<Is...>) {
      static_assert(N == sizeof...(Is));
      return product(arr[Is]...);
    }
  } // namespace details

  template <typename T, size_t N> auto make_product(std::array<T, N> &arr) { return details::make_product_impl(arr, std::make_index_sequence<N>{}); }

  /**
   * Create a std::vector from a range
   */
  template <typename R> auto make_vector_from_range(R &&r) {
    std::vector<std::decay_t<decltype(*(std::begin(r)))>> vec;
    for (auto const &x : r) vec.push_back(x);
    return vec;
  }

} // namespace triqs::utility
back to top