rewrite time integer support for 32bit systems.

This commit is contained in:
Mathieu Lacage
2010-07-07 16:56:35 +02:00
parent 0b5fd59ac5
commit 3f330b7a01
8 changed files with 700 additions and 919 deletions

View File

@@ -19,17 +19,41 @@
*/
#include "high-precision-cairo.h"
#include "ns3/test.h"
#include "ns3/fatal-error.h"
#include "ns3/abort.h"
#include "ns3/log.h"
#include "ns3/assert.h"
#include <math.h>
#include <iostream>
namespace ns3 {
NS_LOG_COMPONENT_DEFINE ("HighPrecisionCairo");
#define ABS_ARGS(sa,sb,ua,ub) \
bool negResult, negA, negB; \
/* take the sign of the operands */ \
negA = _cairo_int128_negative (sa); \
negB = _cairo_int128_negative (sb); \
/* the result is negative only if one of the operand is negative */ \
negResult = (negA && !negB) || (!negA && negB); \
/* now take the absolute part to make sure that the resulting operands are positive */ \
ua = _cairo_int128_to_uint128 (sa); \
ub = _cairo_int128_to_uint128 (sb); \
ua = negA ? _cairo_uint128_negate (ua) : ua; \
ub = negB ? _cairo_uint128_negate (ub) : ub
#define SIGN_RESULT(result) \
negResult ? _cairo_uint128_negate (result) : result
void
HighPrecision::Mul (HighPrecision const &o)
{
// use the 128 bits multiplication
m_value = Mul128 (m_value,o.m_value);
cairo_uint128_t a, b, result;
ABS_ARGS (m_value, o.m_value, a, b);
result = Umul (a, b);
m_value = SIGN_RESULT (result);
}
@@ -39,22 +63,9 @@ HighPrecision::Mul (HighPrecision const &o)
* as the fractional part. It takes into account the sign
* of the operands to produce a signed 128 bits result.
*/
cairo_int128_t
HighPrecision::Mul128 (cairo_int128_t sa, cairo_int128_t sb ) const
cairo_uint128_t
HighPrecision::Umul (cairo_uint128_t a, cairo_uint128_t b)
{
bool negResult, negA, negB;
negA = _cairo_int128_negative (sa);
negB = _cairo_int128_negative (sb);
// the result is negative only if one of the operand is negative
negResult = (negA && !negB) || (!negA && negB);
// now take the absolute part to make sure that the resulting operands are positive
cairo_uint128_t a, b;
a = _cairo_int128_to_uint128 (sa);
b = _cairo_int128_to_uint128 (sb);
a = negA ? _cairo_uint128_negate (a) : a;
b = negB ? _cairo_uint128_negate (b) : b;
cairo_uint128_t result;
cairo_uint128_t hiPart,loPart,midPart;
@@ -73,38 +84,23 @@ HighPrecision::Mul128 (cairo_int128_t sa, cairo_int128_t sb ) const
// truncate the high part and only use the low part
result.hi = _cairo_uint64_add (hiPart.lo,midPart.hi);
// if the high part is not zero, put a warning
if (hiPart.hi != 0)
{
NS_FATAL_ERROR ("High precision 128 bits multiplication error: multiplication overflow.");
}
// add the sign to the result
result = negResult ? _cairo_uint128_negate (result) : result;
return _cairo_uint128_to_int128 (result);
NS_ABORT_MSG_IF (hiPart.hi != 0,
"High precision 128 bits multiplication error: multiplication overflow.");
return result;
}
void
HighPrecision::Div (HighPrecision const &o)
{
cairo_int128_t result = Div128 (m_value, o.m_value);
m_value = result;
cairo_uint128_t a, b, result;
ABS_ARGS(m_value, o.m_value, a, b);
result = Udiv (a, b);
m_value = SIGN_RESULT (result);
}
cairo_int128_t
HighPrecision::Div128 (cairo_int128_t sa, cairo_int128_t sb) const
cairo_uint128_t
HighPrecision::Udiv (cairo_uint128_t a, cairo_uint128_t b)
{
bool negResult, negA, negB;
// take the sign of the operands
negA = _cairo_int128_negative (sa);
negB = _cairo_int128_negative (sb);
// the result is negative only if one of the operand is negative
negResult = (negA && !negB) || (!negA && negB);
// now take the absolute part to make sure that the resulting operands are positive
cairo_uint128_t a, b;
a = _cairo_int128_to_uint128 (sa);
b = _cairo_int128_to_uint128 (sb);
a = negA ? _cairo_uint128_negate (a) : a;
b = negB ? _cairo_uint128_negate (b) : b;
cairo_uquorem128_t qr = _cairo_uint128_divrem (a, b);
cairo_uint128_t result = _cairo_uint128_lsl (qr.quo, 64);
// Now, manage the remainder
@@ -123,9 +119,95 @@ HighPrecision::Div128 (cairo_int128_t sa, cairo_int128_t sb) const
}
qr = _cairo_uint128_divrem (rem, div);
result = _cairo_uint128_add (result, qr.quo);
result = negResult ? _cairo_uint128_negate (result) : result;
return _cairo_uint128_to_int128 (result);
return result;
}
void
HighPrecision::MulFactor (uint64_t factor)
{
if (m_value.hi == 0 && m_value.lo == 0)
{
return;
}
// 1000 = 2^10 - 2^4 - 2^3
#if 0
// 1000000 = 2^20 - 2^16 + 2^14 + 2^9 + 2^6
uint8_t powers [] = {20, 16, 14, 9, 6};
int8_t signs [] = {1, -1, 1, 1, 1};
#else
// 1000000000 = 2^30 - 2^26 - 2^23 + 2^21 - 2^18 - 2^16 - 2^14 + 2^11 + 2^9
uint8_t powers [] = {9, 11, 14, 16, 18, 21, 23, 26, 30};
int8_t signs [] = {1, 1, -1, -1, -1, 1, -1, -1, 1};
#endif
cairo_uint128_t result;
result.hi = 0;
result.lo = 0;
for (uint8_t i = 0; i < sizeof (powers); i++)
{
uint8_t shift = powers[i];
cairo_uint128_t tmp;
tmp.hi = (m_value.hi << shift) + (m_value.lo >> (64-shift));
tmp.lo = m_value.lo << shift;
if (signs[i] < 0)
{
result = Sub (result, tmp);
}
else
{
result = Add (result, tmp);
}
}
m_value = result;
}
void
HighPrecision::MulByInvert (const HighPrecision &o)
{
bool negResult = _cairo_int128_negative (m_value);
cairo_uint128_t a = negResult?_cairo_int128_negate(m_value):m_value;
cairo_uint128_t result = UmulByInvert (a, o.m_value);
m_value = negResult?_cairo_int128_negate(result):result;
}
cairo_uint128_t
HighPrecision::UmulByInvert (cairo_uint128_t a, cairo_uint128_t b)
{
cairo_uint128_t result;
cairo_uint128_t hi, mid;
hi = _cairo_uint64x64_128_mul (a.hi, b.hi);
mid = _cairo_uint128_add (_cairo_uint64x64_128_mul (a.hi, b.lo),
_cairo_uint64x64_128_mul (a.lo, b.hi));
mid.lo = mid.hi;
mid.hi = 0;
result = _cairo_uint128_add (hi,mid);
return result;
}
HighPrecision
HighPrecision::Invert (uint64_t v)
{
NS_ASSERT (v > 1);
cairo_uint128_t a, factor;
a.hi = 1;
a.lo = 0;
factor.hi = 0;
factor.lo = v;
HighPrecision result;
result.m_value = Udiv (a, factor);
HighPrecision tmp = HighPrecision (v, false);
tmp.MulByInvert (result);
if (tmp.GetInteger () != 1)
{
cairo_uint128_t one = {1, 0};
result.m_value = _cairo_uint128_add (result.m_value, one);
}
return result;
}
} // namespace ns3
// include directly to allow optimizations within the compilation unit.
extern "C" {
#include "cairo-wideint.c"
}

View File

@@ -37,37 +37,6 @@
* Division operations (there are really really super costly)
* and Comparison operations (because there are typically a lot of
* these in any complex timekeeping code).
*
* So, the code tries really hard to perform any of these 128 bit
* operations by doing all arithmetic on 64 bit integers when possible
* (i.e., when there is no fractional part. This is a very common case).
* Hence, the following code has a m_fastValue (64 bits) and a
* m_slowValue (128 bits). m_fastValue is used by default and the code
* converts it to a m_slowValue when needed.
*
* If you want to monitor the efficiency of this strategy, you can
* enable the macro HP128INC below and call the HighPrecision::PrintStats
* method at the end of the simulation.
*
* Explanation of Slow and Fast values:
*
* HighPrecision class create a fastValue and a slowValue depending on the
* input number. If the input is an integer with 0 fractional part, it will
* use the fastValue which will contain the integer in a 64 bits format. If
* it has a fractional part, the slowValue will be used. It is represented
* simply as a high part slowValue.hi which will contain the integer part
* and the fractional part slowValue.lo which will contain the factional
* part as an integer (obtained by multiplying the fractional part by 2^64).
*
* Explanation of Slow and Fast operations:
*
* If both operands are fastValues, we will perform fast operations, i-e
* simply using integer operations. If we have though one of the value is
* slowValue we need to convert the fastValue into a slow one. It is simply
* obtained by putting the slowValue.lo = 0 and slowValue.hi = fastValue.
* After that we apply the slow operation which will be a 128 bits operation
* with two 128 bits operands.
*
*/
namespace ns3 {
@@ -77,7 +46,7 @@ class HighPrecision
public:
inline HighPrecision ();
inline HighPrecision (int64_t value, bool dummy);
inline HighPrecision (double value);
explicit inline HighPrecision (double value);
inline int64_t GetInteger (void) const;
inline double GetDouble (void) const;
@@ -85,12 +54,20 @@ public:
inline void Sub (HighPrecision const &o);
void Mul (HighPrecision const &o);
void Div (HighPrecision const &o);
void MulByInvert (const HighPrecision &o);
static HighPrecision Invert (uint64_t v);
void MulFactor (uint64_t factor);
inline int Compare (HighPrecision const &o) const;
inline static HighPrecision Zero (void);
private:
cairo_uint128_t Mul128 (cairo_uint128_t, cairo_uint128_t ) const;
cairo_int128_t Div128 (cairo_int128_t sa, cairo_int128_t sb) const;
static inline cairo_uint128_t Add (cairo_uint128_t a, cairo_uint128_t b);
static inline cairo_uint128_t Sub (cairo_uint128_t a, cairo_uint128_t b);
static cairo_uint128_t Umul (cairo_uint128_t a, cairo_uint128_t b);
static cairo_uint128_t Udiv (cairo_uint128_t a, cairo_uint128_t b);
static cairo_uint128_t UmulByInvert (cairo_uint128_t a, cairo_uint128_t b);
inline bool IsNegative (void) const;
cairo_int128_t m_value;
@@ -124,6 +101,31 @@ HighPrecision::GetInteger (void) const
{
return m_value.hi;
}
cairo_uint128_t
HighPrecision::Add (cairo_uint128_t a, cairo_uint128_t b)
{
cairo_uint128_t result;
result.hi = a.hi + b.hi;
result.lo = a.lo + b.lo;
if (result.lo < a.lo)
{
result.hi++;
}
return result;
}
cairo_uint128_t
HighPrecision::Sub (cairo_uint128_t a, cairo_uint128_t b)
{
cairo_uint128_t result;
result.hi = a.hi - b.hi;
result.lo = a.lo - b.lo;
if (result.lo > a.lo)
{
result.hi--;
}
return result;
}
void
HighPrecision::Add (HighPrecision const &o)
{

View File

@@ -282,6 +282,68 @@ Hp128CompareTestCase::DoRun (void)
return false;
}
class Hp128InvertTestCase : public TestCase
{
public:
Hp128InvertTestCase ();
virtual bool DoRun (void);
};
Hp128InvertTestCase::Hp128InvertTestCase ()
: TestCase ("Test case for invertion")
{
}
#define TEST(factor) \
do { \
HighPrecision a; \
a = HighPrecision::Invert (factor); \
HighPrecision b = V (factor); \
b.MulByInvert (a); \
NS_TEST_ASSERT_MSG_EQ (b.GetInteger (), 1, \
"x * 1/x should be 1 for x=" << factor); \
HighPrecision c = V (1); \
c.MulByInvert (a); \
NS_TEST_ASSERT_MSG_EQ (c.GetInteger (), 0, \
"1 * 1/x should be 0 for x=" << factor); \
HighPrecision d = V (1); \
d.Div (V(factor)); \
NS_TEST_ASSERT_MSG_EQ (d.GetDouble (), c.GetDouble (), \
"1 * 1/x should be equal to 1/x for x=" << factor); \
HighPrecision e = V (-factor); \
e.MulByInvert (a); \
NS_TEST_ASSERT_MSG_EQ (e.GetInteger (), -1, \
"-x * 1/x should be -1 for x=" << factor); \
} while(false)
bool
Hp128InvertTestCase::DoRun (void)
{
TEST(2);
TEST(3);
TEST(4);
TEST(5);
TEST(6);
TEST(10);
TEST(99);
TEST(100);
TEST(1000);
TEST(10000);
TEST(100000);
TEST(100000);
TEST(1000000);
TEST(10000000);
TEST(100000000);
TEST(1000000000);
TEST(10000000000LL);
TEST(100000000000LL);
TEST(1000000000000LL);
TEST(10000000000000LL);
TEST(100000000000000LL);
TEST(1000000000000000LL);
return false;
}
static class HighPrecision128TestSuite : public TestSuite
{
public:
@@ -292,6 +354,7 @@ public:
AddTestCase (new Hp128Bug455TestCase ());
AddTestCase (new Hp128Bug863TestCase ());
AddTestCase (new Hp128CompareTestCase ());
AddTestCase (new Hp128InvertTestCase ());
}
} g_highPrecision128TestSuite;

View File

@@ -27,37 +27,10 @@
#include <math.h>
#include <ostream>
#include "high-precision.h"
#include "time-base.h"
namespace ns3 {
namespace TimeStepPrecision {
enum precision_t
{
S = 0,
MS = 3,
US = 6,
NS = 9,
PS = 12,
FS = 15
};
/**
* \param precision the new precision to use
*
* This should be invoked before any Time object
* is created. i.e., it should be invoked at the very start
* of every simulation. The unit specified by this method
* is used as the unit of the internal simulation time
* which is stored as a 64 bit integer.
*/
void Set (precision_t precision);
/**
* \returns the currently-used time precision.
*/
precision_t Get (void);
} // namespace TimeStepPrecision
/**
* \ingroup simulator
@@ -110,115 +83,14 @@ precision_t Get (void);
* - \ref ns3-Time-Min ns3::Min
*/
template <int N>
class TimeUnit
class TimeUnit : public TimeBase
{
public:
TimeUnit ();
TimeUnit (TimeUnit const &o);
TimeUnit operator = (TimeUnit const &o);
TimeUnit (HighPrecision data);
/**
* \return true if the time is zero, false otherwise.
*/
bool IsZero (void) const;
/**
* \return true if the time is negative or zero, false otherwise.
*/
bool IsNegative (void) const;
/**
* \return true if the time is positive or zero, false otherwise.
*/
bool IsPositive (void) const;
/**
* \return true if the time is strictly negative, false otherwise.
*/
bool IsStrictlyNegative (void) const;
/**
* \return true if the time is strictly positive, false otherwise.
*/
bool IsStrictlyPositive (void) const;
/**
* This is really an internal method exported for the needs of
* the implementation. Please, Do not try to use this method, ever.
*
* \return the ns3::HighPrecision object which holds the value
* stored in this Time<N> type.
*/
HighPrecision const &GetHighPrecision (void) const;
HighPrecision * PeekHighPrecision (void);
private:
HighPrecision m_data;
explicit inline TimeUnit (const HighPrecision &data)
: TimeBase (data)
{}
};
template <int N>
TimeUnit<N>::TimeUnit ()
: m_data ()
{
}
template <int N>
TimeUnit<N>::TimeUnit (TimeUnit const &o)
: m_data (o.m_data)
{
}
template <int N>
TimeUnit<N>
TimeUnit<N>::operator = (TimeUnit const &o)
{
m_data = o.m_data;
return *this;
}
template <int N>
TimeUnit<N>::TimeUnit (HighPrecision data)
: m_data (data)
{
}
template <int N>
HighPrecision const &
TimeUnit<N>::GetHighPrecision (void) const
{
return m_data;
}
template <int N>
HighPrecision *
TimeUnit<N>::PeekHighPrecision (void)
{
return &m_data;
}
template <int N>
bool
TimeUnit<N>::IsZero (void) const
{
return m_data.Compare (HighPrecision::Zero ()) == 0;
}
template <int N>
bool
TimeUnit<N>::IsNegative (void) const
{
return m_data.Compare (HighPrecision::Zero ()) <= 0;
}
template <int N>
bool
TimeUnit<N>::IsPositive (void) const
{
return m_data.Compare (HighPrecision::Zero ()) >= 0;
}
template <int N>
bool
TimeUnit<N>::IsStrictlyNegative (void) const
{
return m_data.Compare (HighPrecision::Zero ()) < 0;
}
template <int N>
bool
TimeUnit<N>::IsStrictlyPositive (void) const
{
return m_data.Compare (HighPrecision::Zero ()) > 0;
}
template <int N>
bool
operator == (TimeUnit<N> const &lhs, TimeUnit<N> const &rhs)
@@ -347,7 +219,7 @@ TimeUnit<N> Min (TimeUnit<N> const &ta, TimeUnit<N> const &tb)
class TimeValue;
template <>
class TimeUnit<1>
class TimeUnit<1> : public TimeBase
{
// -*- New methods -*-
public:
@@ -368,103 +240,71 @@ public:
* \param s The string to parse into a TimeUnit<1>
*/
TimeUnit<1> (const std::string & s);
/**
* \returns an approximation in seconds of the time stored in this
* instance.
*/
double GetSeconds (void) const;
inline double GetSeconds (void) const
{
return TimeBase::ToDouble (*this, TimeBase::S);
}
/**
* \returns an approximation in milliseconds of the time stored in this
* instance.
*/
int64_t GetMilliSeconds (void) const;
inline int64_t GetMilliSeconds (void) const
{
return TimeBase::ToInteger (*this, TimeBase::MS);
}
/**
* \returns an approximation in microseconds of the time stored in this
* instance.
*/
int64_t GetMicroSeconds (void) const;
inline int64_t GetMicroSeconds (void) const
{
return TimeBase::ToInteger (*this, TimeBase::US);
}
/**
* \returns an approximation in nanoseconds of the time stored in this
* instance.
*/
int64_t GetNanoSeconds (void) const;
inline int64_t GetNanoSeconds (void) const
{
return TimeBase::ToInteger (*this, TimeBase::NS);
}
/**
* \returns an approximation in picoseconds of the time stored in this
* instance.
*/
int64_t GetPicoSeconds (void) const;
inline int64_t GetPicoSeconds (void) const
{
return TimeBase::ToInteger (*this, TimeBase::PS);
}
/**
* \returns an approximation in femtoseconds of the time stored in this
* instance.
*/
int64_t GetFemtoSeconds (void) const;
inline int64_t GetFemtoSeconds (void) const
{
return TimeBase::ToInteger (*this, TimeBase::FS);
}
/**
* \returns an approximation of the time stored in this
* instance in the units specified in m_tsPrecision.
*/
int64_t GetTimeStep (void) const;
// -*- The rest is the the same as in the generic template class -*-
public:
TimeUnit ()
: m_data ()
inline int64_t GetTimeStep (void) const
{
int64_t timeValue = GetHighPrecision ().GetInteger ();
return timeValue;
}
TimeUnit (TimeUnit const &o)
: m_data (o.m_data)
{
}
TimeUnit operator = (TimeUnit const &o)
{
m_data = o.m_data;
return *this;
}
TimeUnit (HighPrecision data)
: m_data (data)
{
}
bool IsZero (void) const
{
return m_data.Compare (HighPrecision::Zero ()) == 0;
}
bool IsNegative (void) const
{
return m_data.Compare (HighPrecision::Zero ()) <= 0;
}
bool IsPositive (void) const
{
return m_data.Compare (HighPrecision::Zero ()) >= 0;
}
bool IsStrictlyNegative (void) const
{
return m_data.Compare (HighPrecision::Zero ()) < 0;
}
bool IsStrictlyPositive (void) const
{
return m_data.Compare (HighPrecision::Zero ()) > 0;
}
HighPrecision const &GetHighPrecision (void) const
{
return m_data;
}
HighPrecision * PeekHighPrecision (void)
{
return &m_data;
}
static uint64_t UnitsToTimestep (uint64_t unitValue,
uint64_t unitFactor);
private:
HighPrecision m_data;
/*
* \Returns the value of time_value in units of unitPrec. time_value
* must be specified in timestep units (which are the same as the
* m_tsPrecision units
*/
int64_t ConvertToUnits (int64_t timeValue, uint64_t unitFactor) const;
inline TimeUnit ()
: TimeBase () {}
inline TimeUnit (const TimeBase &o)
: TimeBase (o) {}
explicit inline TimeUnit<1> (const HighPrecision &o)
: TimeBase (o) {}
};
/**
@@ -551,7 +391,10 @@ std::istream& operator>> (std::istream& is, Time & time);
* \endcode
* \param seconds seconds value
*/
Time Seconds (double seconds);
inline Time Seconds (double seconds)
{
return TimeBase::FromDouble (seconds, TimeBase::S);
}
/**
* \brief create ns3::Time instances in units of milliseconds.
@@ -563,7 +406,10 @@ Time Seconds (double seconds);
* \endcode
* \param ms milliseconds value
*/
Time MilliSeconds (uint64_t ms);
inline Time MilliSeconds (uint64_t ms)
{
return TimeBase::FromInteger (ms, TimeBase::MS);
}
/**
* \brief create ns3::Time instances in units of microseconds.
*
@@ -574,7 +420,10 @@ Time MilliSeconds (uint64_t ms);
* \endcode
* \param us microseconds value
*/
Time MicroSeconds (uint64_t us);
inline Time MicroSeconds (uint64_t us)
{
return TimeBase::FromInteger (us, TimeBase::US);
}
/**
* \brief create ns3::Time instances in units of nanoseconds.
*
@@ -585,7 +434,10 @@ Time MicroSeconds (uint64_t us);
* \endcode
* \param ns nanoseconds value
*/
Time NanoSeconds (uint64_t ns);
inline Time NanoSeconds (uint64_t ns)
{
return TimeBase::FromInteger (ns, TimeBase::NS);
}
/**
* \brief create ns3::Time instances in units of picoseconds.
*
@@ -596,7 +448,10 @@ Time NanoSeconds (uint64_t ns);
* \endcode
* \param ps picoseconds value
*/
Time PicoSeconds (uint64_t ps);
inline Time PicoSeconds (uint64_t ps)
{
return TimeBase::FromInteger (ps, TimeBase::PS);
}
/**
* \brief create ns3::Time instances in units of femtoseconds.
*
@@ -607,71 +462,29 @@ Time PicoSeconds (uint64_t ps);
* \endcode
* \param fs femtoseconds value
*/
Time FemtoSeconds (uint64_t fs);
inline Time FemtoSeconds (uint64_t fs)
{
return TimeBase::FromInteger (fs, TimeBase::FS);
}
// internal function not publicly documented
Time TimeStep (uint64_t ts);
inline Time TimeStep (uint64_t ts)
{
return Time (HighPrecision (ts, false));
}
// Explicit instantiation of the TimeUnit template for N=0, with a few
// additional methods that should not be available for N != 0
template <>
class TimeUnit<0>
class TimeUnit<0> : public TimeBase
{
// -*- New methods -*-
public:
double GetDouble (void) const;
TimeUnit<0> (double scalar);
// -*- The rest is the the same as in the generic template class -*-
public:
TimeUnit ()
: m_data ()
{
}
TimeUnit (TimeUnit const &o)
: m_data (o.m_data)
{
}
TimeUnit operator = (TimeUnit const &o)
{
m_data = o.m_data;
return *this;
}
TimeUnit (HighPrecision data)
: m_data (data)
{
}
bool IsZero (void) const
{
return m_data.Compare (HighPrecision::Zero ()) == 0;
}
bool IsNegative (void) const
{
return m_data.Compare (HighPrecision::Zero ()) <= 0;
}
bool IsPositive (void) const
{
return m_data.Compare (HighPrecision::Zero ()) >= 0;
}
bool IsStrictlyNegative (void) const
{
return m_data.Compare (HighPrecision::Zero ()) < 0;
}
bool IsStrictlyPositive (void) const
{
return m_data.Compare (HighPrecision::Zero ()) > 0;
}
HighPrecision const &GetHighPrecision (void) const
{
return m_data;
}
HighPrecision * PeekHighPrecision (void)
{
return &m_data;
}
private:
HighPrecision m_data;
TimeUnit<0> ();
TimeUnit<0> (const TimeUnit &o);
explicit TimeUnit<0> (const HighPrecision &o);
};
/**

View File

@@ -0,0 +1,59 @@
#include "time-base.h"
#include "ns3/assert.h"
namespace ns3 {
struct TimeBase::Resolution
TimeBase::GetNsResolution (void)
{
struct Resolution resolution;
SetResolution (TimeBase::NS, &resolution);
return resolution;
}
void
TimeBase::SetResolution (enum Unit resolution)
{
SetResolution (resolution, PeekResolution ());
}
void
TimeBase::SetResolution (enum Unit unit, struct Resolution *resolution)
{
int8_t power [LAST] = {15, 12, 9, 6, 3, 0};
for (int i = 0; i < TimeBase::LAST; i++)
{
int shift = power[i] - power[(int)unit];
uint64_t factor = (uint64_t) pow (10, fabs (shift));
struct Information *info = &resolution->info[i];
info->factor = factor;
if (shift == 0)
{
info->timeFrom = HighPrecision (1, false);
info->timeTo = HighPrecision (1, false);
info->toMul = true;
info->fromMul = true;
}
else if (shift > 0)
{
info->timeFrom = HighPrecision (factor, false);
info->timeTo = HighPrecision::Invert (factor);
info->toMul = false;
info->fromMul = true;
}
else
{
NS_ASSERT (shift < 0);
info->timeFrom = HighPrecision::Invert (factor);
info->timeTo = HighPrecision (factor, false);
info->toMul = true;
info->fromMul = false;
}
}
resolution->unit = unit;
}
enum TimeBase::Unit
TimeBase::GetResolution (void)
{
return PeekResolution ()->unit;
}
} // namespace ns3

258
src/simulator/time-base.h Normal file
View File

@@ -0,0 +1,258 @@
/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
/*
* Copyright (c) 2005,2006 INRIA
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation;
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* Author: Mathieu Lacage <mathieu.lacage@sophia.inria.fr>
*/
#ifndef TIME_BASE_H
#define TIME_BASE_H
#include "high-precision.h"
namespace ns3 {
/**
* \ingroup time
* \brief keep track of global simulation resolution
*/
class TimeBase
{
public:
/**
* The unit to use to interpret a number representing time
*/
enum Unit
{
S = 0,
MS = 1,
US = 2,
NS = 3,
PS = 4,
FS = 5,
LAST = 6
};
static void SetResolution (enum Unit resolution);
static enum Unit GetResolution (void);
inline static TimeBase FromInteger (uint64_t value, enum Unit timeUnit);
inline static TimeBase FromDouble (double value, enum Unit timeUnit);
inline static uint64_t ToInteger (const TimeBase &time, enum Unit timeUnit);
inline static double ToDouble (const TimeBase &time, enum Unit timeUnit);
inline TimeBase ();
inline TimeBase (const TimeBase &o);
inline TimeBase operator = (const TimeBase &o);
inline TimeBase (const HighPrecision &data);
/**
* \return true if the time is zero, false otherwise.
*/
inline bool IsZero (void) const;
/**
* \return true if the time is negative or zero, false otherwise.
*/
inline bool IsNegative (void) const;
/**
* \return true if the time is positive or zero, false otherwise.
*/
inline bool IsPositive (void) const;
/**
* \return true if the time is strictly negative, false otherwise.
*/
inline bool IsStrictlyNegative (void) const;
/**
* \return true if the time is strictly positive, false otherwise.
*/
inline bool IsStrictlyPositive (void) const;
/**
* This is really an internal method exported for the needs of
* the implementation. Please, Do not try to use this method, ever.
*
* \return the ns3::HighPrecision object which holds the value
* stored in this instance of Time type.
*/
inline HighPrecision const &GetHighPrecision (void) const;
inline HighPrecision * PeekHighPrecision (void);
protected:
HighPrecision m_data;
private:
struct Information
{
bool toMul;
bool fromMul;
uint64_t factor;
HighPrecision timeTo;
HighPrecision timeFrom;
};
struct Resolution
{
struct Information info[LAST];
enum TimeBase::Unit unit;
};
inline static struct Resolution *PeekResolution (void);
inline static struct Information *PeekInformation (enum Unit timeUnit);
static struct Resolution GetNsResolution (void);
static void SetResolution (enum Unit unit, struct Resolution *resolution);
inline static TimeBase From (HighPrecision tmp, enum Unit timeUnit);
inline static HighPrecision To (const TimeBase &time, enum Unit timeUnit);
};
} // namespace ns3
namespace ns3 {
TimeBase::TimeBase ()
: m_data ()
{
}
TimeBase::TimeBase (const TimeBase &o)
: m_data (o.m_data)
{
}
TimeBase TimeBase::operator = (const TimeBase &o)
{
m_data = o.m_data;
return *this;
}
TimeBase::TimeBase (const HighPrecision &data)
: m_data (data)
{
}
HighPrecision const &
TimeBase::GetHighPrecision (void) const
{
return m_data;
}
HighPrecision *
TimeBase::PeekHighPrecision (void)
{
return &m_data;
}
bool
TimeBase::IsZero (void) const
{
return m_data.Compare (HighPrecision::Zero ()) == 0;
}
bool
TimeBase::IsNegative (void) const
{
return m_data.Compare (HighPrecision::Zero ()) <= 0;
}
bool
TimeBase::IsPositive (void) const
{
return m_data.Compare (HighPrecision::Zero ()) >= 0;
}
bool
TimeBase::IsStrictlyNegative (void) const
{
return m_data.Compare (HighPrecision::Zero ()) < 0;
}
bool
TimeBase::IsStrictlyPositive (void) const
{
return m_data.Compare (HighPrecision::Zero ()) > 0;
}
struct TimeBase::Resolution *
TimeBase::PeekResolution (void)
{
static struct TimeBase::Resolution resolution = GetNsResolution ();
return &resolution;
}
struct TimeBase::Information *
TimeBase::PeekInformation (enum Unit timeUnit)
{
return &(PeekResolution ()->info[timeUnit]);
}
TimeBase
TimeBase::From (HighPrecision tmp, enum Unit timeUnit)
{
struct Information *info = PeekInformation (timeUnit);
if (info->fromMul)
{
tmp.Mul (info->timeFrom);
}
else
{
tmp.MulByInvert (info->timeFrom);
}
return TimeBase (tmp);
}
HighPrecision
TimeBase::To (const TimeBase &time, enum Unit timeUnit)
{
struct Information *info = PeekInformation (timeUnit);
HighPrecision tmp = time.GetHighPrecision ();
if (info->toMul)
{
tmp.Mul (info->timeTo);
}
else
{
tmp.MulByInvert (info->timeTo);
}
return tmp;
}
TimeBase
TimeBase::FromInteger (uint64_t value, enum Unit timeUnit)
{
struct Information *info = PeekInformation (timeUnit);
if (info->fromMul)
{
value *= info->factor;
return TimeBase (HighPrecision (value, false));
}
return From (HighPrecision (value, false), timeUnit);
}
TimeBase
TimeBase::FromDouble (double value, enum Unit timeUnit)
{
return From (HighPrecision (value), timeUnit);
}
uint64_t
TimeBase::ToInteger (const TimeBase &time, enum Unit timeUnit)
{
struct Information *info = PeekInformation (timeUnit);
uint64_t v = time.m_data.GetInteger ();
if (info->toMul)
{
v *= info->factor;
}
else
{
v /= info->factor;
}
return v;
}
double
TimeBase::ToDouble (const TimeBase &time, enum Unit timeUnit)
{
return To (time, timeUnit).GetDouble ();
}
} // namespace ns3
#endif /* TIME_BASE_H */

View File

@@ -20,7 +20,7 @@
* TimeStep support by Emmanuelle Laprise <emmanuelle.laprise@bluekazoo.ca>
*/
#include "nstime.h"
#include "ns3/fatal-error.h"
#include "ns3/abort.h"
#include "ns3/global-value.h"
#include "ns3/enum.h"
#include "ns3/string.h"
@@ -30,43 +30,6 @@
namespace ns3 {
namespace TimeStepPrecision {
static const uint64_t MS_FACTOR = (uint64_t)1000;
static const uint64_t US_FACTOR = (uint64_t)1000000;
static const uint64_t NS_FACTOR = (uint64_t)1000000 * (uint64_t)1000;
static const uint64_t PS_FACTOR = (uint64_t)1000000 * (uint64_t)1000000;
static const uint64_t FS_FACTOR = (uint64_t)1000000 * (uint64_t)1000000 * (uint64_t)1000;
static uint64_t g_tsPrecFactor = NS_FACTOR;
static GlobalValue g_precisionDefaultValue ("TimeStepPrecision",
"The time unit of the internal 64 bit integer time.",
EnumValue (NS),
MakeEnumChecker (NS, "NS",
S, "S",
MS, "MS",
US, "US",
PS, "PS",
FS, "FS")
);
precision_t
Get (void)
{
EnumValue v;
g_precisionDefaultValue.GetValue (v);
return (precision_t) v.Get ();
}
void
Set (precision_t precision)
{
g_precisionDefaultValue.SetValue (EnumValue (precision));
g_tsPrecFactor = (uint64_t)pow (10, precision);
}
} // namespace TimeStepPrecision
TimeUnit<1>::TimeUnit (const std::string& s)
{
std::string::size_type n = s.find_first_not_of ("0123456789.");
@@ -79,40 +42,35 @@ TimeUnit<1>::TimeUnit (const std::string& s)
std::string trailer = s.substr (n, std::string::npos);
if (trailer == std::string ("s"))
{
m_data = HighPrecision (r * TimeStepPrecision::g_tsPrecFactor);
*this = Time::FromDouble (r, Time::S);
return;
}
if (trailer == std::string ("ms"))
{
m_data = HighPrecision ((int64_t)(r * (TimeStepPrecision::g_tsPrecFactor / pow (10,3))),
false);
*this = Time::FromDouble (r, Time::MS);
return;
}
if (trailer == std::string ("us"))
{
m_data = HighPrecision ((int64_t)(r * (TimeStepPrecision::g_tsPrecFactor / pow (10,6))),
false);
*this = Time::FromDouble (r, Time::US);
return;
}
if (trailer == std::string ("ns"))
{
m_data = HighPrecision ((int64_t)(r * (TimeStepPrecision::g_tsPrecFactor / pow (10,9))),
false);
*this = Time::FromDouble (r, Time::NS);
return;
}
if (trailer == std::string ("ps"))
{
m_data = HighPrecision ((int64_t)(r * (TimeStepPrecision::g_tsPrecFactor / pow (10,12))),
false);
*this = Time::FromDouble (r, Time::PS);
return;
}
if (trailer == std::string ("fs"))
{
m_data = HighPrecision ((int64_t)(r * (TimeStepPrecision::g_tsPrecFactor / pow (10,15))),
false);
*this = Time::FromDouble (r, Time::FS);
return;
}
NS_FATAL_ERROR ("Can't Parse Time " << s);
NS_ABORT_MSG ("Can't Parse Time " << s);
}
// else
// they didn't provide units, assume seconds
@@ -120,244 +78,68 @@ TimeUnit<1>::TimeUnit (const std::string& s)
iss.str (s);
double v;
iss >> v;
m_data = HighPrecision (v);
m_data.Mul (HighPrecision (TimeStepPrecision::g_tsPrecFactor, false));
*this = Time::FromDouble (v, Time::S);
}
double
TimeUnit<1>::GetSeconds (void) const
{
HighPrecision tmp = GetHighPrecision ();
HighPrecision factor = HighPrecision (TimeStepPrecision::g_tsPrecFactor, false);
tmp.Div (factor);
return tmp.GetDouble ();
}
int64_t
TimeUnit<1>::ConvertToUnits (int64_t timeValue, uint64_t unitFactor) const
{
uint64_t precFactor;
// In order to avoid conversion to double, precFactor can't be less 1
if (TimeStepPrecision::g_tsPrecFactor < unitFactor)
{
precFactor = unitFactor / TimeStepPrecision::g_tsPrecFactor;
timeValue = timeValue * precFactor;
}
else
{
precFactor = TimeStepPrecision::g_tsPrecFactor / unitFactor;
timeValue = timeValue / precFactor;
}
return timeValue;
}
int64_t
TimeUnit<1>::GetMilliSeconds (void) const
{
int64_t ts = GetTimeStep ();
int64_t ms = ConvertToUnits (ts, TimeStepPrecision::MS_FACTOR);
return ms;
}
int64_t
TimeUnit<1>::GetMicroSeconds (void) const
{
int64_t ts = GetTimeStep ();
int64_t us = ConvertToUnits (ts, TimeStepPrecision::US_FACTOR);
return us;
}
int64_t
TimeUnit<1>::GetNanoSeconds (void) const
{
int64_t ts = GetTimeStep ();
int64_t ns = ConvertToUnits (ts, TimeStepPrecision::NS_FACTOR);
return ns;
}
int64_t
TimeUnit<1>::GetPicoSeconds (void) const
{
int64_t ts = GetTimeStep ();
int64_t ps = ConvertToUnits (ts, TimeStepPrecision::PS_FACTOR);
return ps;
}
int64_t
TimeUnit<1>::GetFemtoSeconds (void) const
{
int64_t ts = GetTimeStep ();
int64_t fs = ConvertToUnits (ts, TimeStepPrecision::FS_FACTOR);
return fs;
}
/**
* This returns the value with the precision returned by TimeStepPrecision::Get
*/
int64_t
TimeUnit<1>::GetTimeStep (void) const
{
int64_t timeValue = GetHighPrecision ().GetInteger ();
return timeValue;
}
std::ostream&
operator<< (std::ostream& os, const Time & time)
{
std::string unit;
switch (TimeStepPrecision::Get ())
switch (TimeBase::GetResolution ())
{
case TimeStepPrecision::S:
case TimeBase::S:
unit = "s";
break;
case TimeStepPrecision::MS:
case TimeBase::MS:
unit = "ms";
break;
case TimeStepPrecision::US:
case TimeBase::US:
unit = "us";
break;
case TimeStepPrecision::NS:
case TimeBase::NS:
unit = "ns";
break;
case TimeStepPrecision::PS:
case TimeBase::PS:
unit = "ps";
break;
case TimeStepPrecision::FS:
case TimeBase::FS:
unit = "fs";
break;
case TimeBase::LAST:
NS_ABORT_MSG ("can't be reached");
unit = "unreachable";
break;
}
os << time.GetTimeStep () << unit;
double v = Time::ToDouble (time, Time::GetResolution ());
os << v << unit;
return os;
}
std::istream& operator>> (std::istream& is, Time & time)
{
std::string value;
is >> value;
std::string::size_type n = value.find_first_not_of ("0123456789.");
if (n == std::string::npos)
{
is.setstate (std::ios_base::failbit);
return is;
}
std::string trailer = value.substr (n, value.size () - n);
std::istringstream iss;
iss.str (value.substr (0, n));
if (trailer == std::string ("s"))
{
double v;
iss >> v;
time = Seconds (v);
return is;
}
uint64_t integer;
iss >> integer;
if (is.bad () || is.fail ())
{
is.setstate (std::ios_base::failbit);
}
else if (trailer == std::string ("ms"))
{
time = MilliSeconds (integer);
}
else if (trailer == std::string ("us"))
{
time = MicroSeconds (integer);
}
else if (trailer == std::string ("ns"))
{
time = NanoSeconds (integer);
}
else if (trailer == std::string ("ps"))
{
time = PicoSeconds (integer);
}
else if (trailer == std::string ("fs"))
{
time = FemtoSeconds (integer);
}
else
{
is.setstate (std::ios_base::failbit);
}
time = Time (value);
return is;
}
Time Seconds (double seconds)
{
HighPrecision tmp = HighPrecision (seconds);
HighPrecision factor = HighPrecision (TimeStepPrecision::g_tsPrecFactor, false);
tmp.Mul (factor);
return Time (tmp);
}
uint64_t
TimeUnit<1>::UnitsToTimestep (uint64_t unitValue,
uint64_t unitFactor)
{
uint64_t precFactor;
// In order to avoid conversion to double, precFactor can't be less 1
if (TimeStepPrecision::g_tsPrecFactor < unitFactor)
{
precFactor = unitFactor / TimeStepPrecision::g_tsPrecFactor;
unitValue = unitValue / precFactor;
}
else
{
precFactor = TimeStepPrecision::g_tsPrecFactor / unitFactor;
unitValue = unitValue * precFactor;
}
return unitValue;
}
ATTRIBUTE_VALUE_IMPLEMENT (Time);
ATTRIBUTE_CHECKER_IMPLEMENT (Time);
Time MilliSeconds (uint64_t ms)
{
uint64_t ts = TimeUnit<1>::UnitsToTimestep (ms, TimeStepPrecision::MS_FACTOR);
return TimeStep (ts);
}
TimeUnit<0>::TimeUnit ()
: TimeBase (HighPrecision ())
{}
Time MicroSeconds (uint64_t us)
{
uint64_t ts = TimeUnit<1>::UnitsToTimestep (us, TimeStepPrecision::US_FACTOR);
return TimeStep (ts);
}
Time NanoSeconds (uint64_t ns)
{
uint64_t ts = TimeUnit<1>::UnitsToTimestep (ns, TimeStepPrecision::NS_FACTOR);
return TimeStep (ts);
}
Time PicoSeconds (uint64_t ps)
{
uint64_t ts = TimeUnit<1>::UnitsToTimestep (ps, TimeStepPrecision::PS_FACTOR);
return TimeStep (ts);
}
Time FemtoSeconds (uint64_t fs)
{
uint64_t ts = TimeUnit<1>::UnitsToTimestep (fs, TimeStepPrecision::FS_FACTOR);
return TimeStep (ts);
}
/*
* The timestep value passed to this function must be of the precision
* of TimeStepPrecision::Get
*/
Time TimeStep (uint64_t ts)
{
return Time (HighPrecision (ts, false));
}
TimeUnit<0>::TimeUnit (const TimeUnit &o)
: TimeBase (o)
{}
TimeUnit<0>::TimeUnit (double scalar)
: m_data (HighPrecision (scalar))
{
}
: TimeBase (HighPrecision (scalar))
{}
TimeUnit<0>::TimeUnit (const HighPrecision &o)
: TimeBase (o) {}
double
TimeUnit<0>::GetDouble (void) const
@@ -371,329 +153,6 @@ TimeUnit<0>::GetDouble (void) const
namespace ns3 {
#define PRECISION(mult) (pow (10,-((double)(ns3::TimeStepPrecision::Get ()))) * mult)
#define ASSERT_MSG_EQ(a,b,mult,msg) \
NS_TEST_ASSERT_MSG_EQ (((a) < ((b) - PRECISION (mult)) || (a) > ((b) + PRECISION (mult))),false, \
msg << " Values are not equal within requested precision range: " << \
(a) << "!=" << (b) << " ~ " << PRECISION (mult))
#define ASSERT_MSG_EQ_INT(a,b,mult,msg) \
ASSERT_MSG_EQ (((int64_t)(a)),((int64_t)(b)),mult,msg)
#define ASSERT_EQ(a,b) \
ASSERT_MSG_EQ (a,b,1,"")
class OldTimeTestCase : public TestCase
{
public:
OldTimeTestCase ();
virtual bool DoRun (void);
};
OldTimeTestCase::OldTimeTestCase ()
: TestCase ("Sanity check of common time operations")
{
}
bool
OldTimeTestCase::DoRun (void)
{
NS_TEST_ASSERT_MSG_EQ (TimeStepPrecision::Get (),
TimeStepPrecision::NS,
"Invalid precision mode");
Time t0 = Seconds (10.0);
ASSERT_EQ (t0.GetSeconds (), 10.0);
Time t1 = Seconds (11.0);
ASSERT_EQ (t1.GetSeconds (), 11.0);
t0 = Seconds (1.5);
ASSERT_EQ (t0.GetSeconds (), 1.5);
t0 = Seconds (-1.5);
ASSERT_EQ (t0.GetSeconds (), -1.5);
t0 = MilliSeconds ((uint64_t)10.0);
ASSERT_EQ (t0.GetSeconds (), 0.01);
t1 = MilliSeconds ((uint64_t)11.0);
ASSERT_EQ (t1.GetSeconds (), 0.011);
Time t2, t3;
t2 = t1 - t0;
NS_TEST_ASSERT_MSG_EQ (t2.IsStrictlyPositive (),true, "Variable should be positive");
ASSERT_EQ (t2.GetSeconds (), t1.GetSeconds () - t0.GetSeconds ());
t2 = t1 - t1;
NS_TEST_ASSERT_MSG_EQ (t2.IsZero (),true, "Variable should be zero");
ASSERT_EQ (t2.GetSeconds (), t1.GetSeconds () - t1.GetSeconds ());
t2 = t0 - t1;
NS_TEST_ASSERT_MSG_EQ (t2.IsStrictlyNegative (),true, "Variable should be negative");
ASSERT_EQ (t2.GetSeconds (), t0.GetSeconds () - t1.GetSeconds ());
Time tmp = MilliSeconds (0);
NS_TEST_ASSERT_MSG_EQ ((MilliSeconds (0) == NanoSeconds (0)), true, "Zero is not Zero ?");
NS_TEST_ASSERT_MSG_EQ ((MilliSeconds (0) > NanoSeconds (0)), false, "Zero is bigger than Zero ?");
NS_TEST_ASSERT_MSG_EQ ((MilliSeconds (0) < NanoSeconds (0)), false, "Zero is smaller than Zero ?");
Time t4 = Seconds (10.0) * Scalar (1.5);
ASSERT_EQ (t4.GetSeconds (), 15.0);
Time t5 = NanoSeconds (10) * Scalar (1.5);
ASSERT_EQ (t5.GetNanoSeconds (), 15.0);
Time t6 = Seconds (10.0) * Scalar (15) / Scalar (10);
ASSERT_EQ (t6.GetSeconds (), 15.0);
Time t7 = NanoSeconds (10) * Scalar (15) / Scalar (10);
ASSERT_EQ (t7.GetNanoSeconds (), 15.0);
ASSERT_EQ ((t1 + t2).GetSeconds (), t1.GetSeconds () + t2.GetSeconds ());
ASSERT_EQ ((t1 / t2).GetDouble (), t1.GetSeconds () / t2.GetSeconds ());
return false;
}
class OperationsTimeTestCase : public TestCase
{
public:
OperationsTimeTestCase ();
virtual bool DoRun (void);
};
OperationsTimeTestCase::OperationsTimeTestCase ()
: TestCase ("Check the +, -, * and / operators for the TimeUnit<1>")
{
}
bool
OperationsTimeTestCase::DoRun (void)
{
// What happens if you set these values ?
// t0 = Seconds ((uint64_t)10.0);
// t1 = Seconds ((uint64_t)11.0);
Time t0 = MilliSeconds (10);
Time t1 = MilliSeconds (11);
ASSERT_EQ ((t0 - t1).GetSeconds (),
(t0.GetSeconds () - t1.GetSeconds ()));
ASSERT_EQ (((t0 - t1) * t0 / t0).GetSeconds (),
((t0.GetSeconds () - t1.GetSeconds ()) * t0.GetSeconds () / t0.GetSeconds ()));
ASSERT_EQ (((t0 - t1) * t0 / t1).GetSeconds (),
((t0.GetSeconds () - t1.GetSeconds ()) * t0.GetSeconds () / t1.GetSeconds ()));
ASSERT_EQ ((t0 * (t0 - t1) / t1).GetSeconds (),
(t0.GetSeconds () * (t0.GetSeconds () - t1.GetSeconds ()) / t1.GetSeconds ()));
ASSERT_EQ ((t0 * t1 / (t0 - t1)).GetSeconds (),
(t0.GetSeconds () * t1.GetSeconds () / (t0.GetSeconds () - t1.GetSeconds ())));
ASSERT_EQ ((t0 * (t1 / (t0 - t1))).GetSeconds (),
(t0.GetSeconds () * (t1.GetSeconds () / (t0.GetSeconds () - t1.GetSeconds ()))));
ASSERT_EQ (((t0 * t1) / (t0 - t1)).GetSeconds (),
((t0.GetSeconds () * t1.GetSeconds ()) / (t0.GetSeconds () - t1.GetSeconds ())));
ASSERT_EQ ((t0 / t1 * (t0 - t1)).GetSeconds (),
(t0.GetSeconds () / t1.GetSeconds () * (t0.GetSeconds () - t1.GetSeconds ())));
ASSERT_EQ (((t0 / t1) * (t0 - t1)).GetSeconds (),
(t0.GetSeconds () / t1.GetSeconds ()) * (t0.GetSeconds () - t1.GetSeconds ()));
ASSERT_EQ ((t0 * Scalar (10.0)).GetSeconds (), (t0.GetSeconds () * 10.0));
ASSERT_EQ ((Scalar (10.0) * t0).GetSeconds (), (10.0 * t0.GetSeconds ()));
// Note: we need to multiply by 1e9 because GetSeconds is multiplying
ASSERT_EQ (((t0 / (t1 * (t0 - t1))).GetHighPrecision ().GetDouble () * 1e9),
(t0.GetSeconds () / (t1.GetSeconds () * (t0.GetSeconds () - t1.GetSeconds ()))));
ASSERT_EQ ((t0 / t1).GetDouble (),(t0.GetSeconds () / t1.GetSeconds ()));
ASSERT_EQ ((t0 * t1 / ((t0 - t1) * t0)).GetDouble (),
(t0.GetSeconds () * t1.GetSeconds () / ((t0.GetSeconds () - t1.GetSeconds ()) * t0.GetSeconds ())));
return false;
}
class TimeStepTestCase : public TestCase
{
public:
TimeStepTestCase ();
virtual bool DoRun (void);
};
TimeStepTestCase::TimeStepTestCase ()
: TestCase ("Check boundaries of TimeStep")
{
}
bool
TimeStepTestCase::DoRun (void)
{
Time tooBig = TimeStep (0x8000000000000000LL);
NS_TEST_ASSERT_MSG_EQ (tooBig.IsNegative (), true, "Is not negative ?");
tooBig = TimeStep (0xffffffffffffffffLL);
NS_TEST_ASSERT_MSG_EQ (tooBig.IsNegative (), true, "Is not negative ?");
tooBig = TimeStep (0x7fffffffffffffffLL);
NS_TEST_ASSERT_MSG_EQ (tooBig.IsPositive (), true, "Is not negative ?");
tooBig += TimeStep (1);
NS_TEST_ASSERT_MSG_EQ (tooBig.IsNegative (), true, "Is not negative ?");
return false;
}
class GlobalPrecisionTestCase : public TestCase
{
public:
GlobalPrecisionTestCase ();
virtual bool DoRun (void);
virtual void DoTeardown (void);
};
GlobalPrecisionTestCase::GlobalPrecisionTestCase ()
: TestCase ("Check that global value actually changes the underlying precision")
{
}
#define CHECK_PRECISION(prec) \
Config::SetGlobal ("TimeStepPrecision", StringValue (# prec)); \
NS_TEST_ASSERT_MSG_EQ (TimeStepPrecision::Get (), TimeStepPrecision::prec, "Could not set precision " << # prec)
bool
GlobalPrecisionTestCase::DoRun (void)
{
CHECK_PRECISION (S);
CHECK_PRECISION (MS);
CHECK_PRECISION (US);
CHECK_PRECISION (NS);
CHECK_PRECISION (PS);
CHECK_PRECISION (FS);
return false;
}
void
GlobalPrecisionTestCase::DoTeardown (void)
{
TimeStepPrecision::Set (TimeStepPrecision::NS);
}
#if 0
// disable this test because it triggers crazy
// compiler behavior (ICE+unbounded memory usage)
class ConversionTestCase : public TestCase
{
public:
ConversionTestCase ();
virtual bool DoRun (void);
virtual void DoTeardown (void);
};
ConversionTestCase::ConversionTestCase ()
: TestCase ("Check crazy time conversions")
{
}
void ConversionTestCase::DoTeardown (void)
{
TimeStepPrecision::Set (TimeStepPrecision::NS);
}
#define CHECK_CONVERSIONS(tmp) \
do { \
double val = tmp; \
Time t_sec = Seconds (val); \
ASSERT_MSG_EQ (t_sec.GetSeconds (), val * 1e0, 1e0, "conv sec s"); \
ASSERT_MSG_EQ_INT (t_sec.GetMilliSeconds (), val * 1e3, 1e3, "conv sec ms"); \
ASSERT_MSG_EQ_INT (t_sec.GetMicroSeconds (), val * 1e6, 1e6, "conv sec us"); \
ASSERT_MSG_EQ_INT (t_sec.GetNanoSeconds (), val * 1e9, 1e9, "conv sec ns"); \
ASSERT_MSG_EQ_INT (t_sec.GetPicoSeconds (), val * 1e12, 1e12, "conv sec ps"); \
ASSERT_MSG_EQ_INT (t_sec.GetFemtoSeconds (), val * 1e15, 1e15, "conv sec fs"); \
uint64_t int_val = (uint64_t)val; \
Time t_ms = MilliSeconds (int_val); \
ASSERT_MSG_EQ (t_ms.GetSeconds (), val * 1e-3, 1e0, "conv ms s"); \
ASSERT_MSG_EQ_INT (t_ms.GetMilliSeconds (), val * 1e0, 1e3, "conv ms ms"); \
ASSERT_MSG_EQ_INT (t_ms.GetMicroSeconds (), val * 1e3, 1e6, "conv ms us"); \
ASSERT_MSG_EQ_INT (t_ms.GetNanoSeconds (), val * 1e6, 1e9, "conv ms ns"); \
ASSERT_MSG_EQ_INT (t_ms.GetPicoSeconds (), val * 1e9, 1e12, "conv ms fs"); \
ASSERT_MSG_EQ_INT (t_ms.GetFemtoSeconds (), val * 1e12, 1e15, "conv ms ps"); \
Time t_us = MicroSeconds (int_val); \
ASSERT_MSG_EQ (t_us.GetSeconds (), val * 1e-6, 1e0, "conv us s"); \
ASSERT_MSG_EQ_INT (t_us.GetMilliSeconds (), val * 1e-3, 1e3, "conv us ms"); \
ASSERT_MSG_EQ_INT (t_us.GetMicroSeconds (), val * 1e0, 1e6, "conv us us"); \
ASSERT_MSG_EQ_INT (t_us.GetNanoSeconds (), val * 1e3, 1e9, "conv us ns"); \
ASSERT_MSG_EQ_INT (t_us.GetPicoSeconds (), val * 1e6, 1e12, "conv us ps"); \
ASSERT_MSG_EQ_INT (t_us.GetFemtoSeconds (), val * 1e9, 1e15, "conv us fs"); \
Time t_ns = NanoSeconds (int_val); \
ASSERT_MSG_EQ (t_ns.GetSeconds (), val * 1e-9, 1e0, "conv ns s"); \
ASSERT_MSG_EQ_INT (t_ns.GetMilliSeconds (), val * 1e-6, 1e3, "conv ns ms"); \
ASSERT_MSG_EQ_INT (t_ns.GetMicroSeconds (), val * 1e-3, 1e6, "conv ns us"); \
ASSERT_MSG_EQ_INT (t_ns.GetNanoSeconds (), val * 1e0, 1e9, "conv ns ns"); \
ASSERT_MSG_EQ_INT (t_ns.GetPicoSeconds (), val * 1e3, 1e12, "conv ns ps"); \
ASSERT_MSG_EQ_INT (t_ns.GetFemtoSeconds (), val * 1e6, 1e15, "conv ns fs"); \
Time t_ps = PicoSeconds (int_val); \
ASSERT_MSG_EQ (t_ps.GetSeconds (), val * 1e-12, 1e0, "conv ps s"); \
ASSERT_MSG_EQ_INT (t_ps.GetMilliSeconds (), val * 1e-9, 1e3, "conv ps ms"); \
ASSERT_MSG_EQ_INT (t_ps.GetMicroSeconds (), val * 1e-6, 1e6, "conv ps us"); \
ASSERT_MSG_EQ_INT (t_ps.GetNanoSeconds (), val * 1e-3, 1e9, "conv ps ns"); \
ASSERT_MSG_EQ_INT (t_ps.GetPicoSeconds (), val * 1e0, 1e12, "conv ps ps"); \
ASSERT_MSG_EQ_INT (t_ps.GetFemtoSeconds (), val * 1e3, 1e15, "conv ps fs"); \
Time t_fs = FemtoSeconds (int_val); \
ASSERT_MSG_EQ (t_fs.GetSeconds (), val * 1e-15, 1e0, "conv fs sec"); \
ASSERT_MSG_EQ_INT (t_fs.GetMilliSeconds (), val * 1e-12, 1e3, "conv fs ms"); \
ASSERT_MSG_EQ_INT (t_fs.GetMicroSeconds (), val * 1e-9, 1e6, "conv fs us"); \
ASSERT_MSG_EQ_INT (t_fs.GetNanoSeconds (), val * 1e-6, 1e9, "conv fs ns"); \
ASSERT_MSG_EQ_INT (t_fs.GetPicoSeconds (), val * 1e-3, 1e12, "conv fs ps"); \
ASSERT_MSG_EQ_INT (t_fs.GetFemtoSeconds (), val * 1e0, 1e15, "conv fs fs"); \
} while (false)
bool
ConversionTestCase::DoRun (void)
{
CHECK_CONVERSIONS (5);
CHECK_CONVERSIONS (0);
CHECK_CONVERSIONS (783);
CHECK_CONVERSIONS (1132);
// triggers overflow
// XXX
// CHECK_CONVERSIONS(3341039);
TimeStepPrecision::Set (TimeStepPrecision::US);
CHECK_CONVERSIONS (7);
CHECK_CONVERSIONS (546);
CHECK_CONVERSIONS (6231);
// triggers overflow
// XXX
// CHECK_CONVERSIONS(1234639);
TimeStepPrecision::Set (TimeStepPrecision::MS);
CHECK_CONVERSIONS (3);
CHECK_CONVERSIONS (134);
CHECK_CONVERSIONS (2341);
// triggers overflow
// XXX
// CHECK_CONVERSIONS(8956239);
TimeStepPrecision::Set (TimeStepPrecision::NS);
CHECK_CONVERSIONS (4);
CHECK_CONVERSIONS (342);
CHECK_CONVERSIONS (1327);
// triggers overflow
// XXX
// CHECK_CONVERSIONS(5439627);
TimeStepPrecision::Set (TimeStepPrecision::PS);
CHECK_CONVERSIONS (4);
CHECK_CONVERSIONS (342);
CHECK_CONVERSIONS (1327);
// triggers overflow
// XXX
// CHECK_CONVERSIONS(5439627);
TimeStepPrecision::Set (TimeStepPrecision::NS);
CHECK_CONVERSIONS (12);
TimeStepPrecision::Set (TimeStepPrecision::S);
CHECK_CONVERSIONS (7);
TimeStepPrecision::Set (TimeStepPrecision::FS);
CHECK_CONVERSIONS (5);
return false;
}
#endif
class Bug863TestCase : public TestCase
{
public:
@@ -713,18 +172,61 @@ bool Bug863TestCase::DoRun (void)
return false;
}
class TimeSimpleTestCase : public TestCase
{
public:
TimeSimpleTestCase (enum TimeBase::Unit resolution);
private:
virtual bool DoRun (void);
virtual void DoTearDown (void);
enum TimeBase::Unit m_originalResolution;
enum TimeBase::Unit m_resolution;
};
TimeSimpleTestCase::TimeSimpleTestCase (enum TimeBase::Unit resolution)
: TestCase ("Sanity check of common time operations"),
m_resolution (resolution)
{}
bool
TimeSimpleTestCase::DoRun (void)
{
m_originalResolution = Time::GetResolution ();
Time::SetResolution (m_resolution);
NS_TEST_ASSERT_MSG_EQ_TOL (Seconds (1.0).GetSeconds (), 1.0, TimeStep (1).GetSeconds (),
"is 1 really 1 ?");
NS_TEST_ASSERT_MSG_EQ_TOL (Seconds (10.0).GetSeconds (), 10.0, TimeStep (1).GetSeconds (),
"is 10 really 10 ?");
NS_TEST_ASSERT_MSG_EQ (MilliSeconds (1).GetMilliSeconds (), 1,
"is 1ms really 1ms ?");
NS_TEST_ASSERT_MSG_EQ (MicroSeconds (1).GetMicroSeconds (), 1,
"is 1us really 1us ?");
#if 0
Time ns = NanoSeconds (1);
ns.GetNanoSeconds ();
NS_TEST_ASSERT_MSG_EQ (NanoSeconds (1).GetNanoSeconds (), 1,
"is 1ns really 1ns ?");
NS_TEST_ASSERT_MSG_EQ (PicoSeconds (1).GetPicoSeconds (), 1,
"is 1ps really 1ps ?");
NS_TEST_ASSERT_MSG_EQ (FemtoSeconds (1).GetFemtoSeconds (), 1,
"is 1fs really 1fs ?");
#endif
return false;
}
void
TimeSimpleTestCase::DoTearDown (void)
{
Time::SetResolution (m_originalResolution);
}
static class TimeTestSuite : public TestSuite
{
public:
TimeTestSuite ()
: TestSuite ("time", UNIT)
{
AddTestCase (new OldTimeTestCase ());
AddTestCase (new OperationsTimeTestCase ());
AddTestCase (new TimeStepTestCase ());
AddTestCase (new GlobalPrecisionTestCase ());
AddTestCase (new Bug863TestCase ());
// AddTestCase(new ConversionTestCase());
AddTestCase (new TimeSimpleTestCase (TimeBase::US));
}
} g_timeTestSuite;

View File

@@ -55,6 +55,7 @@ def build(bld):
sim = bld.create_ns3_module('simulator', ['core'])
sim.source = [
'high-precision.cc',
'time-base.cc',
'time.cc',
'event-id.cc',
'scheduler.cc',
@@ -77,6 +78,7 @@ def build(bld):
headers.module = 'simulator'
headers.source = [
'high-precision.h',
'time-base.h',
'nstime.h',
'event-id.h',
'event-impl.h',
@@ -106,7 +108,7 @@ def build(bld):
elif env['USE_HIGH_PRECISION_CAIRO']:
sim.source.extend([
'high-precision-cairo.cc',
'cairo-wideint.c',
# 'cairo-wideint.c',
])
headers.source.extend([
'high-precision-cairo.h',