/// \file
// Range v3 library
//
//  Copyright Eric Niebler 2013-present
//
//  Use, modification and distribution is subject to the
//  Boost Software License, Version 1.0. (See accompanying
//  file LICENSE_1_0.txt or copy at
//  http://www.boost.org/LICENSE_1_0.txt)
//
// Project home: https://github.com/ericniebler/range-v3
//

#ifndef RANGES_V3_RANGE_ACCESS_HPP
#define RANGES_V3_RANGE_ACCESS_HPP

#include <functional> // for reference_wrapper (whose use with begin/end is deprecated)
#include <initializer_list>
#include <iterator>
#include <limits>
#ifdef __has_include
#if __has_include(<string_view>)
#include <string_view>
#endif
#endif
#include <utility>

#include <range/v3/range_fwd.hpp>

#include <range/v3/iterator/concepts.hpp>
#include <range/v3/iterator/reverse_iterator.hpp>
#include <range/v3/iterator/traits.hpp>
#include <range/v3/utility/static_const.hpp>

#include <range/v3/detail/disable_warnings.hpp>

namespace ranges
{
#if defined(__cpp_lib_string_view) && __cpp_lib_string_view > 0
    template<class CharT, class Traits>
    RANGES_INLINE_VAR constexpr bool
        enable_safe_range<std::basic_string_view<CharT, Traits>> = true;
#endif

    namespace detail
    {
        template<typename T>
        RANGES_INLINE_VAR constexpr bool _safe_range = enable_safe_range<uncvref_t<T>>;

        template<typename T>
        RANGES_INLINE_VAR constexpr bool _safe_range<T &> = true;
    }

    /// \cond
    namespace _begin_
    {
        // Poison pill for std::begin. (See the detailed discussion at
        // https://github.com/ericniebler/stl2/issues/139)
        template<typename T>
        void begin(T &&) = delete;

#ifdef RANGES_WORKAROUND_MSVC_895622
        void begin();
#endif

        template<typename T>
        void begin(std::initializer_list<T>) = delete;

        template<class I>
        auto is_iterator(I) -> CPP_ret(void)(requires input_or_output_iterator<I>);

        // clang-format off
        template<typename T>
        CPP_concept_bool has_member_begin =
            CPP_requires ((T &) t) //
            (
                _begin_::is_iterator(t.begin())
            );

        template<typename T>
        CPP_concept_bool has_non_member_begin =
            CPP_requires ((T &) t) //
            (
                _begin_::is_iterator(begin(t))
            );
        // clang-format on

        struct fn
        {
        private:
            template<bool>
            struct impl_
            {
                // has_member_begin == true
                template<typename R>
                constexpr auto operator()(R && r) const noexcept(noexcept(r.begin()))
                {
                    return r.begin();
                }
            };

            template<typename R>
            using impl = impl_<has_member_begin<R>>;

#ifdef RANGES_WORKAROUND_GCC_89953
            template<typename R>
            static constexpr impl<R> impl_v{};
#endif

        public:
            template<typename R, std::size_t N>
            void operator()(R(&&)[N]) const = delete;

            template<typename R, std::size_t N>
            constexpr R * operator()(R (&array)[N]) const noexcept
            {
                return array;
            }

            template<typename R>
#ifdef RANGES_WORKAROUND_GCC_89953
            constexpr auto CPP_fun(operator())(R && r)(
                const                                   //
                noexcept(noexcept(impl_v<R>(r))) //
                requires((detail::_safe_range<R>) && (has_member_begin<R> || has_non_member_begin<R>)))
#else
            constexpr auto CPP_fun(operator())(R && r)(
                const                                   //
                noexcept(noexcept(impl<R>{}(r))) //
                requires((detail::_safe_range<R>) && (has_member_begin<R> || has_non_member_begin<R>)))
#endif
            {
                return impl<R>{}(r);
            }
            template<typename T, typename Fn = fn>
            RANGES_DEPRECATED(
                "Using a reference_wrapper as a range is deprecated. Use views::ref "
                "instead.")
            constexpr auto
            operator()(std::reference_wrapper<T> ref) const
                noexcept(noexcept(Fn{}(ref.get()))) -> decltype(Fn{}(ref.get()))
            {
                return Fn{}(ref.get());
            }

            template<typename T, typename Fn = fn>
            RANGES_DEPRECATED(
                "Using a reference_wrapper as a range is deprecated. Use views::ref "
                "instead.")
            constexpr auto
            operator()(ranges::reference_wrapper<T> ref) const
                noexcept(noexcept(Fn{}(ref.get()))) -> decltype(Fn{}(ref.get()))
            {
                return Fn{}(ref.get());
            }
        };

        // has_member_begin == false
        template<>
        struct fn::impl_<false>
        {
            template<typename R>
            constexpr auto operator()(R && r) const noexcept(noexcept(begin(r)))
            {
                return begin(r);
            }
        };

        template<typename R>
        using _t = decltype(fn{}(std::declval<R>()));
    } // namespace _begin_
    /// \endcond

    /// \ingroup group-range
    /// \param r
    /// \return \c r, if \c r is an array. Otherwise, `r.begin()` if that expression is
    ///   well-formed and returns an input_or_output_iterator. Otherwise, `begin(r)` if
    ///   that expression returns an input_or_output_iterator.
    RANGES_DEFINE_CPO(_begin_::fn, begin)

    /// \cond
    namespace _end_
    {
        // Poison pill for std::end. (See the detailed discussion at
        // https://github.com/ericniebler/stl2/issues/139)
        template<typename T>
        void end(T &&) = delete;

#ifdef RANGES_WORKAROUND_MSVC_895622
        void end();
#endif

        template<typename T>
        void end(std::initializer_list<T>) = delete;

        template<typename I, typename S>
        auto _is_sentinel(S, I) -> CPP_ret(void)(requires sentinel_for<S, I>);

        // clang-format off
        template<typename T>
        CPP_concept_bool has_member_end =
            CPP_requires ((T &) t) //
            (
                _end_::_is_sentinel(t.end(), ranges::begin(t))
            );

        template<typename T>
        CPP_concept_bool has_non_member_end =
            CPP_requires ((T &) t) //
            (
                _end_::_is_sentinel(end(t), ranges::begin(t))
            );
        // clang-format on

        struct fn
        {
        private:
            template<bool>
            struct impl_
            {
                // has_member_end == true
                template<typename R>
                constexpr auto operator()(R && r) const noexcept(noexcept(r.end()))
                {
                    return r.end();
                }
            };

            template<typename Int>
            using iter_diff_t =
                meta::_t<detail::if_then_t<std::is_integral<Int>::value,
                                           std::make_signed<Int>, meta::id<Int>>>;

            template<typename R>
            using impl = impl_<has_member_end<R>>;

#ifdef RANGES_WORKAROUND_GCC_89953
            template<typename R>
            static constexpr impl<R> impl_v{};
#endif

        public:
            template<typename R, std::size_t N>
            void operator()(R(&&)[N]) const = delete;

            template<typename R, std::size_t N>
            constexpr R * operator()(R (&array)[N]) const noexcept
            {
                return array + N;
            }

            template<typename R>
#ifdef RANGES_WORKAROUND_GCC_89953
            constexpr auto CPP_fun(operator())(R && r)(
                const noexcept(noexcept(impl_v<R>(r))) //
                requires((detail::_safe_range<R>) && (has_member_end<R> || has_non_member_end<R>)))
#else
            constexpr auto CPP_fun(operator())(R && r)(
                const noexcept(noexcept(impl<R>{}(r))) //
                requires((detail::_safe_range<R>) && (has_member_end<R> || has_non_member_end<R>)))
#endif
            {
                return impl<R>{}(r);
            }

            template<typename T, typename Fn = fn>
            RANGES_DEPRECATED(
                "Using a reference_wrapper as a range is deprecated. Use views::ref "
                "instead.")
            constexpr auto
            operator()(std::reference_wrapper<T> ref) const
                noexcept(noexcept(Fn{}(ref.get()))) -> decltype(Fn{}(ref.get()))
            {
                return Fn{}(ref.get());
            }

            template<typename T, typename Fn = fn>
            RANGES_DEPRECATED(
                "Using a reference_wrapper as a range is deprecated. Use views::ref "
                "instead.")
            constexpr auto
            operator()(ranges::reference_wrapper<T> ref) const
                noexcept(noexcept(Fn{}(ref.get()))) -> decltype(Fn{}(ref.get()))
            {
                return Fn{}(ref.get());
            }

            template<typename Int>
            auto operator-(Int dist) const
                -> CPP_ret(detail::from_end_<iter_diff_t<Int>>)( //
                    requires detail::integer_like_<Int>)
            {
                using SInt = iter_diff_t<Int>;
                RANGES_EXPECT(0 <= dist);
                RANGES_EXPECT(dist <=
                              static_cast<Int>((std::numeric_limits<SInt>::max)()));
                return detail::from_end_<SInt>{-static_cast<SInt>(dist)};
            }
        };

        // has_member_end == false
        template<>
        struct fn::impl_<false>
        {
            template<typename R>
            constexpr auto operator()(R && r) const noexcept(noexcept(end(r)))
            {
                return end(r);
            }
        };

        template<typename R>
        using _t = decltype(fn{}(std::declval<R>()));
    } // namespace _end_
    /// \endcond

    /// \ingroup group-range
    /// \param r
    /// \return \c r+size(r), if \c r is an array. Otherwise, `r.end()` if that expression
    /// is
    ///   well-formed and returns an input_or_output_iterator. Otherwise, `end(r)` if that
    ///   expression returns an input_or_output_iterator.
    RANGES_DEFINE_CPO(_end_::fn, end)

    /// \cond
    namespace _cbegin_
    {
        struct fn
        {
            template<typename R>
            constexpr _begin_::_t<detail::as_const_t<R>> operator()(R && r) const
                noexcept(noexcept(ranges::begin(detail::as_const(r))))
            {
                return ranges::begin(detail::as_const(r));
            }
        };
    } // namespace _cbegin_
    /// \endcond

    /// \ingroup group-range
    /// \param r
    /// \return The result of calling `ranges::begin` with a const-qualified
    ///    reference to r.
    RANGES_INLINE_VARIABLE(_cbegin_::fn, cbegin)

    /// \cond
    namespace _cend_
    {
        struct fn
        {
            template<typename R>
            constexpr _end_::_t<detail::as_const_t<R>> operator()(R && r) const
                noexcept(noexcept(ranges::end(detail::as_const(r))))
            {
                return ranges::end(detail::as_const(r));
            }
        };
    } // namespace _cend_
    /// \endcond

    /// \ingroup group-range
    /// \param r
    /// \return The result of calling `ranges::end` with a const-qualified
    ///    reference to r.
    RANGES_INLINE_VARIABLE(_cend_::fn, cend)

    /// \cond
    namespace _rbegin_
    {
        template<typename R>
        void rbegin(R &&) = delete;
        // Non-standard, to keep unqualified rbegin(r) from finding std::rbegin
        // and returning a std::reverse_iterator.
        template<typename T>
        void rbegin(std::initializer_list<T>) = delete;
        template<typename T, std::size_t N>
        void rbegin(T (&)[N]) = delete;

        // clang-format off
        template<typename T>
        CPP_concept_bool has_member_rbegin =
            CPP_requires ((T &) t) //
            (
                _begin_::is_iterator(t.rbegin())
            );

        template<typename T>
        CPP_concept_bool has_non_member_rbegin =
            CPP_requires ((T &) t) //
            (
                _begin_::is_iterator(rbegin(t))
            );

        template<typename I>
        void _same_type(I, I);

        template<typename T>
        CPP_concept_bool can_reverse_end =
            CPP_requires ((T &&) t) //
            (
                // make_reverse_iterator is constrained with
                // bidirectional_iterator.
                ranges::make_reverse_iterator(ranges::end(t)),
                _rbegin_::_same_type(ranges::begin(t), ranges::end(t))
            );
        // clang-format on

        struct fn
        {
        private:
            // has_member_rbegin == true
            template<int>
            struct impl_
            {
                template<typename R>
                constexpr auto operator()(R && r) const noexcept(noexcept(r.rbegin()))
                {
                    return r.rbegin();
                }
            };

            template<typename R>
            using impl =
                impl_<has_member_rbegin<R> ? 0 : has_non_member_rbegin<R> ? 1 : 2>;

        public:
            template<typename R>
            constexpr auto CPP_fun(operator())(R && r)(
                const noexcept(noexcept(impl<R>{}(r))) //
                requires((detail::_safe_range<R>) && (has_member_rbegin<R> || has_non_member_rbegin<R> ||
                         can_reverse_end<R>)))
            {
                return impl<R>{}(r);
            }

            template<typename T, typename Fn = fn>
            RANGES_DEPRECATED(
                "Using a reference_wrapper as a range is deprecated. Use views::ref "
                "instead.")
            constexpr auto
            operator()(std::reference_wrapper<T> ref) const
                noexcept(noexcept(Fn{}(ref.get()))) -> decltype(Fn{}(ref.get()))
            {
                return Fn{}(ref.get());
            }

            template<typename T, typename Fn = fn>
            RANGES_DEPRECATED(
                "Using a reference_wrapper as a range is deprecated. Use views::ref "
                "instead.")
            constexpr auto
            operator()(ranges::reference_wrapper<T> ref) const
                noexcept(noexcept(Fn{}(ref.get()))) -> decltype(Fn{}(ref.get()))
            {
                return Fn{}(ref.get());
            }
        };

        // has_non_member_rbegin == true
        template<>
        struct fn::impl_<1>
        {
            template<typename R>
            constexpr auto operator()(R && r) const noexcept(noexcept(rbegin(r)))
            {
                return rbegin(r);
            }
        };

        // can_reverse_end
        template<>
        struct fn::impl_<2>
        {
            template<typename R>
            constexpr auto operator()(R && r) const
                noexcept(noexcept(ranges::make_reverse_iterator(ranges::end(r))))
            {
                return ranges::make_reverse_iterator(ranges::end(r));
            }
        };

        template<typename R>
        using _t = decltype(fn{}(std::declval<R>()));
    } // namespace _rbegin_
    /// \endcond

    /// \ingroup group-range
    /// \param r
    /// \return `make_reverse_iterator(r + ranges::size(r))` if r is an array. Otherwise,
    ///   `r.rbegin()` if that expression is well-formed and returns an
    ///   input_or_output_iterator. Otherwise, `make_reverse_iterator(ranges::end(r))` if
    ///   `ranges::begin(r)` and `ranges::end(r)` are both well-formed and have the same
    ///   type that satisfies `bidirectional_iterator`.
    RANGES_DEFINE_CPO(_rbegin_::fn, rbegin)

    /// \cond
    namespace _rend_
    {
        template<typename R>
        void rend(R &&) = delete;
        // Non-standard, to keep unqualified rend(r) from finding std::rend
        // and returning a std::reverse_iterator.
        template<typename T>
        void rend(std::initializer_list<T>) = delete;
        template<typename T, std::size_t N>
        void rend(T (&)[N]) = delete;

        // clang-format off
        template<typename T>
        CPP_concept_bool has_member_rend =
            CPP_requires ((T &) t) //
            (
                _end_::_is_sentinel(t.rend(), ranges::rbegin(t))
            );

        template<typename T>
        CPP_concept_bool has_non_member_rend =
            CPP_requires ((T &) t) //
            (
                _end_::_is_sentinel(rend(t), ranges::rbegin(t))
            );

        template<typename T>
        CPP_concept_bool can_reverse_begin =
            CPP_requires ((T &&) t) //
            (
                // make_reverse_iterator is constrained with
                // bidirectional_iterator.
                ranges::make_reverse_iterator(ranges::begin(t)),
                _rbegin_::_same_type(ranges::begin(t), ranges::end(t))
            );
        // clang-format on

        struct fn
        {
        private:
            // has_member_rbegin == true
            template<int>
            struct impl_
            {
                template<typename R>
                constexpr auto operator()(R && r) const noexcept(noexcept(r.rend()))
                {
                    return r.rend();
                }
            };

            template<typename R>
            using impl = impl_<has_member_rend<R> ? 0 : has_non_member_rend<R> ? 1 : 2>;

        public:
            template<typename R>
            constexpr auto CPP_fun(operator())(R && r)(
                const noexcept(noexcept(impl<R>{}(r))) //
                requires((detail::_safe_range<R>) && (has_member_rend<R> || has_non_member_rend<R> ||
                         can_reverse_begin<R>)))
            {
                return impl<R>{}(r);
            }

            template<typename T, typename Fn = fn>
            RANGES_DEPRECATED(
                "Using a reference_wrapper as a range is deprecated. Use views::ref "
                "instead.")
            constexpr auto
            operator()(std::reference_wrapper<T> ref) const
                noexcept(noexcept(Fn{}(ref.get()))) -> decltype(Fn{}(ref.get()))
            {
                return Fn{}(ref.get());
            }

            template<typename T, typename Fn = fn>
            RANGES_DEPRECATED(
                "Using a reference_wrapper as a range is deprecated. Use views::ref "
                "instead.")
            constexpr auto
            operator()(ranges::reference_wrapper<T> ref) const
                noexcept(noexcept(Fn{}(ref.get()))) -> decltype(Fn{}(ref.get()))
            {
                return Fn{}(ref.get());
            }
        };

        // has_non_member_rend == true
        template<>
        struct fn::impl_<1>
        {
            template<typename R>
            constexpr auto operator()(R && r) const noexcept(noexcept(rend(r)))
            {
                return rend(r);
            }
        };

        // can_reverse_begin
        template<>
        struct fn::impl_<2>
        {
            template<typename R>
            constexpr auto operator()(R && r) const
                noexcept(noexcept(ranges::make_reverse_iterator(ranges::begin(r))))
            {
                return ranges::make_reverse_iterator(ranges::begin(r));
            }
        };

        template<typename R>
        using _t = decltype(fn{}(std::declval<R>()));
    } // namespace _rend_
    /// \endcond

    /// \ingroup group-range
    /// \param r
    /// \return `make_reverse_iterator(r)` if `r` is an array. Otherwise,
    ///   `r.rend()` if that expression is well-formed and returns a type that
    ///   satisfies `sentinel_for<S, I>` where `I` is the type of `ranges::rbegin(r)`.
    ///   Otherwise, `make_reverse_iterator(ranges::begin(r))` if `ranges::begin(r)`
    ///   and `ranges::end(r)` are both well-formed and have the same type that
    ///   satisfies `bidirectional_iterator`.
    RANGES_DEFINE_CPO(_rend_::fn, rend)

    /// \cond
    namespace _crbegin_
    {
        struct fn
        {
            template<typename R>
            constexpr _rbegin_::_t<detail::as_const_t<R>> operator()(R && r) const
                noexcept(noexcept(ranges::rbegin(detail::as_const(r))))
            {
                return ranges::rbegin(detail::as_const(r));
            }
        };
    } // namespace _crbegin_
    /// \endcond

    /// \ingroup group-range
    /// \param r
    /// \return The result of calling `ranges::rbegin` with a const-qualified
    ///    reference to r.
    RANGES_INLINE_VARIABLE(_crbegin_::fn, crbegin)

    /// \cond
    namespace _crend_
    {
        struct fn
        {
            template<typename R>
            constexpr _rend_::_t<detail::as_const_t<R>> operator()(R && r) const
                noexcept(noexcept(ranges::rend(detail::as_const(r))))
            {
                return ranges::rend(detail::as_const(r));
            }
        };
    } // namespace _crend_
    /// \endcond

    /// \ingroup group-range
    /// \param r
    /// \return The result of calling `ranges::rend` with a const-qualified
    ///    reference to r.
    RANGES_INLINE_VARIABLE(_crend_::fn, crend)

    template<typename Rng>
    using iterator_t = decltype(begin(std::declval<Rng &>()));

    template<typename Rng>
    using sentinel_t = decltype(end(std::declval<Rng &>()));

    namespace cpp20
    {
        using ranges::begin;
        using ranges::cbegin;
        using ranges::cend;
        using ranges::crbegin;
        using ranges::crend;
        using ranges::end;
        using ranges::rbegin;
        using ranges::rend;

        using ranges::iterator_t;
        using ranges::sentinel_t;
    } // namespace cpp20
} // namespace ranges

#include <range/v3/detail/reenable_warnings.hpp>

#endif
