diff --git a/src/simulator/high-precision-cairo.cc b/src/simulator/high-precision-cairo.cc index 217d660f3..37e89efcf 100644 --- a/src/simulator/high-precision-cairo.cc +++ b/src/simulator/high-precision-cairo.cc @@ -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 #include 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" +} diff --git a/src/simulator/high-precision-cairo.h b/src/simulator/high-precision-cairo.h index 7e79f7f0d..8cb533450 100644 --- a/src/simulator/high-precision-cairo.h +++ b/src/simulator/high-precision-cairo.h @@ -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) { diff --git a/src/simulator/high-precision.cc b/src/simulator/high-precision.cc index 257a49e6d..e753e4542 100644 --- a/src/simulator/high-precision.cc +++ b/src/simulator/high-precision.cc @@ -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; diff --git a/src/simulator/nstime.h b/src/simulator/nstime.h index ca4bf0881..61c90b1f8 100644 --- a/src/simulator/nstime.h +++ b/src/simulator/nstime.h @@ -27,37 +27,10 @@ #include #include #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 -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 type. - */ - HighPrecision const &GetHighPrecision (void) const; - HighPrecision * PeekHighPrecision (void); - -private: - HighPrecision m_data; + explicit inline TimeUnit (const HighPrecision &data) + : TimeBase (data) + {} }; -template -TimeUnit::TimeUnit () - : m_data () -{ -} -template -TimeUnit::TimeUnit (TimeUnit const &o) - : m_data (o.m_data) -{ -} -template -TimeUnit -TimeUnit::operator = (TimeUnit const &o) -{ - m_data = o.m_data; - return *this; -} -template -TimeUnit::TimeUnit (HighPrecision data) - : m_data (data) -{ -} - -template -HighPrecision const & -TimeUnit::GetHighPrecision (void) const -{ - return m_data; -} -template -HighPrecision * -TimeUnit::PeekHighPrecision (void) -{ - return &m_data; -} -template -bool -TimeUnit::IsZero (void) const -{ - return m_data.Compare (HighPrecision::Zero ()) == 0; -} -template -bool -TimeUnit::IsNegative (void) const -{ - return m_data.Compare (HighPrecision::Zero ()) <= 0; -} -template -bool -TimeUnit::IsPositive (void) const -{ - return m_data.Compare (HighPrecision::Zero ()) >= 0; -} -template -bool -TimeUnit::IsStrictlyNegative (void) const -{ - return m_data.Compare (HighPrecision::Zero ()) < 0; -} -template -bool -TimeUnit::IsStrictlyPositive (void) const -{ - return m_data.Compare (HighPrecision::Zero ()) > 0; -} - template bool operator == (TimeUnit const &lhs, TimeUnit const &rhs) @@ -347,7 +219,7 @@ TimeUnit Min (TimeUnit const &ta, TimeUnit 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); }; /** diff --git a/src/simulator/time-base.cc b/src/simulator/time-base.cc new file mode 100644 index 000000000..10a9d7a92 --- /dev/null +++ b/src/simulator/time-base.cc @@ -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 diff --git a/src/simulator/time-base.h b/src/simulator/time-base.h new file mode 100644 index 000000000..547d112ed --- /dev/null +++ b/src/simulator/time-base.h @@ -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 + */ +#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 */ diff --git a/src/simulator/time.cc b/src/simulator/time.cc index 437811cfe..08a72eba3 100644 --- a/src/simulator/time.cc +++ b/src/simulator/time.cc @@ -20,7 +20,7 @@ * TimeStep support by Emmanuelle Laprise */ #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; diff --git a/src/simulator/wscript b/src/simulator/wscript index 3cb61ac89..cadfa9330 100644 --- a/src/simulator/wscript +++ b/src/simulator/wscript @@ -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',