//===----------------------------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

// UNSUPPORTED: c++03, c++11, c++14, c++17

// template<input_range V, forward_range Pattern>
//   requires view<V> && view<Pattern> &&
//            indirectly_comparable<iterator_t<V>, iterator_t<Pattern>, ranges::equal_to> &&
//            (forward_range<V> || tiny-range<Pattern>)
// class lazy_split_view;

#include <functional>
#include <ranges>

#include "test_iterators.h"
#include "types.h"

struct ForwardRange {
  forward_iterator<int*> begin() const;
  forward_iterator<int*> end() const;
};
static_assert( std::ranges::forward_range<ForwardRange>);

template <class View, class Pattern>
concept CanInstantiate = requires {
  typename std::ranges::lazy_split_view<View, Pattern>;
};

// All constraints satisfied (`View` and `Pattern` are forward views).
namespace test1 {

  using View = ForwardView;
  using Pattern = ForwardView;
  static_assert( std::ranges::forward_range<View>);
  static_assert( std::ranges::forward_range<Pattern>);
  static_assert( std::ranges::view<View>);
  static_assert( std::ranges::view<Pattern>);
  static_assert( std::indirectly_comparable<
      std::ranges::iterator_t<View>, std::ranges::iterator_t<Pattern>, std::ranges::equal_to>);
  static_assert( CanInstantiate<View, Pattern>);

} // namespace test1

// All constraints satisfied (`View` is an input view and `Pattern` is a tiny view).
namespace test2 {

  using View = InputView;
  using Pattern = ForwardTinyView;
  static_assert( std::ranges::input_range<View>);
  static_assert( std::ranges::forward_range<Pattern>);
  static_assert( std::ranges::view<View>);
  static_assert( std::ranges::view<Pattern>);
  static_assert( std::indirectly_comparable<
      std::ranges::iterator_t<View>, std::ranges::iterator_t<Pattern>, std::ranges::equal_to>);
  static_assert( CanInstantiate<View, Pattern>);

} // namespace test2

// `View` is not an input range.
namespace test3 {

  struct AlmostInputIterator {
    using value_type = char;
    using difference_type = ptrdiff_t;
    using iterator_concept = int;

    constexpr const char& operator*() const;
    constexpr AlmostInputIterator& operator++();
    constexpr void operator++(int);
    constexpr bool operator==(const AlmostInputIterator&) const;
  };

  static_assert( std::input_or_output_iterator<AlmostInputIterator>);
  static_assert(!std::input_iterator<AlmostInputIterator>);

  struct NonInputView : std::ranges::view_base {
    AlmostInputIterator begin() const;
    AlmostInputIterator end() const;
  };

  using View = NonInputView;
  using Pattern = ForwardTinyView;
  static_assert(!std::ranges::input_range<View>);
  static_assert( std::ranges::forward_range<Pattern>);
  static_assert( std::ranges::view<View>);
  static_assert( std::ranges::view<Pattern>);
  static_assert( std::indirectly_comparable<
      std::ranges::iterator_t<View>, std::ranges::iterator_t<Pattern>, std::ranges::equal_to>);
  static_assert(!CanInstantiate<View, Pattern>);

} // namespace test3

// `View` is not a view.
namespace test4 {

  using View = ForwardRange;
  using Pattern = ForwardView;
  static_assert( std::ranges::input_range<View>);
  static_assert( std::ranges::forward_range<Pattern>);
  static_assert(!std::ranges::view<View>);
  static_assert( std::ranges::view<Pattern>);
  static_assert( std::indirectly_comparable<
      std::ranges::iterator_t<View>, std::ranges::iterator_t<Pattern>, std::ranges::equal_to>);
  static_assert(!CanInstantiate<View, Pattern>);

} // namespace test4

// `Pattern` is not a forward range.
namespace test5 {

  using View = ForwardView;
  using Pattern = InputView;
  static_assert( std::ranges::input_range<View>);
  static_assert(!std::ranges::forward_range<Pattern>);
  static_assert( std::ranges::view<View>);
  static_assert( std::ranges::view<Pattern>);
  static_assert( std::indirectly_comparable<
      std::ranges::iterator_t<View>, std::ranges::iterator_t<Pattern>, std::ranges::equal_to>);
  static_assert(!CanInstantiate<View, Pattern>);

} // namespace test5

// Not indirectly comparable.
namespace test6 {

  struct Empty{};
  struct IntForwardView : std::ranges::view_base {
    constexpr forward_iterator<Empty*> begin() const { return {}; }
    constexpr forward_iterator<Empty*> end() const { return {}; }
  };

  using View = ForwardView;
  using Pattern = IntForwardView;
  static_assert( std::ranges::input_range<View>);
  static_assert( std::ranges::forward_range<Pattern>);
  static_assert( std::ranges::view<View>);
  static_assert( std::ranges::view<Pattern>);
  static_assert(!std::indirectly_comparable<
      std::ranges::iterator_t<View>, std::ranges::iterator_t<Pattern>, std::ranges::equal_to>);
  static_assert(!CanInstantiate<View, Pattern>);

} // namespace test6

// `View` is an input range and `Pattern` is not a tiny range.
namespace test7 {

  using View = InputView;
  using Pattern = ForwardView;
  static_assert( std::ranges::input_range<View>);
  static_assert(!std::ranges::forward_range<View>);
  static_assert( std::ranges::forward_range<Pattern>);
  LIBCPP_STATIC_ASSERT(!std::ranges::__tiny_range<Pattern>);
  static_assert( std::ranges::view<View>);
  static_assert( std::ranges::view<Pattern>);
  static_assert( std::indirectly_comparable<
      std::ranges::iterator_t<View>, std::ranges::iterator_t<Pattern>, std::ranges::equal_to>);
  static_assert(!CanInstantiate<View, Pattern>);

} // namespace test7

// `View` is an input range and `Pattern` is almost a tiny range, except the `size()` function is not `constexpr`.
namespace test8 {

  struct AlmostTinyRange : std::ranges::view_base {
    int* begin() const;
    int* end() const;
    static size_t size() { return 1; }
  };

  using View = InputView;
  using Pattern = AlmostTinyRange;
  static_assert( std::ranges::input_range<View>);
  static_assert(!std::ranges::forward_range<View>);
  static_assert( std::ranges::forward_range<Pattern>);
  LIBCPP_STATIC_ASSERT(!std::ranges::__tiny_range<Pattern>);
  static_assert( std::ranges::view<View>);
  static_assert( std::ranges::view<Pattern>);
  static_assert( std::indirectly_comparable<
      std::ranges::iterator_t<View>, std::ranges::iterator_t<Pattern>, std::ranges::equal_to>);
  static_assert(!CanInstantiate<View, Pattern>);

} // namespace test8

// `View` is an input range and `Pattern` is almost a tiny range, except the `size()` returns a number `>2`.
namespace test9 {

  struct AlmostTinyRange : std::ranges::view_base {
    int* begin() const;
    int* end() const;
    constexpr static size_t size() { return 2; }
  };

  using View = InputView;
  using Pattern = ForwardView;
  static_assert( std::ranges::input_range<View>);
  static_assert(!std::ranges::forward_range<View>);
  static_assert( std::ranges::forward_range<Pattern>);
  LIBCPP_STATIC_ASSERT(!std::ranges::__tiny_range<Pattern>);
  static_assert( std::ranges::view<View>);
  static_assert( std::ranges::view<Pattern>);
  static_assert( std::indirectly_comparable<
      std::ranges::iterator_t<View>, std::ranges::iterator_t<Pattern>, std::ranges::equal_to>);
  static_assert(!CanInstantiate<View, Pattern>);

} // namespace test9
