//#####################################################################
// Copyright 2006-2009, Geoffrey Irving, Nipun Kwatra, Craig Schroeder, Andrew Selle, Tamar Shinar, Eftychios Sifakis, Jonathan Su.
// This file is part of PhysBAM whose distribution is governed by the license contained in the accompanying file PHYSBAM_COPYRIGHT.txt.
//#####################################################################
// Class TYPE_UTILITIES
//#####################################################################
#ifndef __TYPE_UTILITIES__
#define __TYPE_UTILITIES__

#include <cstdio>
#include <cstdlib>
#include <type_traits>

namespace PhysBAM{
using std::add_const;
using std::add_const_t;
using std::add_lvalue_reference;
using std::add_lvalue_reference_t;
using std::add_pointer;
using std::add_pointer_t;
using std::conditional;
using std::conditional_t;
using std::enable_if;
using std::enable_if_t;
using std::is_base_of;
using std::is_base_of_v;
using std::is_class;
using std::is_class_v;
using std::is_const;
using std::is_const_v;
using std::is_convertible;
using std::is_convertible_v;
using std::is_empty;
using std::is_empty_v;
using std::is_enum;
using std::is_enum_v;
using std::is_floating_point;
using std::is_floating_point_v;
using std::is_fundamental;
using std::is_fundamental_v;
using std::is_integral;
using std::is_integral_v;
using std::is_pointer;
using std::is_pointer_v;
using std::is_reference;
using std::is_reference_v;
using std::is_same;
using std::is_same_v;
using std::is_scalar;
using std::is_scalar_v;
using std::remove_const;
using std::remove_const_t;
using std::remove_pointer;
using std::remove_pointer_t;
using std::remove_reference;
using std::remove_reference_t;

template<class T1,class T2> struct ASSERT_SAME_HELPER{static const bool value=false;};
template<class T> struct ASSERT_SAME_HELPER<T,T>{static const bool value=true;};

#define static_assert_SAME(T1,T2) static_assert(::PhysBAM::ASSERT_SAME_HELPER<T1,T2>::value,"ASSERT_SAME_HELPER<"#T1","#T2">")

template<class T,class... U> struct FIRST{typedef T TYPE;};

template<class T_ARRAY,class ENABLER=void> struct IS_ARRAY {static const bool value=false;};
template<class T_ARRAY> struct IS_ARRAY<const T_ARRAY>:public IS_ARRAY<T_ARRAY>{};

template<class T_ARRAY,class ENABLER=void> struct IS_ARRAY_VIEW {static const bool value=false;};
template<class T_ARRAY> struct IS_ARRAY_VIEW<const T_ARRAY>:public IS_ARRAY_VIEW<T_ARRAY>{};

template<class T_ARRAY,class ENABLER=void> struct HAS_POINTER {static const bool value=false;};
template<class T_ARRAY> struct HAS_POINTER<const T_ARRAY>:public HAS_POINTER<T_ARRAY>{};

template<class T,int d> class VECTOR;
template<class TV> struct FIXED_SIZE_VECTOR {static const int value=false;static const int size=-1;};
template<class T,int d> struct FIXED_SIZE_VECTOR<VECTOR<T,d> > {static const int value=true;static const int size=d;};

template<class T> struct HAS_CHEAP_COPY {static const bool value=is_fundamental<T>::value || is_enum<T>::value || IS_ARRAY_VIEW<T>::value;};

template<class T,class RW,class ENABLER=void> struct IS_BINARY_IO_SAFE;

template<class T> struct IS_FLOATING_POINT {static const bool value=std::is_floating_point<T>::value;};

// This must appear at global scope before HAS_MEMBER can be used.
#define TEST_FOR_MEMBER(member_name) \
    template<class T> std::enable_if_t<(sizeof(&T::member_name)>0),int> TEST_FOR_MEMBER_##member_name(int);\
    template<class T> char TEST_FOR_MEMBER_##member_name(char);

// must use TEST_FOR_MEMBER at global scope before using this.
#define HAS_MEMBER(class_name,member_name) (sizeof(TEST_FOR_MEMBER_##member_name<class_name>(1))>1)

// use ND<T> instead of T to prevent the compiler from trying to deduce T from a function argument.
template<class T> struct ND_HELPER {typedef T TYPE;};
template<class T> using ND=typename ND_HELPER<T>::TYPE;

}
#endif
