From eef5ddef0e99d639c2239a696bf457b8a0b0e8e7 Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Wed, 16 Sep 2026 19:42:55 +0200 Subject: [PATCH] Parameters: integrate the cyclic-wrap function a much more complete and robust implementation, but requires a precision limit to be pre-computed in the floating-point case --- src/lib/par/scale.hpp | 30 ++++++++++++++++++++------ src/lib/util-quant.hpp | 2 +- tests/lib/par/parameter-scale-test.cpp | 9 ++++++-- 3 files changed, 31 insertions(+), 10 deletions(-) diff --git a/src/lib/par/scale.hpp b/src/lib/par/scale.hpp index 56e2d12a6..bde3ca386 100644 --- a/src/lib/par/scale.hpp +++ b/src/lib/par/scale.hpp @@ -28,14 +28,18 @@ #define LIB_PAR_SCALE_H +#include "lib/error.hpp" #include "lib/par/domain.hpp" -#include "lib/util.hpp" +#include "lib/util-quant.hpp" +#include "lib/format-string.hpp" +//#include "lib/util.hpp" #include namespace lib { namespace par { + namespace err = lumiera::error; using std::optional; @@ -66,12 +70,13 @@ namespace par { optional neutral{}; optional defVal{}; /////////////////////////////OOO »Sentinels« and nominal scales? - bool cyclic{false}; + VAL cyclicLim =0; /* ===== information functions ===== */ bool isValid() const; bool isFactor() const; + bool isCyclic() const; /* ===== conforming operations ===== */ @@ -113,7 +118,7 @@ namespace par { and (not minVal or *minVal <= *defVal) ) ) - and (not cyclic + and (not isCyclic() or (minVal and maxVal and *minVal < *maxVal) ) ; @@ -128,6 +133,13 @@ namespace par { and *neutral == VAL(1); } + template + inline bool + Scale::isCyclic() const + { + return VAL(0) != cyclicLim; + } + /* ===== conforming operations ===== */ @@ -150,13 +162,17 @@ namespace par { inline VAL Scale::conform (VAL const& rawVal) const { - if (cyclic) + if (isCyclic()) { REQUIRE (minVal and maxVal); REQUIRE (*minVal < *maxVal); - const VAL PERIOD = *maxVal - *minVal; - ENSURE (0 < PERIOD); - return (rawVal - *minVal) % PERIOD; + if (std::is_floating_point_v) + if (not (std::abs (rawVal) < cyclicLim)) + throw err::Invalid {util::_Fmt{"Parameter value %4.2g beyond supported numeric precision " + "for cyclic wrapping into (%4.2g ... %4.2g("} + % rawVal % minVal % maxVal + }; + return util::cyclicWrap (rawVal, *minVal, *maxVal); } if (minVal and rawVal < *minVal) return *minVal; diff --git a/src/lib/util-quant.hpp b/src/lib/util-quant.hpp index b77cc39a7..843a2ed77 100644 --- a/src/lib/util-quant.hpp +++ b/src/lib/util-quant.hpp @@ -244,7 +244,7 @@ namespace util { inline constexpr F limit_cyclicWrap (F period) { - REQUIRE (std::isfinite (period)); + REQUIRE (std::isnormal (period)); REQUIRE (F(0) < period); F limit = period / std::numeric_limits::epsilon(); if (not std::isfinite (limit)) diff --git a/tests/lib/par/parameter-scale-test.cpp b/tests/lib/par/parameter-scale-test.cpp index e6dc296e0..7ee377a5e 100644 --- a/tests/lib/par/parameter-scale-test.cpp +++ b/tests/lib/par/parameter-scale-test.cpp @@ -58,7 +58,7 @@ namespace test{ run (Arg) { simpleUsage(); - verify_Bounds(); + verify_Conforming(); } @@ -80,8 +80,13 @@ namespace test{ * @todo WIP 9/26 🔁 define ⟶ implement */ void - verify_Bounds() + verify_Conforming() { + Scale scale; + CHECK (1e5f == scale.conform (1e5f)); + + scale.maxVal = -5.0f; + CHECK (-5.0f == scale.conform (1e5f)); } };