--- /dev/null
+/*
+ * Copyright 2013 Facebook, Inc.
+ *
+ * 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 FOLLY_COMBINEGEN_H_
+#error This file may only be included from folly/experimental/CombineGen.h
+#endif
+
+#include <iterator>
+#include <system_error>
+#include <tuple>
+#include <type_traits>
+
+namespace folly {
+namespace gen {
+namespace detail {
+
+/**
+ * Interleave
+ *
+ * Alternate values from a sequence with values from a sequence container.
+ * Stops once we run out of values from either source.
+ */
+template<class Container>
+class Interleave : public Operator<Interleave<Container>> {
+ // see comment about copies in CopiedSource
+ const std::shared_ptr<const Container> container_;
+ public:
+ explicit Interleave(Container container)
+ : container_(new Container(std::move(container))) {}
+
+ template<class Value,
+ class Source>
+ class Generator : public GenImpl<Value, Generator<Value, Source>> {
+ Source source_;
+ const std::shared_ptr<const Container> container_;
+ typedef const typename Container::value_type& ConstRefType;
+
+ static_assert(std::is_same<const Value&, ConstRefType>::value,
+ "Only matching types may be interleaved");
+ public:
+ explicit Generator(Source source,
+ const std::shared_ptr<const Container> container)
+ : source_(std::move(source)),
+ container_(container) { }
+
+ template<class Handler>
+ bool apply(Handler&& handler) const {
+ auto iter = container_->begin();
+ return source_.apply([&](const Value& value) -> bool {
+ if (iter == container_->end()) {
+ return false;
+ }
+ if (!handler(value)) {
+ return false;
+ }
+ if (!handler(*iter)) {
+ return false;
+ }
+ iter++;
+ return true;
+ });
+ }
+ };
+
+ template<class Value2,
+ class Source,
+ class Gen = Generator<Value2,Source>>
+ Gen compose(GenImpl<Value2, Source>&& source) const {
+ return Gen(std::move(source.self()), container_);
+ }
+
+ template<class Value2,
+ class Source,
+ class Gen = Generator<Value2,Source>>
+ Gen compose(const GenImpl<Value2, Source>& source) const {
+ return Gen(source.self(), container_);
+ }
+};
+
+/**
+ * Zip
+ *
+ * Combine inputs from Source with values from a sequence container by merging
+ * them into a tuple.
+ *
+ */
+template<class Container>
+class Zip : public Operator<Zip<Container>> {
+ // see comment about copies in CopiedSource
+ const std::shared_ptr<const Container> container_;
+ public:
+ explicit Zip(Container container)
+ : container_(new Container(std::move(container))) {}
+
+ template<class Value1,
+ class Source,
+ class Value2 = decltype(*std::begin(*container_)),
+ class Result = std::tuple<typename std::decay<Value1>::type,
+ typename std::decay<Value2>::type>>
+ class Generator : public GenImpl<Result,
+ Generator<Value1,Source,Value2,Result>> {
+ Source source_;
+ const std::shared_ptr<const Container> container_;
+ public:
+ explicit Generator(Source source,
+ const std::shared_ptr<const Container> container)
+ : source_(std::move(source)),
+ container_(container) { }
+
+ template<class Handler>
+ bool apply(Handler&& handler) const {
+ auto iter = container_->begin();
+ return (source_.apply([&](Value1 value) -> bool {
+ if (iter == container_->end()) {
+ return false;
+ }
+ if (!handler(std::make_tuple(std::forward<Value1>(value), *iter))) {
+ return false;
+ }
+ ++iter;
+ return true;
+ }));
+ }
+ };
+
+ template<class Source,
+ class Value,
+ class Gen = Generator<Value, Source>>
+ Gen compose(GenImpl<Value, Source>&& source) const {
+ return Gen(std::move(source.self()), container_);
+ }
+
+ template<class Source,
+ class Value,
+ class Gen = Generator<Value, Source>>
+ Gen compose(const GenImpl<Value, Source>& source) const {
+ return Gen(source.self(), container_);
+ }
+};
+
+template<class... Types1,
+ class... Types2>
+auto add_to_tuple(std::tuple<Types1...> t1, std::tuple<Types2...> t2) ->
+std::tuple<Types1..., Types2...> {
+ return std::tuple_cat(std::move(t1), std::move(t2));
+}
+
+template<class... Types1,
+ class Type2>
+auto add_to_tuple(std::tuple<Types1...> t1, Type2&& t2) ->
+decltype(std::tuple_cat(std::move(t1),
+ std::make_tuple(std::forward<Type2>(t2)))) {
+ return std::tuple_cat(std::move(t1),
+ std::make_tuple(std::forward<Type2>(t2)));
+}
+
+template<class Type1,
+ class... Types2>
+auto add_to_tuple(Type1&& t1, std::tuple<Types2...> t2) ->
+decltype(std::tuple_cat(std::make_tuple(std::forward<Type1>(t1)),
+ std::move(t2))) {
+ return std::tuple_cat(std::make_tuple(std::forward<Type1>(t1)),
+ std::move(t2));
+}
+
+template<class Type1,
+ class Type2>
+auto add_to_tuple(Type1&& t1, Type2&& t2) ->
+decltype(std::make_tuple(std::forward<Type1>(t1),
+ std::forward<Type2>(t2))) {
+ return std::make_tuple(std::forward<Type1>(t1),
+ std::forward<Type2>(t2));
+}
+
+// Merges a 2-tuple into a single tuple (get<0> could already be a tuple)
+class MergeTuples {
+ public:
+ template<class Tuple>
+ auto operator()(Tuple&& value) const ->
+ decltype(add_to_tuple(std::get<0>(std::forward<Tuple>(value)),
+ std::get<1>(std::forward<Tuple>(value)))) {
+ static_assert(std::tuple_size<
+ typename std::remove_reference<Tuple>::type
+ >::value == 2,
+ "Can only merge tuples of size 2");
+ return add_to_tuple(std::get<0>(std::forward<Tuple>(value)),
+ std::get<1>(std::forward<Tuple>(value)));
+ }
+};
+
+} // namespace detail
+
+static const detail::Map<detail::MergeTuples> tuple_flatten;
+
+// TODO(mcurtiss): support zip() for N>1 operands. Because of variadic problems,
+// this might not be easily possible until gcc4.8 is available.
+template<class Source,
+ class Zip = detail::Zip<typename std::decay<Source>::type>>
+Zip zip(Source&& source) {
+ return Zip(std::forward<Source>(source));
+}
+
+} // namespace gen
+} // namespace folly