implement division by scalar for Time and container ops
authorPhilipp Gesang <philipp.gesang@intra2net.com>
Tue, 30 Jan 2018 16:00:07 +0000 (17:00 +0100)
committerThomas Jarosch <thomas.jarosch@intra2net.com>
Wed, 27 Mar 2019 09:31:38 +0000 (10:31 +0100)
Add division by integer plus related operations over containers.

As per C++ convention the division functions themselves will not
attempt to handle the case when zero is passed as the divisor.
The functions operating on containers will simply return a zero-
initialized Time object if the argument container is empty.

src/timefunc.cpp
src/timefunc.hxx
test/test_timefunc.cpp

index b84acb8..8fe5000 100644 (file)
@@ -1112,7 +1112,7 @@ namespace clock {
     }
 
     Time &
-    Time::scale (const time_t factor) NOEXCEPT
+    Time::scale (const int64_t factor) NOEXCEPT
     {
         this->value.tv_sec  *= factor;
         this->value.tv_nsec *= factor;
@@ -1122,6 +1122,32 @@ namespace clock {
         return *this;
     }
 
+    /*
+     * Below division code purposely does not attempt to handle divide-
+     * by-zero just as any other C++ division function does. It is up to
+     * the caller to ensure that the divisor is not zero.
+     */
+    Time &
+    Time::divide (const int64_t divisor) NOEXCEPT
+    {
+        const long   sec  = static_cast<long>    (this->value.tv_sec );
+        int64_t      nsec = static_cast<int64_t> (this->value.tv_nsec);
+        const ldiv_t div  = ldiv (sec, divisor);
+
+        if (div.rem != 0) {
+            nsec += div.rem * TIME_CONST_FACTOR_NANO;
+        }
+
+        nsec /= divisor;
+
+        this->value.tv_sec  = static_cast<time_t> (div.quot);
+        this->value.tv_nsec = static_cast<long>   (nsec);
+
+        this->carry_nsec ();
+
+        return *this;
+    }
+
     boost::optional<std::string>
     Time::format_iso8601 (const bool utc,
                           const bool date,
index 43c5f20..4b4e3c9 100644 (file)
@@ -30,6 +30,7 @@ on this file might be covered by the GNU General Public License.
 #include <errno.h>
 #include <list>
 #include <string>
+#include <cstdlib>
 
 #include <boost/optional.hpp>
 
@@ -445,7 +446,9 @@ namespace clock {
             inline Time &subtract (const time_t t2) NOEXCEPT
             { return this->subtract (t2, 0L); };
 
-            Time &scale (const time_t factor) NOEXCEPT;
+            Time &scale (const int64_t factor) NOEXCEPT;
+
+            Time &divide (const int64_t divisor) NOEXCEPT;
 
             friend int compare (const Time &t1, const Time &t2) NOEXCEPT;
 
@@ -489,13 +492,21 @@ namespace clock {
             { return this->subtract (t2); }
 
             inline Time
-            operator* (const time_t factor) const NOEXCEPT
+            operator* (const int64_t factor) const NOEXCEPT
             { return Time (*this).scale (factor); }
 
             inline Time &
-            operator*= (const time_t factor) NOEXCEPT
+            operator*= (const int64_t factor) NOEXCEPT
             { return this->scale (factor); }
 
+            inline Time
+            operator/ (const int64_t divisor) const NOEXCEPT
+            { return Time (*this).divide (divisor); }
+
+            inline Time &
+            operator/= (const int64_t divisor) NOEXCEPT
+            { return this->divide (divisor); }
+
             friend CONSTEXPR bool
             operator== (const Time &t1, const Time &t2) NOEXCEPT;
 
@@ -531,6 +542,10 @@ namespace clock {
     operator- (const time_t t1, const Time &t2) NOEXCEPT
     { return Time (t1) - t2; }
 
+    inline Time
+    operator* (const time_t t1, const Time &t2) NOEXCEPT
+    { return t2 * t1; }
+
     int compare (const Time &t1, const Time &t2) NOEXCEPT;
 
     /*
@@ -591,6 +606,43 @@ namespace clock {
                      const enum type::id       id   = type::real,
                      const enum type::variant  var  = type::dflt) NOEXCEPT;
 
+    template <typename ContT>
+    Time
+    mean (const ContT &data)
+    {
+        Time sum (0, 0);
+
+        if (data.size () == 0) {
+            return sum;
+        }
+
+        for (typename ContT::const_iterator it = data.begin ();
+             it != data.end (); ++it)
+        {
+            sum += *it;
+        };
+
+        return sum.divide (static_cast<int64_t> (data.size ()));
+    };
+
+    template <typename ContT>
+    Time
+    median (const ContT &data)
+    {
+        if (data.size () == 0) {
+            return zero ();
+        }
+        if (data.size () == 1) {
+            return *data.begin ();
+        }
+
+        std::vector<typename ContT::value_type> sorted;
+        std::copy (data.begin (), data.end (), std::back_inserter (sorted));
+        std::sort (sorted.begin (), sorted.end ());
+
+        return sorted [data.size () / 2];
+    };
+
 } /* [namespace clock] */
 
 } /* [namespace I2n] */
index 08ac8ef..9217261 100644 (file)
@@ -966,37 +966,6 @@ BOOST_AUTO_TEST_SUITE(Clock)
         BOOST_CHECK_GT( t2, *t1);
     }
 
-    BOOST_AUTO_TEST_CASE(containers_list)
-    {
-        std::list<I2n::clock::Time> ts;
-
-        ts.push_back (I2n::clock::zero ());
-        ts.push_back (I2n::clock::zero ());
-
-        BOOST_CHECK_EQUAL(ts.size (), 2);
-    }
-
-    BOOST_AUTO_TEST_CASE(containers_vec)
-    {
-        std::vector<I2n::clock::Time> ts;
-
-        ts.push_back (I2n::clock::zero ());
-        ts.push_back (I2n::clock::zero ());
-
-        BOOST_CHECK_EQUAL(ts.size (), 2);
-    }
-
-    BOOST_AUTO_TEST_CASE(containers_set)
-    {
-        std::set<I2n::clock::Time> ts;
-
-        ts.insert (I2n::clock::zero ());
-        ts.insert (I2n::clock::Time (42, 2187));
-        ts.insert (I2n::clock::zero ());
-
-        BOOST_CHECK_EQUAL(ts.size (), 2);
-    }
-
     BOOST_AUTO_TEST_CASE(FormatISO8601_T)
     {
         I2n::clock::Time t (42, 42);
@@ -1107,6 +1076,146 @@ BOOST_AUTO_TEST_SUITE(Clock)
         BOOST_CHECK_EQUAL(*t3->format_iso8601 (true, false,  true, false), in3);
     }
 
+    BOOST_AUTO_TEST_CASE(containers_list)
+    {
+        std::list<I2n::clock::Time> ts;
+
+        ts.push_back (I2n::clock::zero ());
+        ts.push_back (I2n::clock::zero ());
+
+        BOOST_CHECK_EQUAL(ts.size (), 2);
+    }
+
+    BOOST_AUTO_TEST_CASE(containers_vec)
+    {
+        std::vector<I2n::clock::Time> ts;
+
+        ts.push_back (I2n::clock::zero ());
+        ts.push_back (I2n::clock::zero ());
+
+        BOOST_CHECK_EQUAL(ts.size (), 2);
+    }
+
+    BOOST_AUTO_TEST_CASE(containers_set)
+    {
+        std::set<I2n::clock::Time> ts;
+
+        ts.insert (I2n::clock::zero ());
+        ts.insert (I2n::clock::Time (42, 2187));
+        ts.insert (I2n::clock::zero ());
+
+        BOOST_CHECK_EQUAL(ts.size (), 2);
+    }
+
+    BOOST_AUTO_TEST_CASE(containers_list_mean)
+    {
+        std::list<I2n::clock::Time> ts;
+
+        ts.push_back (I2n::clock::Time (42, 42));
+        ts.push_back (I2n::clock::Time (1337, 1337));
+
+        BOOST_CHECK_EQUAL(I2n::clock::mean (ts),
+                          I2n::clock::Time (689, 500000689));
+    }
+
+    BOOST_AUTO_TEST_CASE(containers_list_mean_zero)
+    {
+        std::list<I2n::clock::Time> ts;
+
+        ts.push_back (I2n::clock::Time (0, 0));
+        ts.push_back (I2n::clock::Time (0, 0));
+        ts.push_back (I2n::clock::Time (0, 0));
+        ts.push_back (I2n::clock::Time (0, 0));
+
+        BOOST_CHECK_EQUAL(I2n::clock::mean (ts),
+                          I2n::clock::zero ());
+    }
+
+    BOOST_AUTO_TEST_CASE(containers_list_mean_empty)
+    {
+        std::list<I2n::clock::Time> ts;
+
+        BOOST_CHECK_EQUAL(I2n::clock::mean (ts), I2n::clock::Time (0, 0));
+    }
+
+    BOOST_AUTO_TEST_CASE(containers_set_mean)
+    {
+        std::set<I2n::clock::Time> ts;
+
+        ts.insert (I2n::clock::Time (42));
+        ts.insert (I2n::clock::Time (1337));
+        ts.insert (I2n::clock::Time (2187));
+
+        BOOST_CHECK_EQUAL(I2n::clock::mean (ts),
+                          I2n::clock::Time (1188, 666666666));
+    }
+
+    BOOST_AUTO_TEST_CASE(containers_set_median_empty)
+    {
+        std::set<I2n::clock::Time> ts;
+
+        BOOST_CHECK_EQUAL(I2n::clock::median (ts), I2n::clock::Time (0, 0));
+    }
+
+    BOOST_AUTO_TEST_CASE(containers_set_median_one)
+    {
+        std::set<I2n::clock::Time> ts;
+
+        ts.insert (I2n::clock::Time (42, 0));
+
+        BOOST_CHECK_EQUAL(I2n::clock::median (ts),
+                          I2n::clock::Time (42, 0));
+    }
+
+    BOOST_AUTO_TEST_CASE(containers_set_median_multi)
+    {
+        std::set<I2n::clock::Time> ts;
+
+        ts.insert (I2n::clock::Time (42));
+        ts.insert (I2n::clock::Time (1337));
+        ts.insert (I2n::clock::Time (2187));
+
+        BOOST_CHECK_EQUAL(I2n::clock::median (ts), I2n::clock::Time (1337));
+    }
+
+    BOOST_AUTO_TEST_CASE(containers_vec_median_multi)
+    {
+        std::vector<I2n::clock::Time> ts;
+
+        ts.push_back (I2n::clock::Time (42));
+        ts.push_back (I2n::clock::Time (1337));
+        ts.push_back (I2n::clock::Time (2187));
+
+        BOOST_CHECK_EQUAL(I2n::clock::median (ts), I2n::clock::Time (1337));
+    }
+
+    BOOST_AUTO_TEST_CASE(containers_list_median_multi)
+    {
+        std::list<I2n::clock::Time> ts;
+
+        ts.push_back (I2n::clock::Time (42));
+        ts.push_back (I2n::clock::Time (1337));
+        ts.push_back (I2n::clock::Time (2187));
+        ts.push_back (I2n::clock::Time (0xcafebabe));
+        ts.push_back (I2n::clock::Time (0xdeadbeef));
+
+        BOOST_CHECK_EQUAL(I2n::clock::median (ts), I2n::clock::Time (2187));
+    }
+
+    BOOST_AUTO_TEST_CASE(containers_list_median_multi_evensize)
+    {
+        std::list<I2n::clock::Time> ts;
+
+        ts.push_back (I2n::clock::Time (42));
+        ts.push_back (I2n::clock::Time (1337));
+        ts.push_back (I2n::clock::Time (2187));
+        ts.push_back (I2n::clock::Time (0xf00d));
+        ts.push_back (I2n::clock::Time (0xcafebabe));
+        ts.push_back (I2n::clock::Time (0xdeadbeef));
+
+        BOOST_CHECK_EQUAL(I2n::clock::median (ts), I2n::clock::Time (0xf00d));
+    }
+
 BOOST_AUTO_TEST_SUITE_END() /* [Clock] */
 
 BOOST_AUTO_TEST_SUITE_END()