merge with bug826 fixes

This commit is contained in:
Mathieu Lacage
2010-08-07 14:30:18 +02:00
20 changed files with 1368 additions and 1226 deletions

View File

@@ -125,8 +125,8 @@ ATTRIBUTE_HELPER_HEADER (DataRate);
* \param rhs
* \return Bits transmitted in rhs seconds at lhs b/s
*/
double operator*(const DataRate& lhs, const TimeUnit<1>& rhs);
double operator*(const TimeUnit<1>& lhs, const DataRate& rhs);
double operator*(const DataRate& lhs, const Time& rhs);
double operator*(const Time& lhs, const DataRate& rhs);
} //namespace ns3

View File

@@ -67,7 +67,7 @@ Backoff::GetBackoffTime (void)
uint32_t backoffSlots = (uint32_t)m_rng.GetValue(minSlot, maxSlot);
backoff = Scalar(backoffSlots) * m_slotTime;
backoff = Scalar (backoffSlots) * m_slotTime;
return (backoff);
}

View File

@@ -157,7 +157,7 @@ NdiscCache::Entry::Entry (NdiscCache* nd)
m_retransTimer(Timer::CANCEL_ON_DESTROY),
m_probeTimer(Timer::CANCEL_ON_DESTROY),
m_delayTimer(Timer::CANCEL_ON_DESTROY),
m_lastReachabilityConfirmation(Timer::CANCEL_ON_DESTROY),
m_lastReachabilityConfirmation(Seconds (0.0)),
m_nsRetransmit (0)
{
NS_LOG_FUNCTION_NOARGS ();

View File

@@ -28,8 +28,8 @@ Waypoint::Waypoint (const Time &waypointTime, const Vector &waypointPosition)
position (waypointPosition)
{}
Waypoint::Waypoint ()
: time (0.0),
position (0,0,0)
: time (Seconds (0.0)),
position (0,0,0)
{}
std::ostream &operator << (std::ostream &os, const Waypoint &waypoint)

View File

@@ -40,7 +40,7 @@ TypeId
DefaultSimulatorImpl::GetTypeId (void)
{
static TypeId tid = TypeId ("ns3::DefaultSimulatorImpl")
.SetParent<Object> ()
.SetParent<SimulatorImpl> ()
.AddConstructor<DefaultSimulatorImpl> ()
;
return tid;

View File

@@ -1,30 +1,59 @@
#include "high-precision-128.h"
#include "ns3/fatal-error.h"
#include "ns3/abort.h"
#include "ns3/assert.h"
#include <math.h>
#ifdef COUNT_OPS
#include <iostream>
#endif
namespace ns3 {
#ifdef COUNT_OPS
uint128_t HighPrecision::g_nAdd = 0;
uint128_t HighPrecision::g_nMuli = 0;
uint128_t HighPrecision::g_nMul = 0;
uint128_t HighPrecision::g_nDiv = 0;
uint128_t HighPrecision::g_nCmp = 0;
HighPrecision::Printer HighPrecision::g_printer;
HighPrecision::Printer::~Printer ()
{
std::cout << "add=" << (double)g_nAdd << " mul=" << (double)g_nMul << " div=" << (double)g_nDiv
<< " muli=" << (double)g_nMuli << " cmp=" << (double)g_nCmp;
}
#endif
#define OUTPUT_SIGN(sa,sb,ua,ub) \
({bool negA, negB; \
negA = sa < 0; \
negB = sb < 0; \
ua = negA?-sa:sa; \
ub = negB?-sb:sb; \
(negA && !negB) || (!negA && negB);})
#define MASK_LO ((((uint128_t)1)<<64)-1)
#define MASK_HI (~MASK_LO)
void
HighPrecision::Mul (HighPrecision const &o)
{
bool negResult, negA, negB;
// take the sign of the operands
negA = m_value < 0;
negB = o.m_value < 0;
// 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
bool negResult;
uint128_t a, b;
a = negA?-m_value:m_value;
b = negB?-o.m_value:o.m_value;
negResult = OUTPUT_SIGN (m_value, o.m_value, a, b);
int128_t result = Umul (a, b);
// add the sign to the result
result = negResult ? -result : result;
m_value = result;
}
uint128_t
HighPrecision::Umul (uint128_t a, uint128_t b)
{
INC_MUL;
uint128_t aL = a & MASK_LO;
uint128_t bL = b & MASK_LO;
uint128_t aH = (a >> 64) & MASK_LO;
uint128_t bH = (b >> 64) & MASK_LO;
uint128_t result;
uint128_t hiPart,loPart,midPart;
@@ -42,28 +71,25 @@ HighPrecision::Mul (HighPrecision const &o)
// truncate the high part and only use the low part
result |= ((hiPart & MASK_LO) << 64) + (midPart & MASK_HI);
// if the high part is not zero, put a warning
if ((hiPart & MASK_HI) != 0)
{
NS_FATAL_ERROR ("High precision 128 bits multiplication error: multiplication overflow.");
}
// add the sign to the result
result = negResult ? -result:result;
m_value = result;
NS_ABORT_MSG_IF ((hiPart & MASK_HI) != 0,
"High precision 128 bits multiplication error: multiplication overflow.");
return result;
}
void
HighPrecision::Div (HighPrecision const &o)
{
bool negResult, negA, negB;
// take the sign of the operands
negA = m_value < 0;
negB = o.m_value < 0;
// 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
bool negResult;
uint128_t a, b;
a = negA?-m_value:m_value;
b = negB?-o.m_value:o.m_value;
negResult = OUTPUT_SIGN (m_value, o.m_value, a, b);
int128_t result = Divu (a, b);
result = negResult ? -result:result;
m_value = result;
}
uint128_t
HighPrecision::Divu (uint128_t a, uint128_t b)
{
INC_DIV;
uint128_t quo = a / b;
uint128_t rem = (a % b);
uint128_t result = quo << 64;
@@ -82,8 +108,50 @@ HighPrecision::Div (HighPrecision const &o)
}
quo = rem / div;
result = result + quo;
result = negResult ? -result:result;
m_value = result;
return result;
}
void
HighPrecision::MulByInvert (const HighPrecision &o)
{
bool negResult = m_value < 0;
uint128_t a = negResult?-m_value:m_value;
uint128_t result = UmulByInvert (a, o.m_value);
m_value = negResult?-result:result;
}
uint128_t
HighPrecision::UmulByInvert (uint128_t a, uint128_t b)
{
INC_MULI;
uint128_t result, ah, bh, al, bl;
uint128_t hi, mid;
ah = a >> 64;
bh = b >> 64;
al = a & MASK_LO;
bl = b & MASK_LO;
hi = ah * bh;
mid = ah * bl + al * bh;
mid >>= 64;
result = ah * bh + mid;
return result;
}
HighPrecision
HighPrecision::Invert (uint64_t v)
{
NS_ASSERT (v > 1);
uint128_t a;
a = 1;
a <<= 64;
HighPrecision result;
result.m_value = Divu (a, v);
HighPrecision tmp = HighPrecision (v, false);
tmp.MulByInvert (result);
if (tmp.GetInteger () != 1)
{
result.m_value += 1;
}
return result;
}
} // namespace ns3

View File

@@ -24,19 +24,37 @@
#include <math.h>
#include <stdint.h>
#define noCOUNT_OPS 1
#if defined(HAVE___UINT128_T) and !defined(HAVE_UINT128_T)
typedef __uint128_t uint128_t;
typedef __int128_t int128_t;
#endif
#ifdef COUNT_OPS
#define INC_ADD HighPrecision::g_nAdd++
#define INC_SUB HighPrecision::g_nAdd++
#define INC_MUL HighPrecision::g_nMul++
#define INC_DIV HighPrecision::g_nDiv++
#define INC_MULI HighPrecision::g_nMuli++
#define INC_CMP HighPrecision::g_nCmp++
#else
#define INC_ADD
#define INC_SUB
#define INC_MUL
#define INC_DIV
#define INC_MULI
#define INC_CMP
#endif
namespace ns3 {
class HighPrecision
{
public:
inline HighPrecision ();
inline HighPrecision (int64_t value, bool dummy);
inline HighPrecision (double value);
explicit inline HighPrecision (int64_t value, bool dummy);
explicit inline HighPrecision (double value);
inline int64_t GetInteger (void) const;
inline double GetDouble (void) const;
@@ -44,11 +62,28 @@ 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);
inline int Compare (HighPrecision const &o) const;
inline static HighPrecision Zero (void);
private:
static uint128_t UmulByInvert (uint128_t a, uint128_t b);
static uint128_t Umul (uint128_t a, uint128_t b);
static uint128_t Divu (uint128_t a, uint128_t b);
int128_t m_value;
#ifdef COUNT_OPS
static uint128_t g_nAdd;
static uint128_t g_nMuli;
static uint128_t g_nMul;
static uint128_t g_nDiv;
static uint128_t g_nCmp;
static struct Printer {
~Printer ();
} g_printer;
#endif
};
} // namespace ns3
@@ -75,17 +110,20 @@ int64_t HighPrecision::GetInteger (void) const
void
HighPrecision::Add (HighPrecision const &o)
{
INC_ADD;
m_value += o.m_value;
}
void
HighPrecision::Sub (HighPrecision const &o)
{
INC_SUB;
m_value -= o.m_value;
}
int
HighPrecision::Compare (HighPrecision const &o) const
{
INC_CMP;
return (m_value < o.m_value)?-1:(m_value == o.m_value)?0:1;
}

View File

@@ -19,17 +19,31 @@
*/
#include "high-precision-cairo.h"
#include "ns3/test.h"
#include "ns3/fatal-error.h"
#include "ns3/abort.h"
#include "ns3/assert.h"
#include <math.h>
#include <iostream>
namespace ns3 {
#define OUTPUT_SIGN(sa,sb,ua,ub) \
({bool negA, negB; \
negA = _cairo_int128_negative (sa); \
negB = _cairo_int128_negative (sb); \
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; \
(negA && !negB) || (!negA && negB);})
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;
bool sign = OUTPUT_SIGN (m_value, o.m_value, a, b);
result = Umul (a, b);
m_value = sign ? _cairo_uint128_negate (result) : result;
}
@@ -39,22 +53,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 +74,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;
bool sign = OUTPUT_SIGN (m_value, o.m_value, a, b);
result = Udiv (a, b);
m_value = sign ? _cairo_uint128_negate (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 +109,56 @@ 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::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 {
@@ -76,8 +45,8 @@ class HighPrecision
{
public:
inline HighPrecision ();
inline HighPrecision (int64_t value, bool dummy);
inline HighPrecision (double value);
explicit inline HighPrecision (int64_t value, bool dummy);
explicit inline HighPrecision (double value);
inline int64_t GetInteger (void) const;
inline double GetDouble (void) const;
@@ -85,12 +54,15 @@ 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);
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 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;

View File

@@ -35,8 +35,8 @@ class HighPrecision
{
public:
inline HighPrecision ();
inline HighPrecision (int64_t value, bool dummy);
inline HighPrecision (double value);
explicit inline HighPrecision (int64_t value, bool dummy);
explicit inline HighPrecision (double value);
inline int64_t GetInteger (void) const;
inline double GetDouble (void) const;
@@ -44,6 +44,8 @@ public:
inline void Sub (HighPrecision const &o);
inline void Mul (HighPrecision const &o);
inline void Div (HighPrecision const &o);
inline void MulByInvert (const HighPrecision &o);
inline static HighPrecision Invert (uint64_t v);
inline int Compare (HighPrecision const &o) const;
inline static HighPrecision Zero (void);
@@ -101,10 +103,21 @@ HighPrecision::Div (HighPrecision const &o)
{
m_value /= o.m_value;
}
void
HighPrecision::MulByInvert (const HighPrecision &o)
{
m_value *= o.m_value;
}
HighPrecision
HighPrecision::Invert (uint64_t v)
{
return HighPrecision (1.0 / v);
}
int
HighPrecision::Compare (HighPrecision const &o) const
{
return m_value < o.m_value;
return (m_value < o.m_value)?-1:(m_value == o.m_value)?0:1;
}
HighPrecision
HighPrecision::Zero (void)

View File

@@ -282,6 +282,70 @@ Hp128CompareTestCase::DoRun (void)
return false;
}
class Hp128InvertTestCase : public TestCase
{
public:
Hp128InvertTestCase ();
virtual bool DoRun (void);
};
Hp128InvertTestCase::Hp128InvertTestCase ()
: TestCase ("Test case for invertion")
{
}
bool
Hp128InvertTestCase::DoRun (void)
{
#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)
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);
#undef TEST
return false;
}
static class HighPrecision128TestSuite : public TestSuite
{
public:
@@ -292,6 +356,7 @@ public:
AddTestCase (new Hp128Bug455TestCase ());
AddTestCase (new Hp128Bug863TestCase ());
AddTestCase (new Hp128CompareTestCase ());
AddTestCase (new Hp128InvertTestCase ());
}
} g_highPrecision128TestSuite;

File diff suppressed because it is too large Load Diff

View File

@@ -45,7 +45,7 @@ TypeId
RealtimeSimulatorImpl::GetTypeId (void)
{
static TypeId tid = TypeId ("ns3::RealtimeSimulatorImpl")
.SetParent<Object> ()
.SetParent<SimulatorImpl> ()
.AddConstructor<RealtimeSimulatorImpl> ()
.AddAttribute ("SynchronizationMode",
"What to do if the simulation cannot keep up with real time.",

View File

@@ -0,0 +1,14 @@
#include "simulator-impl.h"
namespace ns3 {
TypeId
SimulatorImpl::GetTypeId (void)
{
static TypeId tid = TypeId ("ns3::SimulatorImpl")
.SetParent<Object> ()
;
return tid;
}
} // namespace ns3

View File

@@ -35,6 +35,8 @@ class Scheduler;
class SimulatorImpl : public Object
{
public:
static TypeId GetTypeId (void);
/**
* Every event scheduled by the Simulator::insertAtDestroy method is
* invoked. Then, we ensure that any memory allocated by the

View File

@@ -112,49 +112,15 @@ Synchronizer::EventEnd (void)
uint64_t
Synchronizer::TimeStepToNanosecond (uint64_t ts)
{
switch (TimeStepPrecision::Get ()) {
case TimeStepPrecision::S:
return ts * 1000000000;
case TimeStepPrecision::MS:
return ts * 1000000;
case TimeStepPrecision::US:
return ts * 1000;
case TimeStepPrecision::NS:
return ts;
case TimeStepPrecision::PS:
return ts / 1000;
case TimeStepPrecision::FS:
return ts / 1000000;
default:
NS_ASSERT_MSG (false, "Synchronizer::TimeStepToNanosecond: "
"Unexpected precision not implemented");
return 0;
}
return TimeStep (ts).GetNanoSeconds ();
}
uint64_t
Synchronizer::NanosecondToTimeStep (uint64_t ns)
{
switch (TimeStepPrecision::Get ()) {
case TimeStepPrecision::S:
return ns / 1000000000;
case TimeStepPrecision::MS:
return ns / 1000000;
case TimeStepPrecision::US:
return ns / 1000;
case TimeStepPrecision::NS:
return ns;
case TimeStepPrecision::PS:
return ns * 1000;
case TimeStepPrecision::FS:
return ns * 1000000;
default:
NS_ASSERT_MSG (false, "Synchronizer::NanosecondToTimeStep: "
"Unexpected precision not implemented");
return 0;
}
return NanoSeconds (ns).GetTimeStep ();
}
}; // namespace ns3
} // namespace ns3

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

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

@@ -0,0 +1,336 @@
/* -*- 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
*
* This class is the base class for all time-related classes. It controls
* the resolution of the underlying time value . The default resolution
* is nanoseconds. That is, TimeStep (1).GetNanoSeconds () will return
* 1. It is possible to either increase or decrease the resolution and the
* code tries really hard to make this easy.
*
* If your resolution is X (say, nanoseconds) and if you create Time objects
* with a lower resolution (say, picoseconds), don't expect that this
* code will return 1: PicoSeconds (1).GetPicoSeconds (). It will most
* likely return 0 because the Time object has only 64 bits of fractional
* precision which means that PicoSeconds (1) is stored as a 64-bit aproximation
* of 1/1000 in the Time object. If you later multiply it again by the exact
* value 1000, the result is unlikely to be 1 exactly. It will be close to
* 1 but not exactly 1.
*
* In general, it is thus a really bad idea to try to use time objects of a
* resolution higher than the global resolution controlled through
* TimeBase::SetResolution. If you do need to use picoseconds, it's thus best
* to switch the global resolution to picoseconds to avoid nasty surprises.
*
* Another important issue to keep in mind is that if you increase the
* global resolution, you also implicitely decrease the range of your simulation.
* i.e., the global simulation time is stored in a 64 bit integer whose interpretation
* will depend on the global resolution so, 2^64 picoseconds which is the maximum
* duration of your simulation if the global resolution is picoseconds
* is smaller than 2^64 nanoseconds which is the maximum duration of your simulation
* if the global resolution is nanoseconds.
*
* Finally, don't even think about ever changing the global resolution after
* creating Time objects: all Time objects created before the call to SetResolution
* will contain values which are not updated to the new resolution. In practice,
* the default value for the attributes of many models is indeed calculated
* before the main function of the main program enters. Because of this, if you
* use one of these models (and it's likely), it's going to be hard to change
* the global simulation resolution in a way which gives reasonable results. This
* issue has been filed as bug 954 in the ns-3 bugzilla installation.
*/
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
};
/**
* \param resolution the new resolution to use
*
* Change the global resolution used to convert all
* user-provided time values in Time objects and Time objects
* in user-expected time units.
*/
static void SetResolution (enum Unit resolution);
/**
* \returns the current global resolution.
*/
static enum Unit GetResolution (void);
/**
* \param value to convert into a Time object
* \param timeUnit the unit of the value to convert
* \return a new Time object
*
* This method interprets the input value according to the input
* unit and constructs a matching Time object.
*
* \sa FromDouble, ToDouble, ToInteger
*/
inline static TimeBase FromInteger (uint64_t value, enum Unit timeUnit);
/**
* \param value to convert into a Time object
* \param timeUnit the unit of the value to convert
* \return a new Time object
*
* \sa FromInteger, ToInteger, ToDouble
*/
inline static TimeBase FromDouble (double value, enum Unit timeUnit);
/**
* \param time a Time object
* \param timeUnit the unit of the value to return
*
* Convert the input time into an integer value according to the requested
* time unit.
*/
inline static uint64_t ToInteger (const TimeBase &time, enum Unit timeUnit);
/**
* \param time a Time object
* \param timeUnit the unit of the value to return
*
* Convert the input time into a floating point value according to the requested
* time unit.
*/
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,54 +20,18 @@
* 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"
#include "ns3/object.h"
#include "ns3/config.h"
#include <math.h>
#include <sstream>
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)
Time::Time (const std::string& s)
{
std::string::size_type n = s.find_first_not_of ("0123456789.");
if (n != std::string::npos)
@@ -79,40 +43,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,84 +79,60 @@ TimeUnit<1>::TimeUnit (const std::string& s)
iss.str (s);
double v;
iss >> v;
m_data = HighPrecision (v * TimeStepPrecision::g_tsPrecFactor);
*this = Time::FromDouble (v, Time::S);
}
double
TimeUnit<1>::GetSeconds (void) const
{
double timeValue = GetHighPrecision ().GetDouble ();
return timeValue / TimeStepPrecision::g_tsPrecFactor;
struct Time::Resolution
Time::GetNsResolution (void)
{
struct Resolution resolution;
SetResolution (Time::NS, &resolution);
return resolution;
}
int64_t
TimeUnit<1>::ConvertToUnits (int64_t timeValue, uint64_t unitFactor) const
void
Time::SetResolution (enum Unit resolution)
{
uint64_t precFactor;
// In order to avoid conversion to double, precFactor can't be less 1
if (TimeStepPrecision::g_tsPrecFactor < unitFactor)
SetResolution (resolution, PeekResolution ());
}
void
Time::SetResolution (enum Unit unit, struct Resolution *resolution)
{
int8_t power [LAST] = {15, 12, 9, 6, 3, 0};
for (int i = 0; i < Time::LAST; i++)
{
precFactor = unitFactor / TimeStepPrecision::g_tsPrecFactor;
timeValue = timeValue * precFactor;
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;
}
}
else
{
precFactor = TimeStepPrecision::g_tsPrecFactor / unitFactor;
timeValue = timeValue / precFactor;
}
return timeValue;
resolution->unit = unit;
}
int64_t
TimeUnit<1>::GetMilliSeconds (void) const
enum Time::Unit
Time::GetResolution (void)
{
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;
return PeekResolution ()->unit;
}
@@ -205,491 +140,52 @@ std::ostream&
operator<< (std::ostream& os, const Time & time)
{
std::string unit;
switch (TimeStepPrecision::Get ())
switch (Time::GetResolution ())
{
case TimeStepPrecision::S:
case Time::S:
unit = "s";
break;
case TimeStepPrecision::MS:
case Time::MS:
unit = "ms";
break;
case TimeStepPrecision::US:
case Time::US:
unit = "us";
break;
case TimeStepPrecision::NS:
case Time::NS:
unit = "ns";
break;
case TimeStepPrecision::PS:
case Time::PS:
unit = "ps";
break;
case TimeStepPrecision::FS:
case Time::FS:
unit = "fs";
break;
case Time::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)
{
double d_sec = seconds * TimeStepPrecision::g_tsPrecFactor;
return Time (HighPrecision (d_sec));
// return Time (HighPrecision ((int64_t)d_sec, false));
}
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);
}
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 (double scalar)
: m_data (HighPrecision (scalar))
{
}
double
TimeUnit<0>::GetDouble (void) const
{
return GetHighPrecision ().GetDouble ();
}
} // namespace ns3
#include "ns3/test.h"
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:
@@ -709,18 +205,100 @@ bool Bug863TestCase::DoRun (void)
return false;
}
class TimeSimpleTestCase : public TestCase
{
public:
TimeSimpleTestCase (enum Time::Unit resolution);
private:
virtual bool DoRun (void);
virtual void DoTearDown (void);
enum Time::Unit m_originalResolution;
enum Time::Unit m_resolution;
};
TimeSimpleTestCase::TimeSimpleTestCase (enum Time::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);
}
class ArithTestCase : public TestCase
{
public:
ArithTestCase ();
private:
virtual bool DoRun (void);
};
ArithTestCase::ArithTestCase ()
: TestCase ("check arithmetic operators")
{
}
bool
ArithTestCase::DoRun (void)
{
Time a, b, c;
c = a + b;
c = a * b;
c = a / Seconds (1.0);
c = a - b;
c += a;
c -= a;
c /= Seconds (1.0);
c *= a;
bool x;
x = a < b;
x = a > b;
x = a <= b;
x = a >= b;
x = a == b;
x = a != b;
//a = 1.0;
//a = 1;
return false;
}
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 (Time::US));
AddTestCase (new ArithTestCase ());
}
} 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',
@@ -65,6 +66,7 @@ def build(bld):
'ns2-calendar-scheduler.cc',
'event-impl.cc',
'simulator.cc',
'simulator-impl.cc',
'default-simulator-impl.cc',
'timer.cc',
'watchdog.cc',
@@ -76,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',
@@ -105,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',