The Lumiera »Reference Platform« is now upgraded to Debian/Buster, which provides GCC-14 and Clang-20. Thus the compiler support for C++20 language features seems solid enough, and C++23, while still in ''experimental stage'' can be seen as a complement and addendum. This changeset * upgrades the compile switches for the build system * provides all the necessary adjustments to keep the code base compilable Notable changes: * λ-capture by value now requires explicit qualification how to handle `this` * comparison operators are now handled transparently by the core language, largely obsoleting boost::operators. This change incurs several changes to implicit handling rules and causes lots of ambiguities — which typically pinpoint some long standing design issues, especially related to MObjects and the ''time entities''. Most tweaks done here can be ''considered preliminary'' * unfortunately the upgraded standard ''fails'' to handle **tuple-like** entities in a satisfactory way — rather an ''exposition-only'' concept is introduced, which applies solely to some containers from the STL, thereby breaking some very crucial code in the render entities, which was built upon the notion of ''tuple-like'' entities and the ''tuple protocol''. The solution is to abandon the STL in this respect and **provide an alternative implementation** of the `apply` function and related elements.
243 lines
9.4 KiB
C++
243 lines
9.4 KiB
C++
/*
|
||
QuantiserBasics(Test) - a demo quantiser to cover the basic quantiser API
|
||
|
||
Copyright (C)
|
||
2011, Hermann Vosseler <Ichthyostega@web.de>
|
||
|
||
**Lumiera** is free software; you can redistribute it and/or modify it
|
||
under the terms of the GNU General Public License as published by the
|
||
Free Software Foundation; either version 2 of the License, or (at your
|
||
option) any later version. See the file COPYING for further details.
|
||
|
||
* *****************************************************************/
|
||
|
||
/** @file quantiser-basics-test.cpp
|
||
** unit test \ref QuantiserBasics_test
|
||
*/
|
||
|
||
|
||
#include "lib/test/run.hpp"
|
||
#include "lib/test/test-helper.hpp"
|
||
#include "lib/time/quantiser.hpp"
|
||
#include "lib/random.hpp"
|
||
#include "lib/util.hpp"
|
||
#include "lib/test/diagnostic-output.hpp"
|
||
|
||
using lumiera::error::LUMIERA_ERROR_BOTTOM_VALUE;
|
||
using util::isnil;
|
||
using lib::rani;
|
||
|
||
|
||
|
||
namespace lib {
|
||
namespace time{
|
||
namespace test{
|
||
|
||
namespace {
|
||
|
||
const int MAX_FRAMES = 25*500;
|
||
const int DIRT_GRAIN = 50;
|
||
|
||
const FSecs F25(1,25); // duration of one PAL frame
|
||
|
||
inline Time
|
||
secs (int seconds)
|
||
{
|
||
return Time(FSecs(seconds));
|
||
}
|
||
}
|
||
|
||
|
||
|
||
|
||
/****************************************************//**
|
||
* @test cover the basic Quantiser API.
|
||
* This test uses a standalone quantiser implementation
|
||
* to demonstrate and verify the basic behaviour
|
||
* and the usage corner cases of a quantiser.
|
||
*
|
||
* In this most simple form, a quantiser is defined
|
||
* by the time reference point (origin) to use, and
|
||
* the frame rate (grid spacing). For each raw time
|
||
* value, the quantiser yields a time value aligned
|
||
* at the next lower frame bound. Besides that,
|
||
* time values are confined to be within
|
||
* the interval (Time::MIN, Time::MAX)
|
||
*
|
||
* @see TimeQuantisation_test
|
||
*/
|
||
class QuantiserBasics_test : public Test
|
||
{
|
||
|
||
virtual void
|
||
run (Arg)
|
||
{
|
||
seedRand();
|
||
checkSimpleQuantisation();
|
||
coverQuantisationStandardCases();
|
||
coverQuantisationCornerCases();
|
||
}
|
||
|
||
|
||
void
|
||
checkSimpleQuantisation ()
|
||
{
|
||
FixedFrameQuantiser fixQ(25);
|
||
|
||
int frames = rani(MAX_FRAMES);
|
||
FSecs dirt = (F25 / (2 + rani(DIRT_GRAIN)));
|
||
|
||
Time rawTime = Time(frames*F25) + Duration(dirt);
|
||
|
||
CHECK (Time( frames *F25) <= rawTime);
|
||
CHECK (Time((frames+1)*F25) > rawTime);
|
||
|
||
Time quantTime (fixQ.gridLocal (rawTime));
|
||
|
||
CHECK (Time(frames*F25) == quantTime);
|
||
}
|
||
|
||
|
||
/** Test Quantiser
|
||
* allowing to use plain numbers.
|
||
* 1 Frame == 3 micro ticks */
|
||
struct TestQuant
|
||
: FixedFrameQuantiser
|
||
{
|
||
TestQuant (int origin=0)
|
||
: FixedFrameQuantiser( FrameRate(TimeValue::SCALE, 3 ), TimeValue(origin))
|
||
{ }
|
||
|
||
int
|
||
quant (int testPoint)
|
||
{
|
||
TimeVar quantised = this->gridLocal(TimeValue(testPoint));
|
||
return _raw(quantised);
|
||
}
|
||
};
|
||
|
||
void
|
||
coverQuantisationStandardCases()
|
||
{
|
||
TestQuant q0;
|
||
TestQuant q1(1);
|
||
|
||
CHECK ( 6 == q0.quant(7) );
|
||
CHECK ( 6 == q0.quant(6) );
|
||
CHECK ( 3 == q0.quant(5) );
|
||
CHECK ( 3 == q0.quant(4) );
|
||
CHECK ( 3 == q0.quant(3) );
|
||
CHECK ( 0 == q0.quant(2) );
|
||
CHECK ( 0 == q0.quant(1) );
|
||
CHECK ( 0 == q0.quant(0) );
|
||
CHECK (-3 == q0.quant(-1));
|
||
CHECK (-3 == q0.quant(-2));
|
||
CHECK (-3 == q0.quant(-3));
|
||
CHECK (-6 == q0.quant(-4));
|
||
|
||
CHECK ( 6 == q1.quant(7) );
|
||
CHECK ( 3 == q1.quant(6) );
|
||
CHECK ( 3 == q1.quant(5) );
|
||
CHECK ( 3 == q1.quant(4) );
|
||
CHECK ( 0 == q1.quant(3) );
|
||
CHECK ( 0 == q1.quant(2) );
|
||
CHECK ( 0 == q1.quant(1) );
|
||
CHECK (-3 == q1.quant(0) );
|
||
CHECK (-3 == q1.quant(-1));
|
||
CHECK (-3 == q1.quant(-2));
|
||
CHECK (-6 == q1.quant(-3));
|
||
CHECK (-6 == q1.quant(-4));
|
||
}
|
||
|
||
|
||
void
|
||
coverQuantisationCornerCases()
|
||
{
|
||
// For this test we exploit the limits of the time system
|
||
Time SUB_MIN{-Duration::MAX};
|
||
Time SUP_MAX{ Duration::MAX};
|
||
|
||
// origin at lower end of the time range
|
||
FixedFrameQuantiser case1 (1, SUB_MIN);
|
||
CHECK (secs(0) == case1.gridLocal(SUB_MIN ));
|
||
CHECK (secs(0) == case1.gridLocal(SUB_MIN +TimeValue(1) ));
|
||
CHECK (secs(1) == case1.gridLocal(SUB_MIN +secs(1) ));
|
||
CHECK (SUP_MAX -secs(1) > case1.gridLocal( secs(-1) ));
|
||
CHECK (SUP_MAX -secs(1) <= case1.gridLocal( secs (0) ));
|
||
CHECK (SUP_MAX > case1.gridLocal( secs (0) ));
|
||
CHECK (SUP_MAX > case1.gridLocal( secs(+1) ));
|
||
CHECK (SUP_MAX > case1.gridLocal( secs(+2) ));
|
||
|
||
TimeValue largestPoint = case1.gridLocal(secs (0));
|
||
CHECK (largestPoint == case1.gridLocal( secs(+1) ));
|
||
CHECK (largestPoint == case1.gridLocal( secs(+2) ));
|
||
CHECK (largestPoint < SUP_MAX);
|
||
CHECK (largestPoint == Offset{secs(1)} * case1.gridPoint(secs(0)));
|
||
|
||
// origin at upper end of the time range
|
||
FixedFrameQuantiser case2 (1, SUP_MAX);
|
||
CHECK (secs( 0) == case2.gridLocal(SUP_MAX ));
|
||
CHECK (secs(-1) == case2.gridLocal(SUP_MAX -TimeValue(1) )); // note: next lower frame
|
||
CHECK (secs(-1) == case2.gridLocal(SUP_MAX -secs(1) )); // i.e. the same as a whole frame down
|
||
CHECK (SUB_MIN +secs(1) < case2.gridLocal( secs(+2) ));
|
||
CHECK (SUB_MIN +secs(1) >= case2.gridLocal( secs(+1) ));
|
||
CHECK (SUB_MIN < case2.gridLocal( secs(+1) ));
|
||
CHECK (SUB_MIN == case2.gridLocal( secs( 0) )); // note: because of downward truncating,
|
||
CHECK (SUB_MIN == case2.gridLocal( secs(-1) )); // resulting values will already exceed
|
||
CHECK (SUB_MIN == case2.gridLocal( secs(-2) )); // allowed range and thus will be clipped
|
||
|
||
// use very large frame with size of half the time range
|
||
Duration hugeFrame(SUP_MAX);
|
||
FixedFrameQuantiser case3 (hugeFrame);
|
||
CHECK (SUB_MIN == case3.gridLocal(SUB_MIN ));
|
||
CHECK (SUB_MIN == case3.gridLocal(SUB_MIN +TimeValue(1) ));
|
||
CHECK (SUB_MIN == case3.gridLocal( secs(-1) ));
|
||
CHECK (TimeValue(0) == case3.gridLocal( secs( 0) ));
|
||
CHECK (TimeValue(0) == case3.gridLocal( secs(+1) ));
|
||
CHECK (TimeValue(0) == case3.gridLocal(SUP_MAX -TimeValue(1) ));
|
||
CHECK (SUP_MAX == case3.gridLocal(SUP_MAX ));
|
||
|
||
// now displacing this grid by +1sec....
|
||
FixedFrameQuantiser case4 (hugeFrame, secs(1));
|
||
CHECK (SUB_MIN == case4.gridLocal(SUB_MIN ));
|
||
CHECK (SUB_MIN == case4.gridLocal(SUB_MIN +TimeValue(1) )); // clipped...
|
||
CHECK (SUB_MIN == case4.gridLocal(SUB_MIN +secs(1) )); // but now exact (unclipped)
|
||
CHECK (SUB_MIN == case4.gridLocal( secs(-1) ));
|
||
CHECK (SUB_MIN == case4.gridLocal( secs( 0) ));
|
||
CHECK (TimeValue(0) == case4.gridLocal( secs(+1) )); //.....now exactly the frame number zero
|
||
CHECK (TimeValue(0) == case4.gridLocal(SUP_MAX -TimeValue(1) ));
|
||
|
||
|
||
// think big...
|
||
TimeVar excess{SUP_MAX +secs(1)}; // this is a *loophole* to slide by the limitation of Time values
|
||
CHECK (SUP_MAX < excess);
|
||
CHECK (Duration{excess} < excess); // ...yet as soon as we construct another entity, the limitation applies
|
||
CHECK (Duration{excess} == SUP_MAX);
|
||
|
||
CHECK (SUP_MAX == case4.gridLocal(excess )); // Thus, more by accident, the next higher grid point can be computed
|
||
|
||
CHECK (secs(1) == case4.timeOf(0));
|
||
CHECK (excess == case4.timeOf(1)); // The same loophole also allows to generate this next higher grid point
|
||
CHECK (excess == case4.timeOf(2)); // ...while the next after next will limited in computation
|
||
|
||
FixedFrameQuantiser broken (Duration::MAX, SUP_MAX); // Can drive this loophole to the extreme...
|
||
CHECK (secs(0) == broken.timeOf(-1)); // since there is leeway by one order of magnitude
|
||
CHECK (SUP_MAX == broken.timeOf(0));
|
||
CHECK (SUP_MAX+SUP_MAX > Duration::MAX);
|
||
CHECK (SUP_MAX+SUP_MAX == broken.timeOf(1));
|
||
CHECK (SUP_MAX+SUP_MAX == broken.timeOf(2));
|
||
|
||
// frame sizes below the time micro grid get trapped
|
||
long subAtomic = 2*TimeValue::SCALE; // too small for this universe...
|
||
VERIFY_ERROR (BOTTOM_VALUE, FixedFrameQuantiser quark(subAtomic) );
|
||
VERIFY_ERROR (BOTTOM_VALUE, FixedFrameQuantiser quark(Duration (FSecs (1,subAtomic))) );
|
||
}
|
||
};
|
||
|
||
|
||
/** Register this test class... */
|
||
LAUNCHER (QuantiserBasics_test, "unit common");
|
||
|
||
|
||
|
||
}}} // namespace lib::time::test
|