
#include <NTL/ctools.h>
#include <cstdlib>
#include <cmath>



//============== define _ntl_GetWallTime() ===============

// Some of the logic here, especially the Mac stuff,
// is adapted from code obtained from here:

   /*
    * Author:  David Robert Nadeau
    * Site:    http://NadeauSoftware.com/
    * License: Creative Commons Attribution 3.0 Unported License
    *          http://creativecommons.org/licenses/by/3.0/deed.en_US
    */


#if defined(NTL_HAVE_MACOS_TIME)

// Mac OS
// NOTE: new versions of Mac OS support clock_gettime.
// However, we don't use it here as it might lead to inconsistencies
// when binaries get compiled on a later OS and used on an earlier OS.

#include <mach/mach.h>
#include <mach/mach_time.h>

static inline double InitTimeConvert()
{
   mach_timebase_info_data_t timeBase;
   (void)mach_timebase_info( &timeBase );
   return (double)timeBase.numer / (double)timeBase.denom / 1000000000.0;
}

double _ntl_GetWallTime( )
{
   static double timeConvert = InitTimeConvert();
   // even in a multi-threaded environment, this will
   // be safely initialized, according to C++11 standard

   return double(mach_absolute_time()) * timeConvert;
}

#elif defined(NTL_HAVE_POSIX_TIME)
// POSIX clock_gettime()

#include <unistd.h>
#include <ctime>  

#if defined(CLOCK_MONOTONIC)
#define Clk_ID CLOCK_MONOTONIC
#elif defined(CLOCK_REALTIME)
#define Clk_ID CLOCK_REALTIME
#elif defined(CLOCK_HIGHRES)
#define Clk_ID CLOCK_HIGHRES
#endif


double _ntl_GetWallTime( )
{
   using namespace std;
   timespec ts;
   if (clock_gettime(Clk_ID, &ts))
      return -1;
   else
      return double(ts.tv_sec) + double(ts.tv_nsec) / 1000000000.0;
}

#elif defined(NTL_HAVE_CHRONO_TIME)
// C++11 
// I have more faith in mach_absolute_time and clock_gettime
// than this...

#include <chrono>

double _ntl_GetWallTime( )
{
   auto current_time = std::chrono::steady_clock::now();
   auto duration_in_seconds = std::chrono::duration<double>(current_time.time_since_epoch());
   return duration_in_seconds.count();
}

#else
// Fall back...
// This will be the implementation on Windows...
// and in this case, both GetTime() and GetWallTime() return 
// a "wall clock" time.

double _ntl_GetTime();

double _ntl_GetWallTime( )
{
   return _ntl_GetTime();
}


#endif


//==============  END define _ntl_GetWallTime() ===============


/*
 * An IEEE double x is finite if and only if x - x == 0.
 * The function _ntl_IsFinite implements this logic;  however,
 * it does not completely trust that an optimizing compiler
 * really implements this correctly, and so it goes out of its way to
 * confuse the compiler.  For a good compiler that respects IEEE floating
 * point arithmetic, this may not be necessary, but it is better
 * to be a bit paranoid.
 *
 * Like the routine _ntl_ForceToMem below, this routine has the
 * side effect of forcing its argument into memory.
 *
 * I've checked the assembly code generated by various
 * versions of GCC, ICC, and MSVC++, and it all looks good.
 */


long _ntl_IsFinite(double *p)
{
   volatile double x = *p;
   *p = x;

   double y = x;
   double diff = y - x;
   return diff == 0.0;
}


/*
 * On machines with wide floating point registers, the routine _ntl_ForceToMem
 * is used to force a floating point double to a memory location.
 * I've checked with GCC, and even with LTO, this will work.
 * That said, I wouln't really recommend applying LTO to NTL...
 */

void _ntl_ForceToMem(double *p)
{
   volatile double x = *p;
   *p = x;
}




/*
 * The routine _ntl_ldexp(x, e) is like the standard ldexp(x, e) routine,
 * except that it takes a long exponent e, rather than an int exponenet.
 * Some care is taken to ensure reasonable overflow/undeflow behavior.
 * If the value of e does not fit into an int, then the result
 * is x*infinity or x*0, as appropriate.
 * Of course, this can only happen on platforms where long is wider
 * than int (e.g., most 64-bit platforms).
 *
 * We go out of our way to hide the fact that we are multiplying/dividing
 * by zero, so as to avoid unnecessary warnings, and to prevent 
 * overly-agressive optimizing compilers from screwing things up.
 */

volatile double _ntl_ldexp_zero = 0.0;

double _ntl_ldexp(double x, long e)
{
   if (x == 0.0) return x;

   if (e > NTL_MAX_INT)
      return x/_ntl_ldexp_zero;
   else if (e < NTL_MIN_INT)
      return x*_ntl_ldexp_zero;
   else
      return std::ldexp(x, ((int) e));
}

