//===----------------------------------------------------------------------===//
//
//                     The LLVM Compiler Infrastructure
//
// This file is dual licensed under the MIT and the University of Illinois Open
// Source Licenses. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// UNSUPPORTED: c++98, c++03

// <type_traits>

// __lazy_enable_if, __lazy_not, __lazy_and and __lazy_or

// Test the libc++ lazy meta-programming helpers in <type_traits>

#include <type_traits>

template <class Type>
struct Identity {
    typedef Type type;
};

typedef std::true_type TrueT;
typedef std::false_type FalseT;

typedef Identity<TrueT>  LazyTrueT;
typedef Identity<FalseT> LazyFalseT;

// A type that cannot be instantiated
template <class T>
struct CannotInst {
    typedef T type;
    static_assert(std::is_same<T, T>::value == false, "");
};


template <int Value>
struct NextInt {
    typedef NextInt<Value + 1> type;
    static const int value = Value;
};

template <int Value>
const int NextInt<Value>::value;


template <class Type>
struct HasTypeImp {
    template <class Up, class = typename Up::type>
    static TrueT test(int);
    template <class>
    static FalseT test(...);

    typedef decltype(test<Type>(0)) type;
};

// A metafunction that returns True if Type has a nested 'type' typedef
// and false otherwise.
template <class Type>
struct HasType : HasTypeImp<Type>::type {};

void LazyEnableIfTest() {
    {
        typedef std::__lazy_enable_if<true, NextInt<0> > Result;
        static_assert(HasType<Result>::value, "");
        static_assert(Result::type::value == 1, "");
    }
    {
        typedef std::__lazy_enable_if<false, CannotInst<int> > Result;
        static_assert(!HasType<Result>::value, "");
    }
}

void LazyNotTest() {
    {
        typedef std::__lazy_not<LazyTrueT> NotT;
        static_assert(std::is_same<typename NotT::type, FalseT>::value, "");
        static_assert(NotT::value == false, "");
    }
    {
        typedef std::__lazy_not<LazyFalseT> NotT;
        static_assert(std::is_same<typename NotT::type, TrueT>::value, "");
        static_assert(NotT::value == true, "");
    }
    {
         // Check that CannotInst<int> is not instantiated.
        typedef std::__lazy_not<CannotInst<int> > NotT;

        static_assert(std::is_same<NotT, NotT>::value, "");

    }
}

void LazyAndTest() {
    { // Test that it acts as the identity function for a single value
        static_assert(std::__lazy_and<LazyFalseT>::value == false, "");
        static_assert(std::__lazy_and<LazyTrueT>::value == true, "");
    }
    {
        static_assert(std::__lazy_and<LazyTrueT, LazyTrueT>::value == true, "");
        static_assert(std::__lazy_and<LazyTrueT, LazyFalseT>::value == false, "");
        static_assert(std::__lazy_and<LazyFalseT, LazyTrueT>::value == false, "");
        static_assert(std::__lazy_and<LazyFalseT, LazyFalseT>::value == false, "");
    }
    { // Test short circuiting - CannotInst<T> should never be instantiated.
        static_assert(std::__lazy_and<LazyFalseT, CannotInst<int>>::value == false, "");
        static_assert(std::__lazy_and<LazyTrueT, LazyFalseT, CannotInst<int>>::value == false, "");
    }
}


void LazyOrTest() {
    { // Test that it acts as the identity function for a single value
        static_assert(std::__lazy_or<LazyFalseT>::value == false, "");
        static_assert(std::__lazy_or<LazyTrueT>::value == true, "");
    }
    {
        static_assert(std::__lazy_or<LazyTrueT, LazyTrueT>::value == true, "");
        static_assert(std::__lazy_or<LazyTrueT, LazyFalseT>::value == true, "");
        static_assert(std::__lazy_or<LazyFalseT, LazyTrueT>::value == true, "");
        static_assert(std::__lazy_or<LazyFalseT, LazyFalseT>::value == false, "");
    }
    { // Test short circuiting - CannotInst<T> should never be instantiated.
        static_assert(std::__lazy_or<LazyTrueT, CannotInst<int>>::value == true, "");
        static_assert(std::__lazy_or<LazyFalseT, LazyTrueT, CannotInst<int>>::value == true, "");
    }
}


int main() {
    LazyEnableIfTest();
    LazyNotTest();
    LazyAndTest();
    LazyOrTest();
}