From c55260d4e49ca25a64cee3008eaae25c5404d20b Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Thu, 20 Jan 2011 21:30:48 +0100 Subject: [PATCH] generalise to long and int; improve test coverage --- src/lib/util.hpp | 56 ++++++++++++- tests/40components.tests | 131 ++++++++++++++++++++++++------ tests/lib/util-floordiv-test.cpp | 16 ++-- tests/lib/util-floorwrap-test.cpp | 110 ++++++++++--------------- 4 files changed, 205 insertions(+), 108 deletions(-) diff --git a/src/lib/util.hpp b/src/lib/util.hpp index 916374a90..ce3755c52 100644 --- a/src/lib/util.hpp +++ b/src/lib/util.hpp @@ -56,6 +56,30 @@ namespace util { return n1 < n2? N1(n2) : n1; } + + /** helper to treat int or long division uniformly */ + template + struct IDiv; + + template<> + struct IDiv + : div_t + { + IDiv (int num, int den) + : div_t(div (num,den)) + { } + }; + + template<> + struct IDiv + : ldiv_t + { + IDiv (long num, long den) + : ldiv_t(ldiv (num,den)) + { } + }; + + /** floor function for integer arithmetics. * Unlike the built-in integer division, this function * always rounds towards the next \em smaller integer, @@ -63,20 +87,44 @@ namespace util { * @warning floor on doubles performs way better * @see UtilFloordiv_test */ - template - inline LI - floordiv (LI num, LI den) + template + inline I + floordiv (I num, I den) { if (0 < (num^den)) return num/den; else { // truncate similar to floor() - ldiv_t res = ldiv(num,den); + IDiv res(num,den); return (res.rem)? res.quot-1 // negative results truncated towards next smaller int : res.quot; //..unless the division result not truncated at all } } + /** scale wrapping operation. + * Quantises the numerator value into the scale given by the denominator. + * Unlike the built-in integer division, this function always rounds towards + * the next \em smaller integer and also relates the remainder (=modulo) to + * this next lower scale grid point. + * @return quotient and remainder packed into a struct + * @see UtilFloorwarp_test + */ + template + inline IDiv + floorwrap (I num, I den) + { + IDiv res(num,den); + if (0 > (num^den) && res.rem) + { // negative results + // wrapped similar to floor() + --res.quot; + res.rem = den - (-res.rem); + } + return res; + } + + + /* ======== generic empty check ========= */ /** a family of util functions providing a "no value whatsoever" test. Works on strings and all STL containers, includes NULL test for pointers */ diff --git a/tests/40components.tests b/tests/40components.tests index 395981ba3..3020d34c7 100644 --- a/tests/40components.tests +++ b/tests/40components.tests @@ -1094,32 +1094,111 @@ return: 0 END -PLANNED "integer scale wrapping utility" UtilFloorwrap_test < instantiation is largely moot, because - * this internally calls \c fdiv on values promoted to long. Another - * oddity in the same category is the slightly better performance - * of long over int64_t. Also, the alternative formulation of - * the function, which uses the \c fdiv() function also to divide - * the positive results, performs only slightly worse. So this - * actual implementation was chosen mainly because it seems - * to state its intent more clearly in code. + * compared to the built-in integer div function. An oddity to note + * is the slightly better performance of long over int64_t. Also, + * the alternative formulation of the function, which uses the + * \c fdiv() function also to divide the positive results, + * performs only slightly worse. So this implementation + * was chosen mainly because it seems to state its + * intent more clearly in code. */ void runPerformanceTest () diff --git a/tests/lib/util-floorwrap-test.cpp b/tests/lib/util-floorwrap-test.cpp index 0e1885e5b..c42f37593 100644 --- a/tests/lib/util-floorwrap-test.cpp +++ b/tests/lib/util-floorwrap-test.cpp @@ -22,17 +22,20 @@ #include "lib/test/run.hpp" +#include "lib/test/test-helper.hpp" #include "lib/util.hpp" #include -//#include #include #include +#include using ::Test; using std::cout; -//using std::rand; +using std::endl; using boost::format; +using boost::lexical_cast; +using lib::test::showType; namespace util { @@ -40,51 +43,18 @@ namespace test { - namespace{ // Test data and operations - - div_t - floorwrap (int num, int den) - { - div_t res = div (num,den); - if (0 > (num^den) && res.rem) - { // wrap similar to floor() - --res.quot; - res.rem = den - (-res.rem); - } - return res; - } - - - void - showWrap (int val, int scale) - { - div_t wrap = floorwrap(val,scale); - cout << format ("% 3d /% 1d =% 1d %% =% d floor=% 4.1f wrap = (%2d,%2d) \n") - % val % scale % (val/scale) - % (val%scale) % floor(double(val)/scale) - % wrap.quot % wrap.rem; - } - - - - } // (End) test data and operations - - /********************************************************************** - * @test Evaluate a custom built integer floor function. - * This function is crucial for Lumiera's rule of quantisation - * of time values into frame intervals. This rule requires time - * points to be rounded towards the next lower frame border always, - * irrespective of the relation to the actual time origin. - * Contrast this to the built-in integer division operator, which - * truncates towards zero. - * - * @note if invoked with an non empty parameter, this test performs - * some interesting timing comparisons, which initially were - * used to tweak the implementation a bit. + /*************************************************************************** + * @test Verify a custom built integer scale division and wrapping function. + * This function is relevant for decimating values into a given scale, + * like splitting time measurements in hours, minutes, seconds etc. + * Basically, in Lumiera the quantisation into a scale is always + * done with the same orientation, irrespective of the zero point + * on the given scale. Contrast this to the built-in integer + * division and modulo operators working symmetrical to zero. * @see util.hpp - * @see QuantiserBasics_test + * @see TimeFormats_test */ class UtilFloorwrap_test : public Test { @@ -92,34 +62,36 @@ namespace test { virtual void run (Arg arg) { - showWrap ( 12,4); - showWrap ( 11,4); - showWrap ( 10,4); - showWrap ( 9,4); - showWrap ( 8,4); - showWrap ( 7,4); - showWrap ( 6,4); - showWrap ( 5,4); - showWrap ( 4,4); - showWrap ( 3,4); - showWrap ( 2,4); - showWrap ( 1,4); - showWrap ( 0,4); - showWrap (- 1,4); - showWrap (- 2,4); - showWrap (- 3,4); - showWrap (- 4,4); - showWrap (- 5,4); - showWrap (- 6,4); - showWrap (- 7,4); - showWrap (- 8,4); - showWrap (- 9,4); - showWrap (-10,4); - showWrap (-11,4); - showWrap (-12,4); + int range = 0 < arg.size()? lexical_cast (arg[0]) : 12; + int scale = 1 < arg.size()? lexical_cast (arg[1]) : 4; + + checkWrap (range, scale); + checkWrap (range, -scale); + checkWrap (range, scale); + checkWrap (range, -scale); } + template + void + checkWrap (I range, I scale) + { + cout << "--------"<< showType() + << "--------"<< range<<"/"<=-range; --i) + showWrap (i, scale); + } + + template + void + showWrap (I val, I scale) + { + IDiv wrap = floorwrap(val,scale); + cout << format ("% 3d /% 1d =% 1d %% =% d floor=% 4.1f wrap = (%2d,%2d)\n") + % val % scale % (val/scale) + % (val%scale) % floor(double(val)/scale) + % wrap.quot % wrap.rem; + } };