Library: verification for all floating-point types

finally I was able to come up with a rather concise test
that works with float, double and long double; especially
the aliasing due to rounding could be demonstrated, and
the precision limit is made plausible.

Interestingly, `std::fabs()` really works precise enough
so that a very large positive domain can be implemented
and tested with exact match
This commit is contained in:
Fischlurch 2026-09-15 23:28:19 +02:00
parent 0c69db19d7
commit 620ca918c3
2 changed files with 168 additions and 100 deletions

View file

@ -19,21 +19,14 @@
#include "test/run.hpp"
#include "lib/util-quant.hpp"
#include "lib/util.hpp"
#include "test/diagnostic-output.hpp"//////////////////TODO
//#include "lib/format-cout.hpp"
//#include "lib/format-string.hpp"
using ::Test;
//using util::isnil;
//using util::_Fmt;
using util::isLimited;
using std::floating_point;
namespace util {
namespace test {
namespace{ // Test helpers...
template<typename NUM>
@ -49,6 +42,7 @@ namespace test {
/******************************************************************//**
* @test Demonstrate and verify a cyclic wrapping numeric domain.
* - show the wrapping behaviour with simple numbers
@ -56,7 +50,9 @@ namespace test {
* - verify the computation works at the very bounds of the domain
* - verify the most difficult path with a negative minVal,
* and the smallest number in the domain
* @todo add coverage for floating-point numbers
* - show a similar simple wrapping behaviour with all float types
* - demonstrate the meaning of the precision limit
* - demonstrate aliasing and sanitised rounding errors
* @see util::cyclicWrap
* @see parameter-scale-test.cpp
*/
@ -79,7 +75,9 @@ namespace test {
verifyIntegerTypes<int64_t> ();
verifyIntegerTypes<uint64_t>();
verifyFloatTypes();
verifyFloatTypes<float>();
verifyFloatTypes<double>();
verifyFloatTypes<f128>();
}
@ -132,6 +130,8 @@ namespace test {
CHECK (isLimited (LOWER, cyc(_MAX<I>-1), UPPER));
CHECK (isLimited (LOWER, cyc(_MIN<I>+1), UPPER));
//------ Demonstrate behaviour at lower domain bound
//
I dist = LOWER - _MIN<I>; // guaranteed to be positive and representable in-domain
I step = (dist / 5) * 5; // the next applicable grid point
I excess = dist - step;
@ -140,96 +140,52 @@ namespace test {
CHECK (LOWER + offset == cyc(_MIN<I>) );
}
template<typename F>
void
verifyFloatTypes()
{
double maxDom = _MAX<double>;
double super = _MAX<double> - _MIN<double>;
SHOW_EXPR(maxDom)
SHOW_EXPR(super)
SHOW_EXPR(_EPS<double>)
SHOW_EXPR(limit_cyclicWrap<double>(1))
SHOW_EXPR(limit_cyclicWrap<double>(1024))
SHOW_EXPR(limit_cyclicWrap<double>(1.0/1024))
SHOW_EXPR(limit_cyclicWrap<double>(_EPS<double>))
SHOW_EXPR(limit_cyclicWrap<double>(maxDom))
auto cyc = [&](F val){ return cyclicWrap (val, F(-10),F(-5)); };
double limit5 = limit_cyclicWrap<double>(5);
double atLimit = limit5 * (1-_EPS<double>);
double stepLim = std::floor (limit5 / 5) * 5;
SHOW_EXPR(limit5*_EPS<double>)
SHOW_EXPR(limit5)
SHOW_EXPR(stepLim)
SHOW_EXPR(atLimit)
CHECK (almostEqual (F(-10), cyc(0)) );
CHECK (almostEqual (F( -9), cyc(1)) );
CHECK (almostEqual (F( -8), cyc(2)) );
CHECK (almostEqual (F( -7), cyc(3)) );
CHECK (almostEqual (F( -6), cyc(4)) );
CHECK (almostEqual (F(-10), cyc(5)) );
CHECK (almostEqual (F( -9), cyc(6)) );
CHECK (almostEqual (F( -8), cyc(7)) );
CHECK (almostEqual (F( -7), cyc(8)) );
CHECK (almostEqual (F( -6), cyc(9)) );
CHECK (almostEqual (F(-10), cyc(105)));
SHOW_EXPR(cyclicWrap (10.0 ,0.0, 5.0))
SHOW_EXPR(cyclicWrap (limit5 ,0.0, 5.0))
SHOW_EXPR(cyclicWrap (atLimit ,0.0, 5.0))
SHOW_EXPR(cyclicWrap (stepLim ,0.0, 5.0))
SHOW_EXPR(cyclicWrap (stepLim-5 ,0.0, 5.0))
SHOW_EXPR(cyclicWrap (stepLim+1 ,0.0, 5.0))
SHOW_EXPR(cyclicWrap (stepLim+2 ,0.0, 5.0))
SHOW_EXPR(cyclicWrap (stepLim+3 ,0.0, 5.0))
SHOW_EXPR(cyclicWrap (stepLim+4 ,0.0, 5.0))
SHOW_EXPR(cyclicWrap (stepLim+5 ,0.0, 5.0))
SHOW_EXPR(cyclicWrap (stepLim+6 ,0.0, 5.0))
SHOW_EXPR(cyclicWrap (stepLim+10,0.0, 5.0))
SHOW_EXPR(cyclicWrap (stepLim+20,0.0, 5.0))
SHOW_EXPR(cyclicWrap (stepLim+30,0.0, 5.0))
SHOW_EXPR(cyclicWrap (stepLim+40,0.0, 5.0))
SHOW_EXPR(cyclicWrap (stepLim+50,0.0, 5.0))
SHOW_EXPR(cyclicWrap (stepLim+100,0.0, 5.0))
SHOW_EXPR(cyclicWrap (stepLim+101,0.0, 5.0))
// For each period-length, a precision limit can be computed;
// whenever bounds or inputs approach that limit, the rounding errors
// approach the order of magnitude of the period-length, and thus
// computing of wrapped values becomes pointless...
F period5 = F(5);
F limit5 = limit_cyclicWrap<F> (period5);
SHOW_EXPL(cyclicWrap (limit5 ,10.0, 15.0))
SHOW_EXPL(cyclicWrap (limit5+1 ,10.0, 15.0))
double epsStep = _MAX<double>*_EPS<double>;
double toxicMin = _MIN<double> + 2*epsStep;
double toxicMax = epsStep;
double toxicPeriod = toxicMax - toxicMin;
SHOW_EXPR(epsStep)
SHOW_EXPR(toxicMin)
SHOW_EXPR(toxicMax)
SHOW_EXPR(toxicPeriod)
SHOW_EXPR(cyclicWrap<double>(0, toxicMin,toxicMax) )
SHOW_EXPR(cyclicWrap<double>(1, toxicMin,toxicMax) )
SHOW_EXPR(cyclicWrap<double>(1*epsStep, toxicMin,toxicMax) )
SHOW_EXPR(cyclicWrap<double>(2*epsStep, toxicMin,toxicMax) )
SHOW_EXPR(cyclicWrap<double>(3*epsStep, toxicMin,toxicMax) )
SHOW_EXPR(cyclicWrap<double>(0, 1, _MAX<double>) )
SHOW_EXPR(cyclicWrap<double>(1, 1, _MAX<double>) )
SHOW_EXPR(cyclicWrap<double>(2, 1, _MAX<double>) )
SHOW_EXPR(cyclicWrap<double>(-1,1, _MAX<double>) )
SHOW_EXPR(cyclicWrap<double>(-epsStep,1, _MAX<double>) )
SHOW_EXPR(1 == cyclicWrap<double>(1, 1, _MAX<double>))
SHOW_EXPR(2 == cyclicWrap<double>(2, 1, _MAX<double>))
SHOW_EXPR(1 == cyclicWrap<double>(-1,1, _MAX<double>) )
double wrapped = cyclicWrap (-epsStep,double(1), _MAX<double>);
SHOW_EXPR(1 < wrapped)
SHOW_EXPR(wrapped < _MAX<double>)
auto cyc = [&](double val){ return cyclicWrap (val, double(-10),double(-5)); };
CHECK (almostEqual (double(-10), cyc(0)));
CHECK (almostEqual (double( -9), cyc(1)));
CHECK (almostEqual (double( -8), cyc(2)));
CHECK (almostEqual (double( -7), cyc(3)));
CHECK (almostEqual (double( -6), cyc(4)));
CHECK (almostEqual (double(-10), cyc(5)));
CHECK (almostEqual (double( -9), cyc(6)));
CHECK (almostEqual (double( -8), cyc(7)));
CHECK (almostEqual (double( -7), cyc(8)));
CHECK (almostEqual (double( -6), cyc(9)));
CHECK (almostEqual (double(-10), cyc(105)));
CHECK (almostEqual (double(5)/2, limit5 * _EPS<double>));
// one ULP-Step at the limit equals already half the period-length:
CHECK (almostEqual (period5/2, limit5 * _EPS<F>));
// so small steps will alias...
CHECK (limit5 + 1 == limit5);
CHECK (1 == cyclicWrap<double>( 1, 1,_MAX<double>) );
CHECK (2 == cyclicWrap<double>( 2, 1,_MAX<double>) );
CHECK (1 == cyclicWrap<double>(-1, 1,_MAX<double>) );
// HOWEWER, very large periods can be handled (within numeric precision)
CHECK (1 == cyclicWrap<F>( 1, 1,_MAX<F>) );
CHECK (2 == cyclicWrap<F>( 2, 1,_MAX<F>) ); // note: these values are *exact* -- due to std::fmod()
CHECK (3 == cyclicWrap<F>( 3, 1,_MAX<F>) );
CHECK (4 == cyclicWrap<F>( 4, 1,_MAX<F>) );
CHECK (5 == cyclicWrap<F>( 5, 1,_MAX<F>) );
CHECK (1 == cyclicWrap<F>(-1, 1,_MAX<F>) ); // yet this one gets aliased with the minVal
F epsStep = _MAX<F>*_EPS<F>;
// yet the following input is clearly below minVal and will wrap upwards...
F wrapped = cyclicWrap (-epsStep, F(1),_MAX<F>);
CHECK ( 1 < wrapped);
CHECK (wrapped < _MAX<double>);
CHECK (wrapped < _MAX<F>);
}
};

View file

@ -86838,9 +86838,10 @@ Prop.1: dist%3 1 2 0 1 2 0
<arrowlink COLOR="#49a8d6" DESTINATION="ID_636669321" ENDARROW="Default" ENDINCLINATION="87;46;" ID="Arrow_ID_1613615444" STARTARROW="None" STARTINCLINATION="260;14;"/>
<icon BUILTIN="wizard"/>
</node>
<node BACKGROUND_COLOR="#eef0c5" COLOR="#990000" CREATED="1789423635808" ID="ID_840921741" MODIFIED="1789438193575" TEXT="Testabdeckung &#x2014; Grenzf&#xe4;lle">
<icon BUILTIN="pencil"/>
<node CREATED="1789428970477" ID="ID_1439061940" MODIFIED="1789429004081" TEXT="habe gestern schon gesehen: f&#xfc;r sich sind util::flordiv | wrap wohl &#x201e;wasserdicht&#x201c;">
<node COLOR="#338800" CREATED="1789423635808" ID="ID_840921741" MODIFIED="1789525417179" TEXT="Testabdeckung &#x2014; Grenzf&#xe4;lle">
<icon BUILTIN="button_ok"/>
<node COLOR="#435e98" CREATED="1789428970477" FOLDED="true" ID="ID_1439061940" MODIFIED="1789525368840" TEXT="habe gestern schon gesehen: f&#xfc;r sich sind util::flordiv | wrap wohl &#x201e;wasserdicht&#x201c;">
<icon BUILTIN="idea"/>
<node CREATED="1789429005712" ID="ID_1890368766" MODIFIED="1789429031093" TEXT="die Division liefert stets einen kleineren Quotient als der Dividend"/>
<node CREATED="1789429032161" ID="ID_1469836197" MODIFIED="1789429058156" TEXT="die Korrektur-Logik arbeitet &#xbb;gl&#xfc;cklicherweise&#xab; stets zur Dom&#xe4;ne nach innen"/>
<node COLOR="#338800" CREATED="1789433766523" ID="ID_167005830" MODIFIED="1789433779815" TEXT="dies in UtilFloordiv_test dokumentieren">
@ -86853,19 +86854,16 @@ Prop.1: dist%3 1 2 0 1 2 0
</node>
<node COLOR="#338800" CREATED="1789437807095" ID="ID_932126209" MODIFIED="1789437907066" TEXT="zun&#xe4;chst eine einfache Demonstration (als Dokumentation)">
<richcontent TYPE="NOTE"><html>
<head>
</head>
<head/>
<body>
<p>
.... ich k&#246;nnte mir denken, da&#223; mancher Leser das Konzept nicht gleich versteht, deshalb ist ein solches einfaches Beispiel nie verkehrt
</p>
</body>
</html>
</richcontent>
</html></richcontent>
<icon BUILTIN="button_ok"/>
</node>
<node COLOR="#338800" CREATED="1789437908944" ID="ID_880098415" MODIFIED="1789437928788" TEXT="systematisch alle real-existierenden Integral-Typen abdecken">
<node COLOR="#338800" CREATED="1789437908944" FOLDED="true" ID="ID_880098415" MODIFIED="1789525347778" TEXT="systematisch alle real-existierenden Integral-Typen abdecken">
<icon BUILTIN="button_ok"/>
<node BACKGROUND_COLOR="#c8c0b6" COLOR="#435e98" CREATED="1789437929974" ID="ID_532498668" MODIFIED="1789438198770" TEXT="die eigentliche Test-Funktion als Template">
<icon BUILTIN="idea"/>
@ -86888,6 +86886,120 @@ Prop.1: dist%3 1 2 0 1 2 0
<icon BUILTIN="button_ok"/>
</node>
</node>
<node COLOR="#338800" CREATED="1789485850174" FOLDED="true" ID="ID_1074654514" MODIFIED="1789525408140" TEXT="systematische Testabdeckung f&#xfc;r Floatingpoint-Typen entwickeln">
<icon BUILTIN="button_ok"/>
<node COLOR="#435e98" CREATED="1789485871331" ID="ID_552981037" MODIFIED="1789525115154" TEXT="mu&#xdf; zun&#xe4;chst einmal herausfinden was man &#xfc;berhaupt testen k&#xf6;nnte">
<icon BUILTIN="yes"/>
<node CREATED="1789485882603" ID="ID_1932453860" MODIFIED="1789485901968" TEXT="geschenkt: einen einfachen cyclicWrap">
<richcontent TYPE="NOTE"><html>
<head/>
<body>
<p>
ganz analog zum Integral-Fall
</p>
</body>
</html></richcontent>
</node>
<node CREATED="1789485908054" ID="ID_1390215073" MODIFIED="1789485917984" TEXT="mit dem Verhalten am Limit experimentieren"/>
<node CREATED="1789485919101" ID="ID_126516087" MODIFIED="1789485942979" TEXT="Limit-Werte anschauen &#x27f9; sie wachsen mit der Periodenl&#xe4;nge"/>
<node CREATED="1789485944250" ID="ID_758296300" MODIFIED="1789485958924" TEXT="man k&#xf6;nnte also mit Input knapp unter dem Limit testen"/>
<node CREATED="1789485959655" ID="ID_1357089333" MODIFIED="1789518269807" TEXT="oder mit einem Offset knapp unter dem Limit"/>
<node COLOR="#5b280f" CREATED="1789485980355" ID="ID_1975112618" MODIFIED="1789525168334" TEXT="man k&#xf6;nnte zeigen da&#xdf; die Berechnung ungenau wird &#xfc;ber dem Limit">
<icon BUILTIN="button_cancel"/>
<node CREATED="1789486105659" ID="ID_1422733682" MODIFIED="1789486116438" TEXT="f&#xfc;r maxVal &gt; Limit"/>
<node CREATED="1789486117635" ID="ID_1961938709" MODIFIED="1789486122240" TEXT="aber auch f&#xfc;r zu gro&#xdf;e Domain"/>
</node>
<node COLOR="#5b280f" CREATED="1789485994791" ID="ID_1294711924" MODIFIED="1789525135527" TEXT="man k&#xf6;nnte eine Domain-Gr&#xf6;&#xdf;e am Limit testen">
<richcontent TYPE="NOTE"><html>
<head/>
<body>
<p>
die sollte dann aber nur f&#252;r Input-Werte unterhalb des Limits funktionieren, allerdings auch negative Werte; oberhalb des Limits w&#252;rde ich Entgleisungen vermuten
</p>
</body>
</html></richcontent>
<icon BUILTIN="button_cancel"/>
<node CREATED="1789525137057" ID="ID_188158552" MODIFIED="1789525146844" TEXT="nein: l&#xe4;&#xdf;t sich nicht sinnvoll im Test demonstrieren"/>
<node CREATED="1789525147624" ID="ID_888819868" MODIFIED="1789525160044" TEXT="(ich sehe die Entgleisungen wenn ich mir die Zahlen im Debugger anschaue)"/>
</node>
</node>
<node BACKGROUND_COLOR="#e0ceaa" COLOR="#690f14" CREATED="1789486294164" ID="ID_712679325" MODIFIED="1789517587212" TEXT="Beobachtungen: genau am Limit funktioniert das einfache Beispiel noch">
<icon BUILTIN="broken-line"/>
<node CREATED="1789486311768" ID="ID_204301662" MODIFIED="1789486325968" TEXT="ein Epsilon drunter wird absorbiert"/>
<node BACKGROUND_COLOR="#cab2bd" COLOR="#980009" CREATED="1789486326998" ID="ID_185831565" MODIFIED="1789517613464" TEXT="m&#xfc;&#xdf;te also tats&#xe4;chlich ein ULP daneben gehen">
<icon BUILTIN="idea"/>
</node>
<node CREATED="1789517621465" ID="ID_1146256096" MODIFIED="1789517635375" TEXT="tats&#xe4;chlich: das Stepping wird unzuverl&#xe4;ssig"/>
<node CREATED="1789517696551" ID="ID_825633898" MODIFIED="1789517744461" TEXT="limit &#xb7; &#x3b5; &#x2261; &#xbd; periode">
<node CREATED="1789517760503" ID="ID_205743420" MODIFIED="1789517788314" TEXT="das haben wir aber genau so definiert...">
<icon BUILTIN="messagebox_warning"/>
</node>
<node CREATED="1789517771893" ID="ID_624480094" MODIFIED="1789517791174" TEXT="insofern ist das kein &#xbb;Test&#xab;">
<icon BUILTIN="stop-sign"/>
<node CREATED="1789517806226" ID="ID_1031466232" MODIFIED="1789517811675" TEXT="ein Test sollte Verhalten zeigen"/>
<node CREATED="1789517812425" ID="ID_1660313139" MODIFIED="1789517821515" TEXT="und nicht eine Definition replizieren"/>
</node>
</node>
</node>
<node CREATED="1789518828341" ID="ID_452450374" MODIFIED="1789525394621">
<richcontent TYPE="NODE"><html>
<head/>
<body>
<p>
Problem ist: hier kann ich kein &#187;<b>korrekt</b>&#171; oder &#187;<b>defekt</b>&#171; zeigen
</p>
</body>
</html></richcontent>
<linktarget COLOR="#57fec0" DESTINATION="ID_452450374" ENDARROW="Default" ENDINCLINATION="-182;8;" ID="Arrow_ID_1586349868" SOURCE="ID_409017691" STARTARROW="None" STARTINCLINATION="87;8;"/>
<icon BUILTIN="broken-line"/>
<node CREATED="1789518866588" ID="ID_1949224608" MODIFIED="1789518885765" TEXT="das Verhalten wird zum Limit hin zunehmend &#xbb;unscharf&#xab;"/>
<node CREATED="1789518886522" ID="ID_515488673" MODIFIED="1789518921574" TEXT="wie viel Unsch&#xe4;rfe tolerierbar ist l&#xe4;&#xdf;t sich nicht generisch sagen">
<node CREATED="1789518978509" ID="ID_1991175199" MODIFIED="1789518986039" TEXT="es gibt keine &#xbb;Schrittweite&#xab;"/>
<node CREATED="1789518987204" ID="ID_30471047" MODIFIED="1789519013068" TEXT="wann ist ein &quot;off by 0.1&#xab; noch tolerierbar??"/>
</node>
<node CREATED="1789518902636" ID="ID_1841879477" MODIFIED="1789518930946" TEXT="letztlich sind floating-point-numbers immer &#xbb;unscharf&#xab;"/>
<node CREATED="1789518945761" ID="ID_1474263016" MODIFIED="1789518957882" TEXT="jedwede Demonstration w&#xfc;rde eben nur dieses darstellen"/>
<node BACKGROUND_COLOR="#f7dba2" COLOR="#690f14" CREATED="1789522669935" ID="ID_1829288235" MODIFIED="1789522703291" TEXT="und : eine klar verst&#xe4;ndliche Demonstration ist schwer">
<icon BUILTIN="messagebox_warning"/>
<node COLOR="#b21478" CREATED="1789522720139" ID="ID_1872531495" MODIFIED="1789522906208" TEXT="Grenzwerte sind &#xbb;unhandlich&#xab;">
<richcontent TYPE="NOTE"><html>
<head/>
<body>
<p>
sobald man mit &#949; und ULPs hantiert, sind die numerischen Werte nur so minuti&#246;s verschieden, da&#223; man es in der &#252;blichen, gerundeten Darstellung gar nicht sieht. Damit scheiden alle Test-Methoden aus, die &quot;einfache Zahlen&quot; verwenden und ein festes Ergebnis vorgeben. Hinzu kommt, da&#223; man auch einen &#949;-Vergleich machen mu&#223;, und dann ohne gesonderte &#220;berlegung nicht offensichtlich ist, welche &quot;Unsch&#228;rfe&quot; nun &quot;unscharf&quot; ist, und welche Teil des Test-Subjekts ist. Nicht da&#223; man das alles irgendwie &quot;hinbekommt&quot; &#8212; aber der Leser versteht nicht mehr, <i>was hier gespielt wird.</i>
</p>
</body>
</html></richcontent>
<font NAME="SansSerif" SIZE="11"/>
<icon BUILTIN="smily_bad"/>
</node>
</node>
</node>
<node BACKGROUND_COLOR="#c8c0b6" COLOR="#435e98" CREATED="1789521749900" ID="ID_912513687" MODIFIED="1789525194850" TEXT="sollte aber trotzdem wesentliche Elemente demonstrieren">
<icon BUILTIN="yes"/>
<node CREATED="1789521771353" ID="ID_559262092" MODIFIED="1789522935600" TEXT="was das &#xbb;limit&#xab; bedeutet : n&#xe4;mlich im Verh&#xe4;ltnis zur Periode"/>
<node CREATED="1789521783888" ID="ID_1660128606" MODIFIED="1789521817663" TEXT="da&#xdf; das Wrapping mit einem sehr gro&#xdf;en Intervall funktioniert"/>
<node CREATED="1789521818763" ID="ID_22678032" MODIFIED="1789521843436" TEXT="da&#xdf; es nahe der Interval-Obergrenze zu einem Aliasing kommen kann"/>
</node>
<node BACKGROUND_COLOR="#a6fdce" COLOR="#338800" CREATED="1789521853775" ID="ID_1420946197" MODIFIED="1789525101636" TEXT="Test formulieren...">
<icon BUILTIN="yes"/>
<node CREATED="1789521897465" ID="ID_1330009757" MODIFIED="1789521904636" TEXT="zun&#xe4;chst alles f&#xfc;r double"/>
<node CREATED="1789521905359" ID="ID_284924920" MODIFIED="1789521995911" TEXT="aber bereits mit Konversionen und almostEqual"/>
<node CREATED="1789525027288" ID="ID_1906103710" MODIFIED="1789525036691" TEXT="dann auf generischen Float umstellen...">
<node CREATED="1789525037796" ID="ID_767684162" MODIFIED="1789525039283" TEXT="float"/>
<node CREATED="1789525040023" ID="ID_1224952753" MODIFIED="1789525041834" TEXT="double"/>
<node CREATED="1789525042606" ID="ID_815586095" MODIFIED="1789525045834" TEXT="long double"/>
</node>
</node>
</node>
<node BACKGROUND_COLOR="#174956" COLOR="#8dfd49" CREATED="1788313090668" HGAP="15" ID="ID_904924646" MODIFIED="1789525281531" STYLE="bubble" TEXT="ufff: GR&#xdc;N" VSHIFT="2">
<edge COLOR="#61fe5b" STYLE="bezier" WIDTH="thin"/>
<icon BUILTIN="ksmiletris"/>
<node COLOR="#0f3e46" CREATED="1789525285095" HGAP="36" ID="ID_409017691" MODIFIED="1789525404221" STYLE="fork" TEXT="das ist schon &#xbb;advanced stuff&#xab;...." VSHIFT="-10">
<arrowlink COLOR="#57fec0" DESTINATION="ID_452450374" ENDARROW="Default" ENDINCLINATION="-182;8;" ID="Arrow_ID_1586349868" STARTARROW="None" STARTINCLINATION="87;8;"/>
<font NAME="SansSerif" SIZE="10"/>
</node>
</node>
</node>
</node>
</node>