]> git.ipfire.org Git - thirdparty/ntp.git/commitdiff
added incomplete test for lfpfunc.c. Changes to makefile and created c_timestructs...
authorDamir Tomic <viperus@ntp.org>
Mon, 8 Jun 2015 08:29:19 +0000 (10:29 +0200)
committerDamir Tomic <viperus@ntp.org>
Mon, 8 Jun 2015 08:29:19 +0000 (10:29 +0200)
bk: 5575525fANFmrmD8NRZAzpGl0iaf9Q

tests/libntp/c_timestructs.c [new file with mode: 0644]
tests/libntp/c_timestructs.h [new file with mode: 0644]
tests/libntp/g_lfpfunc.cpp [moved from tests/libntp/lfpfunc.cpp with 100% similarity]
tests/libntp/lfpfunc.c [new file with mode: 0644]
tests/libntp/run-test-lfpfunc.c [new file with mode: 0644]

diff --git a/tests/libntp/c_timestructs.c b/tests/libntp/c_timestructs.c
new file mode 100644 (file)
index 0000000..889b8c0
--- /dev/null
@@ -0,0 +1,155 @@
+/*
+ * timestructs.cpp -- test bed adaptors for time structs.
+ *
+ * Written by Juergen Perlinger (perlinger@ntp.org) for the NTP project.
+ * The contents of 'html/copyright.html' apply.
+ */
+#include "libntptest.h"
+#include "timestructs.h"
+
+#include "timetoa.h"
+#include "timevalops.h"
+#include "timespecops.h"
+
+
+namespace timeStruct {
+
+std::ostream&
+operator << (std::ostream& os, const timeStruct::l_fp_wrap& val)
+{
+       // raw data formatting
+       os << "0x" << std::hex << val.V.l_ui << ':'
+          << std::setfill('0') << std::setw(8) << val.V.l_uf
+          << std::dec;
+       // human-readable format
+       os << '[' << lfptoa(&val.V, 10) << ']';
+       return os;
+}
+
+std::ostream&
+operator << (std::ostream& os, const timeStruct::timeval_wrap& val)
+{
+       // raw data formatting
+       os << val.V.tv_sec << ':' << val.V.tv_usec;
+       // human-readable format
+       os << '['
+          << format_time_fraction(val.V.tv_sec, val.V.tv_usec, 6)
+          << ']';
+       return os;
+}
+
+std::ostream&
+operator << (std::ostream& os, const timeStruct::timespec_wrap& val)
+{
+       // raw data formatting
+       os << val.V.tv_sec << ':' << val.V.tv_nsec;
+       // human-readable format
+       os << '['
+          << format_time_fraction(val.V.tv_sec, val.V.tv_nsec, 9)
+          << ']';
+       return os;
+}
+
+// Implementation of the l_fp closeness predicate
+
+AssertFpClose::AssertFpClose(
+       u_int32 hi,
+       u_int32 lo
+       )
+{
+       limit.l_ui = hi;
+       limit.l_uf = lo;
+}
+
+::testing::AssertionResult
+AssertFpClose::operator()(
+       const char* m_expr,
+       const char* n_expr,
+       const l_fp & m,
+       const l_fp & n
+       )
+{
+       l_fp diff;
+
+       if (L_ISGEQ(&m, &n)) {
+               diff = m;
+               L_SUB(&diff, &n);
+       } else {
+               diff = n;
+               L_SUB(&diff, &m);
+       }
+       if (L_ISGEQ(&limit, &diff))
+               return ::testing::AssertionSuccess();
+
+       return ::testing::AssertionFailure()
+           << m_expr << " which is " << l_fp_wrap(m)
+           << "\nand\n"
+           << n_expr << " which is " << l_fp_wrap(n)
+           << "\nare not close; diff=" << l_fp_wrap(diff);
+}
+
+// Implementation of the timeval closeness predicate
+
+AssertTimevalClose::AssertTimevalClose(
+       time_t hi,
+       int32  lo
+       )
+{
+       limit.tv_sec = hi;
+       limit.tv_usec = lo;
+}
+
+::testing::AssertionResult
+AssertTimevalClose::operator()(
+       const char* m_expr,
+       const char* n_expr,
+       const struct timeval & m,
+       const struct timeval & n
+       )
+{
+       struct timeval diff;
+
+       diff = abs_tval(sub_tval(m, n));
+       if (cmp_tval(limit, diff) >= 0)
+               return ::testing::AssertionSuccess();
+
+       return ::testing::AssertionFailure()
+           << m_expr << " which is " << timeval_wrap(m)
+           << "\nand\n"
+           << n_expr << " which is " << timeval_wrap(n)
+           << "\nare not close; diff=" << timeval_wrap(diff);
+}
+
+// Implementation of the timespec closeness predicate
+
+AssertTimespecClose::AssertTimespecClose(
+       time_t hi,
+       int32  lo
+       )
+{
+       limit.tv_sec = hi;
+       limit.tv_nsec = lo;
+}
+
+::testing::AssertionResult
+AssertTimespecClose::operator()(
+       const char* m_expr,
+       const char* n_expr,
+       const struct timespec & m,
+       const struct timespec & n
+       )
+{
+       struct timespec diff;
+
+       diff = abs_tspec(sub_tspec(m, n));
+       if (cmp_tspec(limit, diff) >= 0)
+               return ::testing::AssertionSuccess();
+
+       return ::testing::AssertionFailure()
+           << m_expr << " which is " << timespec_wrap(m)
+           << "\nand\n"
+           << n_expr << " which is " << timespec_wrap(n)
+           << "\nare not close; diff=" << timespec_wrap(diff);
+}
+
+} // namespace timeStruct
diff --git a/tests/libntp/c_timestructs.h b/tests/libntp/c_timestructs.h
new file mode 100644 (file)
index 0000000..8cd5927
--- /dev/null
@@ -0,0 +1,213 @@
+/*
+ * timestructs.h -- test bed adaptors for time structs.
+ *
+ * Written by Juergen Perlinger (perlinger@ntp.org) for the NTP project.
+ * The contents of 'html/copyright.html' apply.
+ *
+ * Some wrapper classes and a closeness predicate that are used to
+ * bridge the gap between the goggletest framework and the structs used
+ * for representing time stamps (l_fp, struct timeval, struct timespec).
+ *
+ * Some ostream conversion operators are provided to give diagnostic
+ * output on errors. The normal string conversion functions will give
+ * HRVs (human readable values) but we might also be interested in the
+ * machine representation for diagnostic purposes.
+ */
+
+
+//#ifndef TESTCALSHIMS_H
+#include "testcalshims.h"
+//#endif
+
+
+#ifndef TIMESTRUCTS_H
+#define TIMESTRUCTS_H
+
+#include "ntp_fp.h"
+
+
+l_fp test ; 
+//namespace timeStruct {
+
+// wrap a l_fp struct with common operations
+struct l_fp_wrap {
+  
+       l_fp V;
+       
+       
+       //bool operator == (const l_fp_wrap& rhs) const
+       //      { return L_ISEQU(&V, &rhs.V); }
+
+//---------------THIS HAS TO BE MANUALLY CONVERTED IN CODE!!!
+       //operator l_fp* () 
+       //      { return &V; }
+       //operator l_fp& ()
+       //      { return V; }
+//-----------------------------------------
+/*
+       l_fp_wrap & operator = (const l_fp_wrap& rhs)
+               { V = rhs.V; return *this; }
+       l_fp_wrap& operator = (const l_fp& rhs)
+               { V = rhs; return *this; }
+*/
+};
+
+l_fp_wrap(l_fp V){
+       ZERO(V); }
+l_fp_wrap(l_fp V, u_int32 hi, u_int32 lo){
+       V.l_ui = hi; V.l_uf = lo; }
+l_fp_wrap(l_fp V, const l_fp &rhs){
+       V = rhs; }
+
+l_fp_wrap_equals(const l_fp_wrap& current, const l_fp_wrap& rhs) const // operator ==
+       { return L_ISEQU(&current.V, &rhs.V); }
+
+l_fp_wrap_and(const l_fp_wrap& current, const l_fp_wrap& rhs){
+       V = rhs.V; 
+       return current//*this;
+}
+l_fp_wrap_and(const l_fp& current, const l_fp& rhs){ 
+       V = rhs; 
+       return current//*this; 
+}
+
+       
+
+// wrap a 'struct timeval' with common operations
+struct timeval_wrap {
+
+       struct timeval V;
+
+       timeval_wrap()
+               { ZERO(V); }
+       timeval_wrap(time_t hi, long lo)
+               { V.tv_sec = hi; V.tv_usec = lo; }
+       timeval_wrap(const struct timeval & rhs)
+               { V = rhs; }
+       timeval_wrap(const timeval_wrap & rhs)
+               { V = rhs.V; }
+       bool operator == (const timeval_wrap& rhs) const
+               { return V.tv_sec == rhs.V.tv_sec &&
+                        V.tv_usec == rhs.V.tv_usec ; }
+       bool valid() const
+               { return V.tv_usec >= 0 && V.tv_usec < 1000000; }
+       operator struct timeval* () 
+               { return &V; }
+       operator struct timeval& ()
+               { return V; }
+       timeval_wrap& operator = (const timeval_wrap& rhs)
+               { V = rhs.V; return *this; }
+       timeval_wrap& operator = (const struct timeval& rhs)
+               { V = rhs; return *this; }
+};
+
+// wrap a 'struct timespec' with common operations
+struct timespec_wrap {
+
+       struct timespec V;
+
+       timespec_wrap()
+               { ZERO(V); }
+       timespec_wrap(time_t hi, long lo)
+               { V.tv_sec = hi; V.tv_nsec = lo; }
+       timespec_wrap(const struct timespec & rhs)
+               { V = rhs; }
+       timespec_wrap(const timespec_wrap & rhs)
+               { V = rhs.V; }
+       bool operator == (const timespec_wrap& rhs) const
+               { return V.tv_sec == rhs.V.tv_sec &&
+                        V.tv_nsec == rhs.V.tv_nsec ; }
+       bool valid() const
+               { return V.tv_nsec >= 0 && V.tv_nsec < 1000000000; }
+       operator struct timespec* () 
+               { return &V; }
+       operator struct timespec& ()
+               { return V;     }
+       timespec_wrap& operator = (const timespec_wrap& rhs)
+               { V = rhs.V; return *this; }
+       timespec_wrap& operator = (const struct timespec& rhs)
+               { V = rhs; return *this; }
+};
+
+// l_fp closeness testing predicate
+//
+// This predicate is used for the closeness ('near') testing of l_fp
+// values. Once constructed with a limit, it can be used to check the
+// absolute difference of two l_fp structs against that limit; if the
+// difference is less or equal to this limit, the test passes.
+class AssertFpClose {
+private:
+       l_fp limit;
+
+public:
+       AssertFpClose(u_int32 hi, u_int32 lo);
+
+       ::testing::AssertionResult
+       operator()(const char* m_expr, const char* n_expr,
+                  const l_fp & m, const l_fp & n);
+};
+
+
+// timeval closeness testing predicate
+//
+// CAVEAT: This class uses the timevalops functions
+// - sub_tval
+// - abs_tval
+// - cmp_tval
+//
+// This creates a dependency loop of sorts. The loop is defused by the
+// fact that these basic operations can be tested by exact value tests,
+// so once the basic timeval operations passed it's safe to use this
+// predicate.
+class AssertTimevalClose {
+private:
+       struct timeval limit;
+
+public:
+       // note: (hi,lo) should be a positive normalised timeval;
+       // the constructor does not normalise the values!
+       AssertTimevalClose(time_t hi, int32 lo);
+
+       ::testing::AssertionResult
+       operator()(const char* m_expr, const char* n_expr,
+                  const struct timeval & m, const struct timeval & n);
+};
+
+
+// timespec closeness testing predicate
+//
+// CAVEAT: This class uses the timespecops functions
+// - sub_tspec
+// - abs_tspec
+// - cmp_tspec
+//
+// See the equivalent timeval helper.
+class AssertTimespecClose {
+private:
+       struct timespec limit;
+
+public:
+       // note: (hi,lo) should be a positive normalised timespec;
+       // the constructor does not normalise the values!
+       AssertTimespecClose(time_t hi, int32 lo);
+
+       ::testing::AssertionResult
+       operator()(const char* m_expr, const char* n_expr,
+                  const struct timespec & m, const struct timespec & n);
+};
+
+
+// since googletest wants to string format items, we declare the
+// necessary operators. Since all adaptors have only public members
+// there is need for friend declarations anywhere.
+
+extern std::ostream& operator << (std::ostream& os,
+                                 const timeStruct::l_fp_wrap& val);
+extern std::ostream& operator << (std::ostream& os,
+                                 const timeStruct::timeval_wrap& val);
+extern std::ostream& operator << (std::ostream& os,
+                                 const timeStruct::timespec_wrap& val);
+
+//} // namespace timeStruct
+
+#endif // TIMESTRUCTS_H
diff --git a/tests/libntp/lfpfunc.c b/tests/libntp/lfpfunc.c
new file mode 100644 (file)
index 0000000..573e53e
--- /dev/null
@@ -0,0 +1,643 @@
+//#include "c_timestructs.h"
+
+#include "testcalshims.h"
+#include "unity.h"
+
+//#include "libntptest.h"
+//#include "timestructs.h"
+
+
+
+#include "ntp_fp.h"
+
+
+#include <float.h>
+#include <math.h>
+
+#include <string.h> //
+//#include <sstream>
+
+
+typedef struct  {
+       uint32_t h, l;
+} lfp_hl;
+
+
+/*
+//----------------------------------------------------------------------
+// OO-wrapper for 'l_fp'
+//----------------------------------------------------------------------
+
+class LFP
+{
+public:
+       ~LFP();
+       LFP();
+       LFP(const LFP& rhs);
+       LFP(int32 i, u_int32 f);
+
+       LFP  operator+ (const LFP &rhs) const;
+       LFP& operator+=(const LFP &rhs);
+
+       LFP  operator- (const LFP &rhs) const;
+       LFP& operator-=(const LFP &rhs);
+
+       LFP& operator=(const LFP &rhs);
+       LFP  operator-() const;
+
+       bool operator==(const LFP &rhs) const;
+
+       LFP  neg() const;
+       LFP  abs() const;
+       int  signum() const;
+
+       bool l_isgt (const LFP &rhs) const
+               { return L_ISGT(&_v, &rhs._v); }
+       bool l_isgtu(const LFP &rhs) const
+               { return L_ISGTU(&_v, &rhs._v); }
+       bool l_ishis(const LFP &rhs) const
+               { return L_ISHIS(&_v, &rhs._v); }
+       bool l_isgeq(const LFP &rhs) const
+               { return L_ISGEQ(&_v, &rhs._v); }
+       bool l_isequ(const LFP &rhs) const
+               { return L_ISEQU(&_v, &rhs._v); }
+
+       int  ucmp(const LFP & rhs) const;
+       int  scmp(const LFP & rhs) const;
+       
+       std::string   toString() const;
+       std::ostream& toStream(std::ostream &oo) const;
+       
+       operator double() const;
+       explicit LFP(double);
+       
+protected:
+       LFP(const l_fp &rhs);
+
+       static int cmp_work(u_int32 a[3], u_int32 b[3]);
+       
+       l_fp _v;
+};
+       
+static std::ostream& operator<<(std::ostream &oo, const LFP& rhs)
+{
+       return rhs.toStream(oo);
+}
+
+//----------------------------------------------------------------------
+// reference comparision
+// This is implementad as a full signed MP-subtract in 3 limbs, where
+// the operands are zero or sign extended before the subtraction is
+// executed.
+//----------------------------------------------------------------------
+int  LFP::scmp(const LFP & rhs) const
+{
+       u_int32 a[3], b[3];
+       const l_fp &op1(_v), &op2(rhs._v);
+       
+       a[0] = op1.l_uf; a[1] = op1.l_ui; a[2] = 0;
+       b[0] = op2.l_uf; b[1] = op2.l_ui; b[2] = 0;
+
+       a[2] -= (op1.l_i < 0);
+       b[2] -= (op2.l_i < 0);
+
+       return cmp_work(a,b);
+}
+
+int  LFP::ucmp(const LFP & rhs) const
+{
+       u_int32 a[3], b[3];
+       const l_fp &op1(_v), &op2(rhs._v);
+       
+       a[0] = op1.l_uf; a[1] = op1.l_ui; a[2] = 0;
+       b[0] = op2.l_uf; b[1] = op2.l_ui; b[2] = 0;
+
+       return cmp_work(a,b);
+}
+
+int LFP::cmp_work(u_int32 a[3], u_int32 b[3])
+{
+       u_int32 cy, idx, tmp;
+       for (cy = idx = 0; idx < 3; ++idx) {
+               tmp = a[idx]; cy  = (a[idx] -=   cy  ) > tmp;
+               tmp = a[idx]; cy |= (a[idx] -= b[idx]) > tmp;
+       }
+       if (a[2])
+               return -1;
+       return a[0] || a[1];
+}
+
+//----------------------------------------------------------------------
+// imlementation of the LFP stuff
+// This should be easy enough...
+//----------------------------------------------------------------------
+
+LFP::~LFP()
+{
+       // NOP
+}
+
+LFP::LFP()
+{
+       _v.l_ui = 0;
+       _v.l_uf = 0;
+}
+
+LFP::LFP(int32 i, u_int32 f)
+{
+       _v.l_i  = i;
+       _v.l_uf = f;
+}
+
+LFP::LFP(const LFP &rhs)
+{
+       _v = rhs._v;
+}
+
+LFP::LFP(const l_fp & rhs)
+{
+       _v = rhs;
+}
+
+LFP& LFP::operator=(const LFP & rhs)
+{
+       _v = rhs._v;
+       return *this;
+}
+
+LFP& LFP::operator+=(const LFP & rhs)
+{
+       L_ADD(&_v, &rhs._v);
+       return *this;
+}
+
+LFP& LFP::operator-=(const LFP & rhs)
+{
+       L_SUB(&_v, &rhs._v);
+       return *this;
+}
+
+LFP LFP::operator+(const LFP &rhs) const
+{
+       LFP tmp(*this);
+       return tmp += rhs;
+}
+
+LFP LFP::operator-(const LFP &rhs) const
+{
+       LFP tmp(*this);
+       return tmp -= rhs;
+}
+
+LFP LFP::operator-() const
+{
+       LFP tmp(*this);
+       L_NEG(&tmp._v);
+       return tmp;
+}
+
+LFP
+LFP::neg() const
+{
+       LFP tmp(*this);
+       L_NEG(&tmp._v);
+       return tmp;
+}
+
+LFP
+LFP::abs() const
+{
+       LFP tmp(*this);
+       if (L_ISNEG(&tmp._v))
+               L_NEG(&tmp._v);
+       return tmp;
+}
+
+int
+LFP::signum() const
+{
+       if (_v.l_ui & 0x80000000u)
+               return -1;
+       return (_v.l_ui || _v.l_uf);
+}
+
+std::string
+LFP::toString() const
+{
+       std::ostringstream oss;
+       toStream(oss);
+       return oss.str();
+}
+
+std::ostream&
+LFP::toStream(std::ostream &os) const
+{
+       return os
+           << mfptoa(_v.l_ui, _v.l_uf, 9)
+           << " [$" << std::setw(8) << std::setfill('0') << std::hex << _v.l_ui
+           <<  ':'  << std::setw(8) << std::setfill('0') << std::hex << _v.l_uf
+           << ']';
+}
+
+bool LFP::operator==(const LFP &rhs) const
+{
+       return L_ISEQU(&_v, &rhs._v);
+}
+
+
+LFP::operator double() const
+{
+       double res;
+       LFPTOD(&_v, res);
+       return res;
+}
+
+LFP::LFP(double rhs)
+{
+       DTOLFP(rhs, &_v);
+}
+
+
+//----------------------------------------------------------------------
+// testing the relational macros works better with proper predicate
+// formatting functions; it slows down the tests a bit, but makes for
+// readable failure messages.
+//----------------------------------------------------------------------
+
+testing::AssertionResult isgt_p(
+       const LFP &op1, const LFP &op2)
+{
+       if (op1.l_isgt(op2))
+               return testing::AssertionSuccess()
+                   << "L_ISGT(" << op1 << "," << op2 << ") is true";
+       else
+               return testing::AssertionFailure()
+                   << "L_ISGT(" << op1 << "," << op2 << ") is false";
+}
+
+testing::AssertionResult isgeq_p(
+       const LFP &op1, const LFP &op2)
+{
+       if (op1.l_isgeq(op2))
+               return testing::AssertionSuccess()
+                   << "L_ISGEQ(" << op1 << "," << op2 << ") is true";
+       else
+               return testing::AssertionFailure()
+                   << "L_ISGEQ(" << op1 << "," << op2 << ") is false";
+}
+
+testing::AssertionResult isgtu_p(
+       const LFP &op1, const LFP &op2)
+{
+       if (op1.l_isgtu(op2))
+               return testing::AssertionSuccess()
+                   << "L_ISGTU(" << op1 << "," << op2 << ") is true";
+       else
+               return testing::AssertionFailure()
+                   << "L_ISGTU(" << op1 << "," << op2 << ") is false";
+}
+
+testing::AssertionResult ishis_p(
+       const LFP &op1, const LFP &op2)
+{
+       if (op1.l_ishis(op2))
+               return testing::AssertionSuccess()
+                   << "L_ISHIS(" << op1 << "," << op2 << ") is true";
+       else
+               return testing::AssertionFailure()
+                   << "L_ISHIS(" << op1 << "," << op2 << ") is false";
+}
+
+testing::AssertionResult isequ_p(
+       const LFP &op1, const LFP &op2)
+{
+       if (op1.l_isequ(op2))
+               return testing::AssertionSuccess()
+                   << "L_ISEQU(" << op1 << "," << op2 << ") is true";
+       else
+               return testing::AssertionFailure()
+                   << "L_ISEQU(" << op1 << "," << op2 << ") is false";
+}
+
+*/
+
+//----------------------------------------------------------------------
+// test data table for add/sub and compare
+//----------------------------------------------------------------------
+
+
+static const lfp_hl addsub_tab[][3] = {
+       // trivial idendity:
+       {{0 ,0         }, { 0,0         }, { 0,0}},
+       // with carry from fraction and sign change:
+       {{-1,0x80000000}, { 0,0x80000000}, { 0,0}},
+       // without carry from fraction
+       {{ 1,0x40000000}, { 1,0x40000000}, { 2,0x80000000}},
+       // with carry from fraction:
+       {{ 1,0xC0000000}, { 1,0xC0000000}, { 3,0x80000000}},
+       // with carry from fraction and sign change:
+       {{0x7FFFFFFF, 0x7FFFFFFF}, {0x7FFFFFFF,0x7FFFFFFF}, {0xFFFFFFFE,0xFFFFFFFE}},
+       // two tests w/o carry (used for l_fp<-->double):
+       {{0x55555555,0xAAAAAAAA}, {0x11111111,0x11111111}, {0x66666666,0xBBBBBBBB}},
+       {{0x55555555,0x55555555}, {0x11111111,0x11111111}, {0x66666666,0x66666666}},
+       // wide-range test, triggers compare trouble
+       {{0x80000000,0x00000001}, {0xFFFFFFFF,0xFFFFFFFE}, {0x7FFFFFFF,0xFFFFFFFF}}
+};
+static const size_t addsub_cnt = (sizeof(addsub_tab)/sizeof(addsub_tab[0]));
+static const size_t addsub_tot = (sizeof(addsub_tab)/sizeof(addsub_tab[0][0]));
+
+
+
+//----------------------------------------------------------------------
+// epsilon estimation for the precision of a conversion double --> l_fp
+//
+// The error estimation limit is as follows:
+//  * The 'l_fp' fixed point fraction has 32 bits precision, so we allow
+//    for the LSB to toggle by clamping the epsilon to be at least 2^(-31)
+//
+//  * The double mantissa has a precsion 54 bits, so the other minimum is
+//    dval * (2^(-53))
+//
+//  The maximum of those two boundaries is used for the check.
+//
+// Note: once there are more than 54 bits between the highest and lowest
+// '1'-bit of the l_fp value, the roundtrip *will* create truncation
+// errors. This is an inherent property caused by the 54-bit mantissa of
+// the 'double' type.
+double eps(double d)
+{
+       return fmax(ldexp(1.0, -31), ldexp(fabs(d), -53)); //max<double>
+}
+
+//Y U NO OVERLOAD, C XD
+/*
+l_fp l_fp_init()
+{
+       l_fp temp;
+       temp.l_ui = 0;
+       temp.l_uf = 0;
+       return temp;
+}
+*/
+
+l_fp l_fp_init(int32 i, u_int32 f)
+{
+       l_fp temp;
+       temp.l_i  = i;
+       temp.l_uf = f;
+
+       return temp;
+}
+
+
+
+l_fp l_fp_add(const l_fp first, const l_fp second) //&rhs!!!
+{
+       l_fp temp;
+       //LFP tmp(*this);
+       //return tmp += rhs;
+       temp = first;
+       L_ADD(&temp, &second);
+       //return first + second;
+       return temp;
+}
+
+l_fp l_fp_subtract(const l_fp first, const l_fp second) //&rhs!!!
+{
+       l_fp temp;
+       //LFP tmp(*this);
+       //return tmp += rhs;
+       temp = first;
+       L_SUB(&temp, &second);
+       
+       return temp;
+}
+
+l_fp l_fp_negate(const l_fp first)
+{
+       l_fp temp;
+       //LFP tmp(*this);
+       //return tmp += rhs;
+       temp = first;
+       L_NEG(&temp);
+       
+       return temp;
+}
+/*
+//negation!
+LFP LFP::operator-() const
+{
+       LFP tmp(*this);
+       L_NEG(&tmp._v);
+       return tmp;
+}
+*/
+//----------------------------------------------------------------------
+// test addition
+//----------------------------------------------------------------------
+void test_AdditionLR() {
+       
+       size_t idx=0;
+       for (idx=0; idx < addsub_cnt; ++idx) {
+
+
+               l_fp op1 = l_fp_init(addsub_tab[idx][0].h, addsub_tab[idx][0].l);
+               //LFP op1(addsub_tab[idx][0].h, addsub_tab[idx][0].l);
+               l_fp op2 = l_fp_init(addsub_tab[idx][1].h, addsub_tab[idx][1].l);
+               //LFP exp(addsub_tab[idx][2].h, addsub_tab[idx][2].l);
+               l_fp exp = l_fp_init(addsub_tab[idx][2].h, addsub_tab[idx][2].l);
+               //LFP res(op1 + op2);
+               l_fp res = l_fp_add(op1,op2);           
+
+               TEST_ASSERT_EQUAL_MEMORY(&exp, &res,sizeof(exp));
+       }       
+}
+
+void test_AdditionRL() {
+
+       size_t idx=0;
+       for (idx=0; idx < addsub_cnt; ++idx) {
+               l_fp op2 = l_fp_init(addsub_tab[idx][0].h, addsub_tab[idx][0].l);
+               l_fp op1 = l_fp_init(addsub_tab[idx][1].h, addsub_tab[idx][1].l);
+               l_fp exp = l_fp_init(addsub_tab[idx][2].h, addsub_tab[idx][2].l);
+               l_fp res = l_fp_add(op1,op2);
+
+               TEST_ASSERT_EQUAL_MEMORY(&exp, &res,sizeof(exp));
+       }       
+}
+
+
+
+//----------------------------------------------------------------------
+// test subtraction
+//----------------------------------------------------------------------
+void test_SubtractionLR() {
+
+       size_t idx=0;
+       for (idx=0; idx < addsub_cnt; ++idx) {
+               l_fp op2 = l_fp_init(addsub_tab[idx][0].h, addsub_tab[idx][0].l);
+               l_fp exp = l_fp_init(addsub_tab[idx][1].h, addsub_tab[idx][1].l);
+               l_fp op1 = l_fp_init(addsub_tab[idx][2].h, addsub_tab[idx][2].l);
+               l_fp res = l_fp_subtract(op1,op2);
+               //LFP res(op1 - op2);
+                               
+               TEST_ASSERT_EQUAL_MEMORY(&exp, &res,sizeof(exp));
+       }       
+}
+
+void test_SubtractionRL() {
+
+       size_t idx=0;
+       for (idx=0; idx < addsub_cnt; ++idx) {
+               l_fp exp = l_fp_init(addsub_tab[idx][0].h, addsub_tab[idx][0].l);
+               l_fp op2 = l_fp_init(addsub_tab[idx][1].h, addsub_tab[idx][1].l);
+               l_fp op1 = l_fp_init(addsub_tab[idx][2].h, addsub_tab[idx][2].l);
+               l_fp res = l_fp_subtract(op1,op2);
+
+               TEST_ASSERT_EQUAL_MEMORY(&exp, &res,sizeof(exp));
+       }       
+}
+
+//----------------------------------------------------------------------
+// test negation
+//----------------------------------------------------------------------
+
+void test_Negation() {
+
+       size_t idx=0;
+       for (idx=0; idx < addsub_cnt; ++idx) {
+               l_fp op1 = l_fp_init(addsub_tab[idx][0].h, addsub_tab[idx][0].l);
+               l_fp op2 = l_fp_negate(op1);
+               l_fp sum = l_fp_add(op1, op2);
+               
+               l_fp zero = l_fp_init(0,0);
+               TEST_ASSERT_EQUAL_MEMORY(&zero, &sum,sizeof(sum));
+       
+       }       
+}
+
+/*
+
+//----------------------------------------------------------------------
+// test absolute value
+//----------------------------------------------------------------------
+void test_Absolute() {
+       for (size_t idx=0; idx < addsub_cnt; ++idx) {
+               LFP op1(addsub_tab[idx][0].h, addsub_tab[idx][0].l);
+               LFP op2(op1.abs());
+
+               TEST_ASSERT_TRUE(op2.signum() >= 0);
+
+               if (op1.signum() >= 0)
+                       op1 -= op2;
+               else
+                       op1 += op2;
+               TEST_ASSERT_EQUAL(LFP(0,0), op1);
+       }
+
+       // There is one special case we have to check: the minimum
+       // value cannot be negated, or, to be more precise, the
+       // negation reproduces the original pattern.
+       LFP minVal(0x80000000, 0x00000000);
+       LFP minAbs(minVal.abs());
+       TEST_ASSERT_EQUAL(-1, minVal.signum());
+       TEST_ASSERT_EQUAL(minVal, minAbs);
+}
+
+//----------------------------------------------------------------------
+// fp -> double -> fp rountrip test
+//----------------------------------------------------------------------
+void test_FDF_RoundTrip() {
+       // since a l_fp has 64 bits in it's mantissa and a double has
+       // only 54 bits available (including the hidden '1') we have to
+       // make a few concessions on the roundtrip precision. The 'eps()'
+       // function makes an educated guess about the avilable precision
+       // and checks the difference in the two 'l_fp' values against
+       // that limit.
+       for (size_t idx=0; idx < addsub_cnt; ++idx) {
+               LFP    op1(addsub_tab[idx][0].h, addsub_tab[idx][0].l);
+               double op2(op1);
+               LFP    op3(op2);
+               // for manual checks only:
+               // std::cout << std::setprecision(16) << op2 << std::endl;
+               ASSERT_LE(fabs(op1-op3), eps(op2));
+       }       
+}
+
+//----------------------------------------------------------------------
+// test the compare stuff
+//
+// This uses the local compare and checks if the operations using the
+// macros in 'ntp_fp.h' produce mathing results.
+// ----------------------------------------------------------------------
+void test_SignedRelOps() {
+       const lfp_hl * tv(&addsub_tab[0][0]);
+       for (size_t lc=addsub_tot-1; lc; --lc,++tv) {
+               LFP op1(tv[0].h,tv[0].l);
+               LFP op2(tv[1].h,tv[1].l);
+               int cmp(op1.scmp(op2));
+
+               switch (cmp) {
+               case -1:
+                       std::swap(op1, op2);
+               case 1:
+                       TEST_ASSERT_TRUE (isgt_p(op1,op2));
+                       TEST_ASSERT_FALSE(isgt_p(op2,op1));
+
+                       TEST_ASSERT_TRUE (isgeq_p(op1,op2));
+                       TEST_ASSERT_FALSE(isgeq_p(op2,op1));
+
+                       TEST_ASSERT_FALSE(isequ_p(op1,op2));
+                       TEST_ASSERT_FALSE(isequ_p(op2,op1));
+                       break;
+               case 0:
+                       TEST_ASSERT_FALSE(isgt_p(op1,op2));
+                       TEST_ASSERT_FALSE(isgt_p(op2,op1));
+
+                       TEST_ASSERT_TRUE (isgeq_p(op1,op2));
+                       TEST_ASSERT_TRUE (isgeq_p(op2,op1));
+
+                       TEST_ASSERT_TRUE (isequ_p(op1,op2));
+                       TEST_ASSERT_TRUE (isequ_p(op2,op1));
+                       break;
+               default:
+                       FAIL() << "unexpected SCMP result: " << cmp;
+               }
+       }
+}
+
+void test_UnsignedRelOps() {
+       const lfp_hl * tv(&addsub_tab[0][0]);
+       for (size_t lc=addsub_tot-1; lc; --lc,++tv) {
+               LFP op1(tv[0].h,tv[0].l);
+               LFP op2(tv[1].h,tv[1].l);
+               int cmp(op1.ucmp(op2));
+
+               switch (cmp) {
+               case -1:
+                       std::swap(op1, op2);
+               case 1:
+                       TEST_ASSERT_TRUE (isgtu_p(op1,op2));
+                       TEST_ASSERT_FALSE(isgtu_p(op2,op1));
+
+                       TEST_ASSERT_TRUE (ishis_p(op1,op2));
+                       TEST_ASSERT_FALSE(ishis_p(op2,op1));
+                       break;
+               case 0:
+                       TEST_ASSERT_FALSE(isgtu_p(op1,op2));
+                       TEST_ASSERT_FALSE(isgtu_p(op2,op1));
+
+                       TEST_ASSERT_TRUE (ishis_p(op1,op2));
+                       TEST_ASSERT_TRUE (ishis_p(op2,op1));
+                       break;
+               default:
+                       FAIL() << "unexpected UCMP result: " << cmp;
+               }
+       }
+}
+
+*/
+
+//----------------------------------------------------------------------
+// that's all folks... but feel free to add things!
+//----------------------------------------------------------------------
diff --git a/tests/libntp/run-test-lfpfunc.c b/tests/libntp/run-test-lfpfunc.c
new file mode 100644 (file)
index 0000000..d4a78f7
--- /dev/null
@@ -0,0 +1,56 @@
+/* AUTOGENERATED FILE. DO NOT EDIT. */
+
+//=======Test Runner Used To Run Each Test Below=====
+#define RUN_TEST(TestFunc, TestLineNum) \
+{ \
+  Unity.CurrentTestName = #TestFunc; \
+  Unity.CurrentTestLineNumber = TestLineNum; \
+  Unity.NumberOfTests++; \
+  if (TEST_PROTECT()) \
+  { \
+      setUp(); \
+      TestFunc(); \
+  } \
+  if (TEST_PROTECT() && !TEST_IS_IGNORED) \
+  { \
+    tearDown(); \
+  } \
+  UnityConcludeTest(); \
+}
+
+//=======Automagically Detected Files To Include=====
+#include "unity.h"
+#include <setjmp.h>
+#include <stdio.h>
+
+//=======External Functions This Runner Calls=====
+extern void setUp(void);
+extern void tearDown(void);
+extern void test_AdditionLR();
+extern void test_AdditionRL();
+extern void test_SubtractionLR();
+extern void test_SubtractionRL();
+extern void test_Negation();
+
+
+//=======Test Reset Option=====
+void resetTest()
+{
+  tearDown();
+  setUp();
+}
+
+
+//=======MAIN=====
+int main(void)
+{
+  Unity.TestFile = "lfpfunc.c";
+  UnityBegin("lfpfunc.c");
+  RUN_TEST(test_AdditionLR, 438);
+  RUN_TEST(test_AdditionRL, 456);
+  RUN_TEST(test_SubtractionLR, 474);
+  RUN_TEST(test_SubtractionRL, 488);
+  RUN_TEST(test_Negation, 505);
+
+  return (UnityEnd());
+}