generalise to long and int; improve test coverage

This commit is contained in:
Fischlurch 2011-01-20 21:30:48 +01:00
parent 05383ea44a
commit c55260d4e4
4 changed files with 205 additions and 108 deletions

View file

@ -56,6 +56,30 @@ namespace util {
return n1 < n2? N1(n2) : n1;
}
/** helper to treat int or long division uniformly */
template<typename I>
struct IDiv;
template<>
struct IDiv<int>
: div_t
{
IDiv<int> (int num, int den)
: div_t(div (num,den))
{ }
};
template<>
struct IDiv<long>
: ldiv_t
{
IDiv<long> (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<typename LI>
inline LI
floordiv (LI num, LI den)
template<typename I>
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<I> 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<typename I>
inline IDiv<I>
floorwrap (I num, I den)
{
IDiv<I> 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 */

View file

@ -1094,32 +1094,111 @@ return: 0
END
PLANNED "integer scale wrapping utility" UtilFloorwrap_test <<END
out-lit: 12 / 4 = 3 % = 0 floor= 3.0 wrap = ( 3, 0)
out-lit: 11 / 4 = 2 % = 3 floor= 2.0 wrap = ( 2, 3)
out-lit: 10 / 4 = 2 % = 2 floor= 2.0 wrap = ( 2, 2)
out-lit: 9 / 4 = 2 % = 1 floor= 2.0 wrap = ( 2, 1)
out-lit: 8 / 4 = 2 % = 0 floor= 2.0 wrap = ( 2, 0)
out-lit: 7 / 4 = 1 % = 3 floor= 1.0 wrap = ( 1, 3)
out-lit: 6 / 4 = 1 % = 2 floor= 1.0 wrap = ( 1, 2)
out-lit: 5 / 4 = 1 % = 1 floor= 1.0 wrap = ( 1, 1)
out-lit: 4 / 4 = 1 % = 0 floor= 1.0 wrap = ( 1, 0)
out-lit: 3 / 4 = 0 % = 3 floor= 0.0 wrap = ( 0, 3)
out-lit: 2 / 4 = 0 % = 2 floor= 0.0 wrap = ( 0, 2)
out-lit: 1 / 4 = 0 % = 1 floor= 0.0 wrap = ( 0, 1)
out-lit: 0 / 4 = 0 % = 0 floor= 0.0 wrap = ( 0, 0)
out-lit: -1 / 4 = 0 % =-1 floor=-1.0 wrap = (-1, 3)
out-lit: -2 / 4 = 0 % =-2 floor=-1.0 wrap = (-1, 2)
out-lit: -3 / 4 = 0 % =-3 floor=-1.0 wrap = (-1, 1)
out-lit: -4 / 4 =-1 % = 0 floor=-1.0 wrap = (-1, 0)
out-lit: -5 / 4 =-1 % =-1 floor=-2.0 wrap = (-2, 3)
out-lit: -6 / 4 =-1 % =-2 floor=-2.0 wrap = (-2, 2)
out-lit: -7 / 4 =-1 % =-3 floor=-2.0 wrap = (-2, 1)
out-lit: -8 / 4 =-2 % = 0 floor=-2.0 wrap = (-2, 0)
out-lit: -9 / 4 =-2 % =-1 floor=-3.0 wrap = (-3, 3)
out-lit:-10 / 4 =-2 % =-2 floor=-3.0 wrap = (-3, 2)
out-lit:-11 / 4 =-2 % =-3 floor=-3.0 wrap = (-3, 1)
out-lit:-12 / 4 =-3 % = 0 floor=-3.0 wrap = (-3, 0)
TEST "integer scale wrapping utility" UtilFloorwrap_test <<END
out-lit: --------i--------12/4
out-lit: 12 / 4 = 3 % = 0 floor= 3.0 wrap = ( 3, 0)
out-lit: 11 / 4 = 2 % = 3 floor= 2.0 wrap = ( 2, 3)
out-lit: 10 / 4 = 2 % = 2 floor= 2.0 wrap = ( 2, 2)
out-lit: 9 / 4 = 2 % = 1 floor= 2.0 wrap = ( 2, 1)
out-lit: 8 / 4 = 2 % = 0 floor= 2.0 wrap = ( 2, 0)
out-lit: 7 / 4 = 1 % = 3 floor= 1.0 wrap = ( 1, 3)
out-lit: 6 / 4 = 1 % = 2 floor= 1.0 wrap = ( 1, 2)
out-lit: 5 / 4 = 1 % = 1 floor= 1.0 wrap = ( 1, 1)
out-lit: 4 / 4 = 1 % = 0 floor= 1.0 wrap = ( 1, 0)
out-lit: 3 / 4 = 0 % = 3 floor= 0.0 wrap = ( 0, 3)
out-lit: 2 / 4 = 0 % = 2 floor= 0.0 wrap = ( 0, 2)
out-lit: 1 / 4 = 0 % = 1 floor= 0.0 wrap = ( 0, 1)
out-lit: 0 / 4 = 0 % = 0 floor= 0.0 wrap = ( 0, 0)
out-lit: -1 / 4 = 0 % =-1 floor=-1.0 wrap = (-1, 3)
out-lit: -2 / 4 = 0 % =-2 floor=-1.0 wrap = (-1, 2)
out-lit: -3 / 4 = 0 % =-3 floor=-1.0 wrap = (-1, 1)
out-lit: -4 / 4 =-1 % = 0 floor=-1.0 wrap = (-1, 0)
out-lit: -5 / 4 =-1 % =-1 floor=-2.0 wrap = (-2, 3)
out-lit: -6 / 4 =-1 % =-2 floor=-2.0 wrap = (-2, 2)
out-lit: -7 / 4 =-1 % =-3 floor=-2.0 wrap = (-2, 1)
out-lit: -8 / 4 =-2 % = 0 floor=-2.0 wrap = (-2, 0)
out-lit: -9 / 4 =-2 % =-1 floor=-3.0 wrap = (-3, 3)
out-lit: -10 / 4 =-2 % =-2 floor=-3.0 wrap = (-3, 2)
out-lit: -11 / 4 =-2 % =-3 floor=-3.0 wrap = (-3, 1)
out-lit: -12 / 4 =-3 % = 0 floor=-3.0 wrap = (-3, 0)
out-lit: --------i--------12/-4
out-lit: 12 /-4 =-3 % = 0 floor=-3.0 wrap = (-3, 0)
out-lit: 11 /-4 =-2 % = 3 floor=-3.0 wrap = (-3,-1)
out-lit: 10 /-4 =-2 % = 2 floor=-3.0 wrap = (-3,-2)
out-lit: 9 /-4 =-2 % = 1 floor=-3.0 wrap = (-3,-3)
out-lit: 8 /-4 =-2 % = 0 floor=-2.0 wrap = (-2, 0)
out-lit: 7 /-4 =-1 % = 3 floor=-2.0 wrap = (-2,-1)
out-lit: 6 /-4 =-1 % = 2 floor=-2.0 wrap = (-2,-2)
out-lit: 5 /-4 =-1 % = 1 floor=-2.0 wrap = (-2,-3)
out-lit: 4 /-4 =-1 % = 0 floor=-1.0 wrap = (-1, 0)
out-lit: 3 /-4 = 0 % = 3 floor=-1.0 wrap = (-1,-1)
out-lit: 2 /-4 = 0 % = 2 floor=-1.0 wrap = (-1,-2)
out-lit: 1 /-4 = 0 % = 1 floor=-1.0 wrap = (-1,-3)
out-lit: 0 /-4 = 0 % = 0 floor=-0.0 wrap = ( 0, 0)
out-lit: -1 /-4 = 0 % =-1 floor= 0.0 wrap = ( 0,-1)
out-lit: -2 /-4 = 0 % =-2 floor= 0.0 wrap = ( 0,-2)
out-lit: -3 /-4 = 0 % =-3 floor= 0.0 wrap = ( 0,-3)
out-lit: -4 /-4 = 1 % = 0 floor= 1.0 wrap = ( 1, 0)
out-lit: -5 /-4 = 1 % =-1 floor= 1.0 wrap = ( 1,-1)
out-lit: -6 /-4 = 1 % =-2 floor= 1.0 wrap = ( 1,-2)
out-lit: -7 /-4 = 1 % =-3 floor= 1.0 wrap = ( 1,-3)
out-lit: -8 /-4 = 2 % = 0 floor= 2.0 wrap = ( 2, 0)
out-lit: -9 /-4 = 2 % =-1 floor= 2.0 wrap = ( 2,-1)
out-lit: -10 /-4 = 2 % =-2 floor= 2.0 wrap = ( 2,-2)
out-lit: -11 /-4 = 2 % =-3 floor= 2.0 wrap = ( 2,-3)
out-lit: -12 /-4 = 3 % = 0 floor= 3.0 wrap = ( 3, 0)
out-lit: --------l--------12/4
out-lit: 12 / 4 = 3 % = 0 floor= 3.0 wrap = ( 3, 0)
out-lit: 11 / 4 = 2 % = 3 floor= 2.0 wrap = ( 2, 3)
out-lit: 10 / 4 = 2 % = 2 floor= 2.0 wrap = ( 2, 2)
out-lit: 9 / 4 = 2 % = 1 floor= 2.0 wrap = ( 2, 1)
out-lit: 8 / 4 = 2 % = 0 floor= 2.0 wrap = ( 2, 0)
out-lit: 7 / 4 = 1 % = 3 floor= 1.0 wrap = ( 1, 3)
out-lit: 6 / 4 = 1 % = 2 floor= 1.0 wrap = ( 1, 2)
out-lit: 5 / 4 = 1 % = 1 floor= 1.0 wrap = ( 1, 1)
out-lit: 4 / 4 = 1 % = 0 floor= 1.0 wrap = ( 1, 0)
out-lit: 3 / 4 = 0 % = 3 floor= 0.0 wrap = ( 0, 3)
out-lit: 2 / 4 = 0 % = 2 floor= 0.0 wrap = ( 0, 2)
out-lit: 1 / 4 = 0 % = 1 floor= 0.0 wrap = ( 0, 1)
out-lit: 0 / 4 = 0 % = 0 floor= 0.0 wrap = ( 0, 0)
out-lit: -1 / 4 = 0 % =-1 floor=-1.0 wrap = (-1, 3)
out-lit: -2 / 4 = 0 % =-2 floor=-1.0 wrap = (-1, 2)
out-lit: -3 / 4 = 0 % =-3 floor=-1.0 wrap = (-1, 1)
out-lit: -4 / 4 =-1 % = 0 floor=-1.0 wrap = (-1, 0)
out-lit: -5 / 4 =-1 % =-1 floor=-2.0 wrap = (-2, 3)
out-lit: -6 / 4 =-1 % =-2 floor=-2.0 wrap = (-2, 2)
out-lit: -7 / 4 =-1 % =-3 floor=-2.0 wrap = (-2, 1)
out-lit: -8 / 4 =-2 % = 0 floor=-2.0 wrap = (-2, 0)
out-lit: -9 / 4 =-2 % =-1 floor=-3.0 wrap = (-3, 3)
out-lit: -10 / 4 =-2 % =-2 floor=-3.0 wrap = (-3, 2)
out-lit: -11 / 4 =-2 % =-3 floor=-3.0 wrap = (-3, 1)
out-lit: -12 / 4 =-3 % = 0 floor=-3.0 wrap = (-3, 0)
out-lit: --------l--------12/-4
out-lit: 12 /-4 =-3 % = 0 floor=-3.0 wrap = (-3, 0)
out-lit: 11 /-4 =-2 % = 3 floor=-3.0 wrap = (-3,-1)
out-lit: 10 /-4 =-2 % = 2 floor=-3.0 wrap = (-3,-2)
out-lit: 9 /-4 =-2 % = 1 floor=-3.0 wrap = (-3,-3)
out-lit: 8 /-4 =-2 % = 0 floor=-2.0 wrap = (-2, 0)
out-lit: 7 /-4 =-1 % = 3 floor=-2.0 wrap = (-2,-1)
out-lit: 6 /-4 =-1 % = 2 floor=-2.0 wrap = (-2,-2)
out-lit: 5 /-4 =-1 % = 1 floor=-2.0 wrap = (-2,-3)
out-lit: 4 /-4 =-1 % = 0 floor=-1.0 wrap = (-1, 0)
out-lit: 3 /-4 = 0 % = 3 floor=-1.0 wrap = (-1,-1)
out-lit: 2 /-4 = 0 % = 2 floor=-1.0 wrap = (-1,-2)
out-lit: 1 /-4 = 0 % = 1 floor=-1.0 wrap = (-1,-3)
out-lit: 0 /-4 = 0 % = 0 floor=-0.0 wrap = ( 0, 0)
out-lit: -1 /-4 = 0 % =-1 floor= 0.0 wrap = ( 0,-1)
out-lit: -2 /-4 = 0 % =-2 floor= 0.0 wrap = ( 0,-2)
out-lit: -3 /-4 = 0 % =-3 floor= 0.0 wrap = ( 0,-3)
out-lit: -4 /-4 = 1 % = 0 floor= 1.0 wrap = ( 1, 0)
out-lit: -5 /-4 = 1 % =-1 floor= 1.0 wrap = ( 1,-1)
out-lit: -6 /-4 = 1 % =-2 floor= 1.0 wrap = ( 1,-2)
out-lit: -7 /-4 = 1 % =-3 floor= 1.0 wrap = ( 1,-3)
out-lit: -8 /-4 = 2 % = 0 floor= 2.0 wrap = ( 2, 0)
out-lit: -9 /-4 = 2 % =-1 floor= 2.0 wrap = ( 2,-1)
out-lit: -10 /-4 = 2 % =-2 floor= 2.0 wrap = ( 2,-2)
out-lit: -11 /-4 = 2 % =-3 floor= 2.0 wrap = ( 2,-3)
out-lit: -12 /-4 = 3 % = 0 floor= 3.0 wrap = ( 3, 0)
return: 0
END

View file

@ -174,15 +174,13 @@ namespace test {
* a precision beyond the 16 digits provided by double.
* Besides that, we can conclude that the additional tests and
* adjustment of the custom floordiv only creates a slight overhead
* compared to the built-in integer div function. The comparison
* with the \c floordiv<int> 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 ()

View file

@ -22,17 +22,20 @@
#include "lib/test/run.hpp"
#include "lib/test/test-helper.hpp"
#include "lib/util.hpp"
#include <cmath>
//#include <vector>
#include <iostream>
#include <boost/format.hpp>
#include <boost/lexical_cast.hpp>
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<int> (arg[0]) : 12;
int scale = 1 < arg.size()? lexical_cast<int> (arg[1]) : 4;
checkWrap (range, scale);
checkWrap (range, -scale);
checkWrap<long> (range, scale);
checkWrap<long> (range, -scale);
}
template<typename I>
void
checkWrap (I range, I scale)
{
cout << "--------"<< showType<I>()
<< "--------"<< range<<"/"<<scale<<endl;
for (I i=range; i >=-range; --i)
showWrap (i, scale);
}
template<typename I>
void
showWrap (I val, I scale)
{
IDiv<I> 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;
}
};