//===----------------------------------------------------------------------===//
//
// 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
//
//===----------------------------------------------------------------------===//

#ifndef TEST_STD_RANGES_RANGE_ADAPTORS_RANGE_FILTER_TYPES_H
#define TEST_STD_RANGES_RANGE_ADAPTORS_RANGE_FILTER_TYPES_H

#include <ranges>
#include <utility>

struct TrackInitialization {
  constexpr explicit TrackInitialization(bool* moved, bool* copied) : moved_(moved), copied_(copied) { }
  constexpr TrackInitialization(TrackInitialization const& other) : moved_(other.moved_), copied_(other.copied_) {
    *copied_ = true;
  }
  constexpr TrackInitialization(TrackInitialization&& other) : moved_(other.moved_), copied_(other.copied_) {
    *moved_ = true;
  }
  TrackInitialization& operator=(TrackInitialization const&) = default;
  TrackInitialization& operator=(TrackInitialization&&) = default;
  bool* moved_;
  bool* copied_;
};

struct AlwaysTrue {
  template <typename T>
  constexpr bool operator()(T const&) const { return true; }
};

template <class Iterator, class Sentinel>
struct minimal_view : std::ranges::view_base {
  constexpr explicit minimal_view(Iterator it, Sentinel sent)
    : it_(base(std::move(it)))
    , sent_(base(std::move(sent)))
  { }

  minimal_view(minimal_view&&) = default;
  minimal_view& operator=(minimal_view&&) = default;

  constexpr Iterator begin() const { return Iterator(it_); }
  constexpr Sentinel end() const { return Sentinel(sent_); }

private:
  decltype(base(std::declval<Iterator>())) it_;
  decltype(base(std::declval<Sentinel>())) sent_;
};

template <bool IsNoexcept>
class NoexceptIterMoveInputIterator {
  int *it_;

public:
  using iterator_category = std::input_iterator_tag;
  using value_type = int;
  using difference_type = typename std::iterator_traits<int *>::difference_type;
  using pointer = int*;
  using reference = int&;

  NoexceptIterMoveInputIterator() = default;
  explicit constexpr NoexceptIterMoveInputIterator(int *it) : it_(it) {}

  friend constexpr decltype(auto) iter_move(const NoexceptIterMoveInputIterator& it) noexcept(IsNoexcept) {
    return std::ranges::iter_move(it.it_);
  }

  friend constexpr int* base(const NoexceptIterMoveInputIterator& i) { return i.it_; }

  constexpr reference operator*() const { return *it_; }
  constexpr NoexceptIterMoveInputIterator& operator++() {++it_; return *this;}
  constexpr NoexceptIterMoveInputIterator operator++(int)
  { NoexceptIterMoveInputIterator tmp(*this); ++(*this); return tmp; }
};

template <bool IsNoexcept>
class NoexceptIterSwapInputIterator {
  int *it_;

public:
  using iterator_category = std::input_iterator_tag;
  using value_type = int;
  using difference_type = typename std::iterator_traits<int *>::difference_type;
  using pointer = int*;
  using reference = int&;

  NoexceptIterSwapInputIterator() = default;
  explicit constexpr NoexceptIterSwapInputIterator(int *it) : it_(it) {}

  friend constexpr void iter_swap(const NoexceptIterSwapInputIterator& a, const NoexceptIterSwapInputIterator& b) noexcept(IsNoexcept) {
    return std::ranges::iter_swap(a.it_, b.it_);
  }

  friend constexpr int* base(const NoexceptIterSwapInputIterator& i) { return i.it_; }

  constexpr reference operator*() const { return *it_; }
  constexpr NoexceptIterSwapInputIterator& operator++() {++it_; return *this;}
  constexpr NoexceptIterSwapInputIterator operator++(int)
  { NoexceptIterSwapInputIterator tmp(*this); ++(*this); return tmp; }
};

#endif // TEST_STD_RANGES_RANGE_ADAPTORS_RANGE_FILTER_TYPES_H
