// cyCodeBase by Cem Yuksel
// [www.cemyuksel.com]
//-------------------------------------------------------------------------------
//! \file   cyCore.h 
//! \author Cem Yuksel
//! 
//! \brief  Core functions and macros
//! 
//! Core functions and macros for math and other common operations
//! 
//-------------------------------------------------------------------------------
//
// Copyright (c) 2016, Cem Yuksel <cem@cemyuksel.com>
// All rights reserved.
// 
// Permission is hereby granted, free of charge, to any person obtaining a copy 
// of this software and associated documentation files (the "Software"), to deal 
// in the Software without restriction, including without limitation the rights 
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 
// copies of the Software, and to permit persons to whom the Software is 
// furnished to do so, subject to the following conditions:
// 
// The above copyright notice and this permission notice shall be included in all 
// copies or substantial portions of the Software.
// 
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 
// SOFTWARE.
// 
//-------------------------------------------------------------------------------

#ifndef _CY_CORE_H_INCLUDED_
#define _CY_CORE_H_INCLUDED_

//-------------------------------------------------------------------------------

#ifndef _CY_CORE_MEMCPY_LIMIT
#define _CY_CORE_MEMCPY_LIMIT 256
#endif

//-------------------------------------------------------------------------------

#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <assert.h>
#include <emmintrin.h>
#include <cmath>
#include <type_traits>
#include <limits>

//-------------------------------------------------------------------------------
namespace cy {
//-------------------------------------------------------------------------------
//////////////////////////////////////////////////////////////////////////
// Compiler compatibility
//////////////////////////////////////////////////////////////////////////

#if defined(__INTEL_COMPILER)
# define _CY_COMPILER_INTEL __INTEL_COMPILER
# define _CY_COMPILER_VER_MEETS(msc,gcc,clang,intel) _CY_COMPILER_INTEL >= intel
# define _CY_COMPILER_VER_BELOW(msc,gcc,clang,intel) _CY_COMPILER_INTEL <  intel
#elif defined(__clang__)
# define _CY_COMPILER_CLANG (__clang_major__ * 10000 + __clang_minor__ * 100 + __clang_patchlevel__)
# define _CY_COMPILER_VER_MEETS(msc,gcc,clang,intel) _CY_COMPILER_CLANG >= clang
# define _CY_COMPILER_VER_BELOW(msc,gcc,clang,intel) _CY_COMPILER_CLANG <  clang
#elif defined(_MSC_VER)
# define _CY_COMPILER_MSC _MSC_VER
# define _CY_COMPILER_VER_MEETS(msc,gcc,clang,intel) _CY_COMPILER_MSC >= msc
# define _CY_COMPILER_VER_BELOW(msc,gcc,clang,intel) _CY_COMPILER_MSC <  msc
#elif __GNUC__
# define _CY_COMPILER_GCC (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__)
# define _CY_COMPILER_VER_MEETS(msc,gcc,clang,intel) _CY_COMPILER_GCC >= gcc
# define _CY_COMPILER_VER_BELOW(msc,gcc,clang,intel) _CY_COMPILER_GCC <  gcc
#else
# define _CY_COMPILER_UNKNOWN
# define _CY_COMPILER_VER_MEETS(msc,gcc,clang,intel) false
# define _CY_COMPILER_VER_BELOW(msc,gcc,clang,intel) false
#endif

// constexpr
#ifndef __cpp_constexpr
# if _CY_COMPILER_VER_MEETS(1900,40600,30100,1310)
#  define __cpp_constexpr
# else
#  define constexpr
# endif
#endif

// nullptr
#if _CY_COMPILER_VER_BELOW(1600,40600,20900,1210)
class _cy_nullptr_t {
public:
  template<class T> operator T*() const { return 0; }
  template<class C, class T> operator T C::*() const { return 0; }
private:
  void operator & () const {}
};
static _cy_nullptr_t nullptr;
#endif

// template aliases
#define _CY_TEMPLATE_ALIAS_UNPACK(...) __VA_ARGS__
#if _CY_COMPILER_VER_BELOW(1800,40700,30000,1210)
# define _CY_TEMPLATE_ALIAS(template_name,template_equivalent) class template_name : public _CY_TEMPLATE_ALIAS_UNPACK template_equivalent {}
#else
# define _CY_TEMPLATE_ALIAS(template_name,template_equivalent) using template_name = _CY_TEMPLATE_ALIAS_UNPACK template_equivalent
#endif

// std::is_trivially_copyable
#if _CY_COMPILER_VER_MEETS(1700,50000,30400,1300)
# define _cy_std_is_trivially_copyable 1
#endif

// restrict
#if defined(__INTEL_COMPILER)
//# define restrict restrict
#elif defined(__clang__)
# define restrict __restrict__
#elif defined(_MSC_VER)
# define restrict __restrict
#elif __GNUC__
# define restrict __restrict__
#else
# define restrict
#endif

// alignment
#if _CY_COMPILER_VER_BELOW(1900,40800,30000,1500)
# if defined(_MSC_VER)
#  define alignas(alignment_size) __declspec(align(alignment_size))
# else
#  define alignas(alignment_size) __attribute__((aligned(alignment_size)))
# endif
#endif

// final, override
#if _CY_COMPILER_VER_BELOW(1700,40700,20900,1210)
# define override
# if defined(_MSC_VER)
#  define final sealed
# else
#  define final
# endif
#endif

// static_assert
#if _CY_COMPILER_VER_BELOW(1900,60000,20500,1800)
# define static_assert(condition,message) assert(condition && message)
#endif

// unrestricted unions
#ifndef __cpp_unrestricted_unions
# if _CY_COMPILER_VER_MEETS(1900,40600,30000,1400)
#  define __cpp_unrestricted_unions
# endif
#endif

//////////////////////////////////////////////////////////////////////////
// Auto Vectorization
//////////////////////////////////////////////////////////////////////////

#ifdef _MSC_VER
# if _MSC_VER >= 1700
#  define _CY_IVDEP __pragma(loop(ivdep))
# endif
#elif defined __GNUC__
# if _CY_GCC_VER >= 40900
#  define _CY_IVDEP _Pragma("GCC ivdep");
# endif
#elif defined __clang__
# if _CY_CLANG_VER >= 30500
#  define _CY_IVDEP _Pragma("clang loop vectorize(enable) interleave(enable)");
# endif
#else
//# define _CY_IVDEP _Pragma("ivdep");
# define _CY_IVDEP
#endif

#ifndef _CY_IVDEP
# define _CY_IVDEP
#endif

#define _CY_IVDEP_FOR _CY_IVDEP for

//////////////////////////////////////////////////////////////////////////
// Disabling MSVC's non-standard depreciation warnings
//////////////////////////////////////////////////////////////////////////

#if defined(_MSC_VER) && !defined(_CRT_SECURE_NO_WARNINGS)
# define _CY_CRT_SECURE_NO_WARNINGS     __pragma( warning(push) ) __pragma( warning(disable:4996) )
# define _CY_CRT_SECURE_RESUME_WARNINGS __pragma( warning(pop)  )
#else
# define _CY_CRT_SECURE_NO_WARNINGS
# define _CY_CRT_SECURE_RESUME_WARNINGS
#endif

//////////////////////////////////////////////////////////////////////////
// Math functions
//////////////////////////////////////////////////////////////////////////

//!@name Common math function templates

template <typename T> inline T Max      ( T const &v1, T const &v2 ) { return v1 >= v2 ? v1 : v2; }
template <typename T> inline T Min      ( T const &v1, T const &v2 ) { return v1 <= v2 ? v1 : v2; }
template <typename T> inline T Max      ( T const &v1, T const &v2, T const &v3 ) { return (v1>=v2) ? (v1>=v3?v1:v3) : (v2>=v3?v2:v3); }
template <typename T> inline T Min      ( T const &v1, T const &v2, T const &v3 ) { return (v1<=v2) ? (v1<=v3?v1:v3) : (v2<=v3?v2:v3); }
template <typename T> inline T Clamp    ( T const &v, T minVal=T(0), T maxVal=T(1) ) { return Min(maxVal,Max(minVal,v)); }

template <typename T> inline T ACosSafe ( T const &v ) { return (T) std::acos(Clamp(v,T(-1),T(1))); }
template <typename T> inline T ASinSafe ( T const &v ) { return (T) std::asin(Clamp(v,T(-1),T(1))); }
template <typename T> inline T Sqrt     ( T const &v ) { return (T) std::sqrt(v); }
template <typename T> inline T SqrtSafe ( T const &v ) { return (T) std::sqrt(Max(v,T(0))); }

//template<> inline float  Sqrt<float> ( float  const &v ) { return std::sqrtf(v); }
template<> inline float  Sqrt    <float> ( float  const &v ) { return _mm_cvtss_f32(_mm_sqrt_ss(_mm_set_ps1(v))); }
template<> inline float  SqrtSafe<float> ( float  const &v ) { return _mm_cvtss_f32(_mm_sqrt_ss(_mm_set_ps1(Max(v,0.0f)))); }
template<> inline double Sqrt    <double>( double const &v ) { __m128d t=_mm_set1_pd(v);          return _mm_cvtsd_f64(_mm_sqrt_sd(t,t)); }
template<> inline double SqrtSafe<double>( double const &v ) { __m128d t=_mm_set1_pd(Max(v,0.0)); return _mm_cvtsd_f64(_mm_sqrt_sd(t,t)); }

template<typename T> inline T Pi  () { return T(3.141592653589793238462643383279502884197169); }

template <typename T> bool IsFinite( T const &v ) { return std::numeric_limits<T>::is_integer || std::isfinite(v); }

//////////////////////////////////////////////////////////////////////////
// Memory Operations
//////////////////////////////////////////////////////////////////////////

template <typename T>
void MemCopy( T * restrict dest, T const * restrict src, size_t count )
{
#ifdef _cy_std_is_trivially_copyable
	if ( std::is_trivially_copyable<T>() ) {
		memcpy( dest, src, (count)*sizeof(T) );
	} else 
#endif
		for ( size_t i=0; i<count; ++i ) dest[i] = src[i];
}

template <typename T, typename S>
void MemConvert( T * restrict dest, S const * restrict src, size_t count )
{
	for ( size_t i=0; i<count; ++i ) dest[i] = reinterpret_cast<T>(src[i]);
}

template <typename T>
void MemClear( T * dest, size_t count )
{
	memset( dest, 0, count*sizeof(T) );
}

template <typename T> inline void Swap     ( T &v1, T &v2 ) { if ( std::is_trivially_copyable<T>::value ) { T t=v1; v1=v2; v2=t; } else SwapBytes(v1,v2); }
template <typename T> inline void SwapBytes( T &v1, T &v2 ) { char t[sizeof(T)]; memcpy(&t,&v1,sizeof(T)); memcpy(&v1,&v2,sizeof(T)); memcpy(&v2,&t,sizeof(T)); }

/////////////////////////////////////////////////////////////////////////////////
// Defaulted and Deleted Functions
/////////////////////////////////////////////////////////////////////////////////

#if _CY_COMPILER_VER_MEETS(1800,40400,30000,1200)
# define CY_CLASS_FUNCTION_DEFAULT = default;
# define CY_CLASS_FUNCTION_DELETE  = delete;
#else
# define CY_CLASS_FUNCTION_DEFAULT {}
# define CY_CLASS_FUNCTION_DELETE  { static_assert(false,"Calling deleted method."); }
#endif

//////////////////////////////////////////////////////////////////////////

//-------------------------------------------------------------------------------
} // namespace cy
//-------------------------------------------------------------------------------

#endif

