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

// transform_view::<iterator>::difference_type
// transform_view::<iterator>::value_type
// transform_view::<iterator>::iterator_category
// transform_view::<iterator>::iterator_concept

#include <ranges>

#include "test_macros.h"
#include "../types.h"

template<class V, class F>
concept HasIterCategory = requires { typename std::ranges::transform_view<V, F>::iterator_category; };

constexpr bool test() {
  {
    // Member typedefs for contiguous iterator.
    static_assert(std::same_as<std::iterator_traits<int*>::iterator_concept, std::contiguous_iterator_tag>);
    static_assert(std::same_as<std::iterator_traits<int*>::iterator_category, std::random_access_iterator_tag>);

    using TView = std::ranges::transform_view<MoveOnlyView, Increment>;
    using TIter = std::ranges::iterator_t<TView>;
    static_assert(std::same_as<typename TIter::iterator_concept, std::random_access_iterator_tag>);
    static_assert(std::same_as<typename TIter::iterator_category, std::random_access_iterator_tag>);
    static_assert(std::same_as<typename TIter::value_type, int>);
    static_assert(std::same_as<typename TIter::difference_type, std::ptrdiff_t>);
  }
  {
    // Member typedefs for random access iterator.
    using TView = std::ranges::transform_view<RandomAccessView, Increment>;
    using TIter = std::ranges::iterator_t<TView>;
    static_assert(std::same_as<typename TIter::iterator_concept, std::random_access_iterator_tag>);
    static_assert(std::same_as<typename TIter::iterator_category, std::random_access_iterator_tag>);
    static_assert(std::same_as<typename TIter::value_type, int>);
    static_assert(std::same_as<typename TIter::difference_type, std::ptrdiff_t>);
  }
  {
    // Member typedefs for random access iterator, LWG3798 rvalue reference.
    using TView = std::ranges::transform_view<RandomAccessView, IncrementRvalueRef>;
    using TIter = std::ranges::iterator_t<TView>;
    static_assert(std::same_as<typename TIter::iterator_concept, std::random_access_iterator_tag>);
    static_assert(std::same_as<typename TIter::iterator_category, std::random_access_iterator_tag>);
    static_assert(std::same_as<typename TIter::value_type, int>);
    static_assert(std::same_as<typename TIter::difference_type, std::ptrdiff_t>);
  }
  {
    // Member typedefs for random access iterator/not-lvalue-ref.
    using TView = std::ranges::transform_view<RandomAccessView, PlusOneMutable>;
    using TIter = std::ranges::iterator_t<TView>;
    static_assert(std::same_as<typename TIter::iterator_concept, std::random_access_iterator_tag>);
    static_assert(std::same_as<typename TIter::iterator_category, std::input_iterator_tag>); // Note: this is now input_iterator_tag.
    static_assert(std::same_as<typename TIter::value_type, int>);
    static_assert(std::same_as<typename TIter::difference_type, std::ptrdiff_t>);
  }
  {
    // Member typedefs for random access iterator, LWG3564 mutable overload
    using TView = std::ranges::transform_view<RandomAccessView, PlusOneMutableOverload>;
    using TIter = std::ranges::iterator_t<TView>;
    static_assert(std::same_as<typename TIter::iterator_concept, std::random_access_iterator_tag>);
    static_assert(std::same_as<typename TIter::iterator_category, std::random_access_iterator_tag>);
    static_assert(std::same_as<typename TIter::value_type, int>);
    static_assert(std::same_as<typename TIter::difference_type, std::ptrdiff_t>);

    using CTIter = std::ranges::iterator_t<const TView>;
    static_assert(std::same_as<typename CTIter::iterator_concept, std::random_access_iterator_tag>);
    static_assert(std::same_as<typename CTIter::iterator_category,
                               std::input_iterator_tag>); // Note: this is now input_iterator_tag.
    static_assert(std::same_as<typename CTIter::value_type, int>);
    static_assert(std::same_as<typename CTIter::difference_type, std::ptrdiff_t>);
  }
  {
    // Member typedefs for bidirectional iterator.
    using TView = std::ranges::transform_view<BidirectionalView, Increment>;
    using TIter = std::ranges::iterator_t<TView>;
    static_assert(std::same_as<typename TIter::iterator_concept, std::bidirectional_iterator_tag>);
    static_assert(std::same_as<typename TIter::iterator_category, std::bidirectional_iterator_tag>);
    static_assert(std::same_as<typename TIter::value_type, int>);
    static_assert(std::same_as<typename TIter::difference_type, std::ptrdiff_t>);
  }
  {
    // Member typedefs for forward iterator.
    using TView = std::ranges::transform_view<ForwardView, Increment>;
    using TIter = std::ranges::iterator_t<TView>;
    static_assert(std::same_as<typename TIter::iterator_concept, std::forward_iterator_tag>);
    static_assert(std::same_as<typename TIter::iterator_category, std::forward_iterator_tag>);
    static_assert(std::same_as<typename TIter::value_type, int>);
    static_assert(std::same_as<typename TIter::difference_type, std::ptrdiff_t>);
  }
  {
    // Member typedefs for input iterator.
    using TView = std::ranges::transform_view<InputView, Increment>;
    using TIter = std::ranges::iterator_t<TView>;
    static_assert(std::same_as<typename TIter::iterator_concept, std::input_iterator_tag>);
    static_assert(!HasIterCategory<InputView, Increment>);
    static_assert(std::same_as<typename TIter::value_type, int>);
    static_assert(std::same_as<typename TIter::difference_type, std::ptrdiff_t>);
  }

  return true;
}

int main(int, char**) {
  test();
  static_assert(test());

  return 0;
}
