// Copyright 2016-2019 Francesco Biscani (bluescarni@gmail.com)
//
// This file is part of the mp++ library.
//
// This Source Code Form is subject to the terms of the Mozilla
// Public License v. 2.0. If a copy of the MPL was not distributed
// with this file, You can obtain one at http://mozilla.org/MPL/2.0/.

#ifndef MPPP_CONFIG_HPP
#define MPPP_CONFIG_HPP

// Start of defines instantiated by CMake.
// clang-format off
#define MPPP_VERSION_STRING "0.15"
#define MPPP_VERSION_MAJOR 0
#define MPPP_VERSION_MINOR 15
#define MPPP_WITH_MPFR


// clang-format on
// End of defines instantiated by CMake.

// Compiler configuration.

#if defined(__clang__) || defined(__GNUC__) || defined(__INTEL_COMPILER)

// NOTE: GCC, clang (including clang-cl) and the Intel compiler support
// __builtin_expect() and __restrict.
#define mppp_likely(x) __builtin_expect(static_cast<bool>(x), 1)
#define mppp_unlikely(x) __builtin_expect(static_cast<bool>(x), 0)
#define MPPP_RESTRICT __restrict

#elif defined(_MSC_VER)

// NOTE: MSVC (without clang-cl) only supports __restrict.
#define mppp_likely(x) (x)
#define mppp_unlikely(x) (x)
#define MPPP_RESTRICT __restrict

#else

// Otherwise, disable both __builtin_expect() and __restrict.
#define mppp_likely(x) (x)
#define mppp_unlikely(x) (x)
#define MPPP_RESTRICT

#endif

// thread_local configuration:
// - on clang/osx, this seems to be supported since xcode 8:
//   https://stackoverflow.com/questions/28094794/why-does-apple-clang-disallow-c11-thread-local-when-official-clang-supports
//   Note that additional conditions might be needed for iOS, if it ever comes
//   to that. Regarding the versioning, it seems the clang version macros are set
//   to the xcode version:
//   https://stackoverflow.com/questions/19387043/how-can-i-reliably-detect-the-version-of-clang-at-preprocessing-time
//   xcode 8.x is appleclang 8.y:
//   https://en.wikipedia.org/wiki/Xcode#8.x_series
// - at least some MinGW versions have a buggy thread_local implementation. This is shown by testing,
//   and reported in a bunch of places as well:
//   https://sourceforge.net/p/mingw-w64/bugs/445/
//   https://github.com/Alexpux/MINGW-packages/issues/2519
// - at least some early versions of the Intel compiler have a buggy implementation. This
//   came out from testing, but it might be fixed now.

#if (defined(__APPLE__) && __clang_major__ < 8) || defined(__MINGW32__) || defined(__INTEL_COMPILER)

#define MPPP_MAYBE_TLS

#else

// For the rest, we assume thread_local is available.
#define MPPP_MAYBE_TLS static thread_local
#define MPPP_HAVE_THREAD_LOCAL

#endif

// Concepts setup.
#if defined(__cpp_concepts)

#define MPPP_HAVE_CONCEPTS

#endif

// C++ standard setup.
// NOTE: this is necessary because at this time MSVC does not set correctly the
// __cplusplus macro.
#if defined(_MSC_VER)

#define MPPP_CPLUSPLUS _MSVC_LANG

#else

#define MPPP_CPLUSPLUS __cplusplus

#endif

// constexpr setup.
#if MPPP_CPLUSPLUS >= 201402L

#define MPPP_CONSTEXPR_14 constexpr

#else

#define MPPP_CONSTEXPR_14

#endif

// Detect if the compiler supports GCC-style 128-bit integers.
// NOTE: we can check int128 on GCC/clang with __SIZEOF_INT128__ apparently:
// http://stackoverflow.com/questions/21886985/what-gcc-versions-support-the-int128-intrinsic-type
// NOTE: clang-cl supports the types, but not all the arithmetic operations on them. Let's disable it for now.
#if ((defined(__clang__) && !defined(_MSC_VER)) || defined(__GNUC__) || defined(__INTEL_COMPILER))                     \
    && defined(__SIZEOF_INT128__)

#define MPPP_HAVE_GCC_INT128

#endif

#endif
