/*
Copyright 2010-2011, D. E. Shaw Research.
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:

* Redistributions of source code must retain the above copyright
  notice, this list of conditions, and the following disclaimer.

* Redistributions in binary form must reproduce the above copyright
  notice, this list of conditions, and the following disclaimer in the
  documentation and/or other materials provided with the distribution.

* Neither the name of D. E. Shaw Research nor the names of its
  contributors may be used to endorse or promote products derived from
  this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/**

@page porting Preprocessor symbols for porting Random123 to different platforms.

The Random123 library is portable across C, C++, CUDA, OpenCL environments,
and multiple operating systems (Linux, Windows 7, Mac OS X, FreeBSD, Solaris).
This level of portability requires the abstraction of some features
and idioms that are either not standardized (e.g., asm statments), or for which 
different vendors have their own standards (e.g., SSE intrinsics) or for
which vendors simply refuse to conform to well-established standards (e.g., <inttypes.h>).

Random123/features/compilerfeatures.h
conditionally includes a compiler-or-OS-specific Random123/featires/XXXfeatures.h file which
defines appropriate values for the preprocessor symbols which can be used with
a specific compiler or OS.  Those symbols will then
be used by other header files and source files in the Random123
library (and may be used by applications) to control what actually
gets presented to the compiler.

Most of the symbols are boolean valued.  In general, they will
\b always be defined with value either 1 or 0, so do
\b NOT use \#ifdef.  Use \#if R123_USE_SOMETHING instead.

Library users can override any value by defining the pp-symbol with a compiler option,
e.g.,

    cc -DR123_USE_MULHILO64_C99 

will use a strictly c99 version of the full-width 64x64->128-bit multiplication
function, even if it would be disabled by default.

All boolean-valued pre-processor symbols in Random123/features/compilerfeatures.h start with the prefix R123_USE_
@verbatim
         AES_NI
         AES_OPENSSL
         SSE4_2
         SSE4_1
         SSE

         STD_RANDOM

         GNU_UINT128
         ASM_GNU
         ASM_MSASM

         CPUID_MSVC

         CXX11_RANDOM
         CXX11_TYPE_TRAITS
         CXX11_STATIC_ASSERT
         CXX11_CONSTEXPR
         CXX11_UNRESTRICTED_UNIONS
         CXX11_EXPLICIT_CONVERSIONS
         CXX11_LONG_LONG
         CXX11_STD_ARRAY
         CXX11 
   
         X86INTRIN_H
         IA32INTRIN_H
         XMMINTRIN_H
         EMMINTRIN_H
         SMMINTRIN_H
         WMMINTRIN_H
         INTRIN_H

         MULHILO32_ASM
         MULHILO64_ASM
         MULHILO64_MSVC_INTRIN
         MULHILO64_CUDA_INTRIN
         MULHILO64_OPENCL_INTRIN
         MULHILO64_C99

         U01_DOUBLE
	 
@endverbatim
Most have obvious meanings.  Some non-obvious ones:

AES_NI and AES_OPENSSL are not mutually exclusive.  You can have one,
both or neither.

GNU_UINT128 says that it's safe to use __uint128_t, but it
does not require its use.  In particular, it should be
used in mulhilo<uint64_t> only if MULHILO64_ASM is unset.

If the XXXINTRIN_H macros are true, then one should
@code
#include <xxxintrin.h>
@endcode
to gain accesss to compiler intrinsics.

The CXX11_SOME_FEATURE macros allow the code to use specific
features of the C++11 language and library.  The catchall
In the absence of a specific CXX11_SOME_FEATURE, the feature
is controlled by the catch-all R123_USE_CXX11 macro.

U01_DOUBLE defaults on, and can be turned off (set to 0)
if one does not want the utility functions that convert to double
(i.e. u01_*_53()), e.g. on OpenCL without the cl_khr_fp64 extension.

There are a number of invariants that are always true.  Application code may
choose to rely on these:

<ul>
<li>ASM_GNU and ASM_MASM are mutually exclusive
<li>The "higher" SSE values imply the lower ones.
</ul>

There are also non-boolean valued symbols:

<ul>
<li>R123_STATIC_INLINE -
  According to both C99 and GNU99, the 'static inline' declaration allows
  the compiler to not emit code if the function is not used.  
  Note that the semantics of 'inline', 'static' and 'extern' in
  gcc have changed over time and are subject to modification by
  command line options, e.g., -std=gnu89, -fgnu-inline.
  Nevertheless, it appears that the meaning of 'static inline' 
  has not changed over time and (with a little luck) the use of 'static inline'
  here will be portable between versions of gcc and to other C99
  compilers.
  See: http://gcc.gnu.org/onlinedocs/gcc/Inline.html
       http://www.greenend.org.uk/rjk/2003/03/inline.html

<li>R123_FORCE_INLINE(decl) -
  which expands to 'decl', adorned with the compiler-specific
  embellishments to strongly encourage that the declared function be
  inlined.  If there is no such compiler-specific magic, it should
  expand to decl, unadorned.
   
<li>R123_CUDA_DEVICE - which expands to __device__ (or something else with
  sufficiently similar semantics) when CUDA is in use, and expands
  to nothing in other cases.

<li>R123_METAL_THREAD_ADDRESS_SPACE - which expands to 'thread' (or
  something else with sufficiently similar semantics) when compiling a
  Metal kernel, and expands to nothing in other cases.

<li>R123_ASSERT(x) - which expands to assert(x), or maybe to nothing at
  all if we're in an environment so feature-poor that you can't even
  call assert (I'm looking at you, CUDA and OpenCL), or even include
  assert.h safely (OpenCL).

<li>R123_STATIC_ASSERT(expr,msg) - which expands to
  static_assert(expr,msg), or to an expression that
  will raise a compile-time exception if expr is not true.

<li>R123_ULONG_LONG - which expands to a declaration of the longest available
  unsigned integer.

<li>R123_64BIT(x) - expands to something equivalent to
  UINT64_C(x) from <stdint.h>, even in environments where <stdint.h>
  is not available, e.g., MSVC and OpenCL.

<li>R123_BUILTIN_EXPECT(expr,likely_value) - expands to something with
  the semantics of gcc's __builtin_expect(expr,likely_value).  If
  the environment has nothing like __builtin_expect, it should expand
  to just expr.
</ul>


\cond HIDDEN_FROM_DOXYGEN
*/

/* 
N.B.  When something is added to the list of features, it should be
added to each of the *features.h files, AND to examples/ut_features.cpp.
*/

/* N.B.  most other compilers (icc, nvcc, open64, llvm) will also define __GNUC__, so order matters. */
#if defined(__METAL_MACOS__)
#include "metalfeatures.h"
#elif defined(__OPENCL_VERSION__) && __OPENCL_VERSION__ > 0
#include "openclfeatures.h"
#elif defined(__CUDACC__)
#include "nvccfeatures.h"
#elif defined(__ICC)
#include "iccfeatures.h"
#elif defined(__xlC__) || defined(__ibmxl__)
#include "xlcfeatures.h"
#elif defined(__SUNPRO_C) || defined(__SUNPRO_CC)
#include "sunprofeatures.h"
#elif defined(__OPEN64__)
#include "open64features.h"
#elif defined(__clang__)
#include "clangfeatures.h"
#elif defined(__GNUC__)
#include "gccfeatures.h"
#elif defined(__PGI)
#include "pgccfeatures.h"
#elif defined(_MSC_FULL_VER)
#include "msvcfeatures.h"
#else
#error "Can't identify compiler.  You'll need to add a new xxfeatures.hpp"
{ /* maybe an unbalanced brace will terminate the compilation */
#endif

#ifndef R123_USE_CXX11
#define R123_USE_CXX11 (__cplusplus >= 201103L)
#endif

#ifndef R123_USE_CXX11_UNRESTRICTED_UNIONS
#define R123_USE_CXX11_UNRESTRICTED_UNIONS R123_USE_CXX11
#endif

#ifndef R123_USE_CXX11_STATIC_ASSERT
#define R123_USE_CXX11_STATIC_ASSERT R123_USE_CXX11
#endif

#ifndef R123_USE_CXX11_CONSTEXPR
#define R123_USE_CXX11_CONSTEXPR R123_USE_CXX11
#endif

#ifndef R123_USE_CXX11_EXPLICIT_CONVERSIONS
#define R123_USE_CXX11_EXPLICIT_CONVERSIONS R123_USE_CXX11
#endif

#ifndef R123_USE_CXX11_RANDOM
#define R123_USE_CXX11_RANDOM R123_USE_CXX11
#endif

#ifndef R123_USE_CXX11_TYPE_TRAITS
#define R123_USE_CXX11_TYPE_TRAITS R123_USE_CXX11
#endif

#ifndef R123_USE_CXX11_LONG_LONG
#define R123_USE_CXX11_LONG_LONG R123_USE_CXX11
#endif

#ifndef R123_USE_CXX11_STD_ARRAY
#define R123_USE_CXX11_STD_ARRAY R123_USE_CXX11
#endif

#ifndef R123_USE_MULHILO64_C99
#define R123_USE_MULHILO64_C99 0
#endif

#ifndef R123_USE_MULHILO64_MULHI_INTRIN
#define R123_USE_MULHILO64_MULHI_INTRIN 0
#endif

#ifndef R123_USE_MULHILO32_MULHI_INTRIN
#define R123_USE_MULHILO32_MULHI_INTRIN 0
#endif

#ifndef R123_STATIC_ASSERT
#if R123_USE_CXX11_STATIC_ASSERT
#define R123_STATIC_ASSERT(expr, msg) static_assert(expr, msg)
#else
    /* if msg always_looked_like_this, we could paste it into the name.  Worth it? */
#define R123_STATIC_ASSERT(expr, msg) typedef char static_assertion[(!!(expr))*2-1]
#endif
#endif

#ifndef R123_CONSTEXPR
#if R123_USE_CXX11_CONSTEXPR
#define R123_CONSTEXPR constexpr
#else
#define R123_CONSTEXPR
#endif
#endif

#ifndef R123_USE_64BIT
#define R123_USE_64BIT 1
#endif    

#ifndef R123_USE_PHILOX_64BIT
#define R123_USE_PHILOX_64BIT (R123_USE_64BIT && (R123_USE_MULHILO64_ASM || R123_USE_MULHILO64_MSVC_INTRIN || R123_USE_MULHILO64_CUDA_INTRIN || R123_USE_GNU_UINT128 || R123_USE_MULHILO64_C99 || R123_USE_MULHILO64_OPENCL_INTRIN || R123_USE_MULHILO64_MULHI_INTRIN))
#endif

#ifndef R123_ULONG_LONG
#if defined(__cplusplus) && !R123_USE_CXX11_LONG_LONG
/* C++98 doesn't have long long.  It doesn't have uint64_t either, but
   we will have typedef'ed uint64_t to something in the xxxfeatures.h.
   With luck, it won't elicit complaints from -pedantic.  Cross your
   fingers... */
#define R123_ULONG_LONG uint64_t
#else
#define R123_ULONG_LONG unsigned long long
#endif
#endif

/* UINT64_C should have been #defined by XXXfeatures.h, either by
   #include <stdint.h> or through compiler-dependent hacks */
#ifndef R123_64BIT
#define R123_64BIT(x) UINT64_C(x)
#endif

#ifndef R123_THROW
#define R123_THROW(x)    throw (x)
#endif

#ifndef R123_METAL_THREAD_ADDRESS_SPACE
#define R123_METAL_THREAD_ADDRESS_SPACE
#endif

#ifndef R123_METAL_CONSTANT_ADDRESS_SPACE
#define R123_METAL_CONSTANT_ADDRESS_SPACE
#endif
    
/*
 * Windows.h (and perhaps other "well-meaning" code define min and
 * max, so there's a high chance that our definition of min, max
 * methods or use of std::numeric_limits min and max will cause
 * complaints in any program that happened to include Windows.h or
 * suchlike first.  We use the null macro below in our own header
 * files definition or use of min, max to defensively preclude
 * this problem.  It may not be enough; one might need to #define
 * NOMINMAX before including Windows.h or compile with -DNOMINMAX.
 */
#define R123_NO_MACRO_SUBST

/** \endcond */
