Parameters: numeric-types deep dive
Turns out that implementing a cyclically wrapping domain properly is much more tricky than initially anticipated. After initial analysis I decided where to draw the line and what ''esoteric features'' aspects to leave out. Then I used Knowledge-Technology (Claude) to scrutinise and improve my implementation, and to learn a lot more about integral arithmetics in general.
This commit is contained in:
parent
426d7c610e
commit
77257c5653
9 changed files with 1071 additions and 483 deletions
518
research/try.cpp
518
research/try.cpp
|
|
@ -1,6 +1,7 @@
|
|||
/* try.cpp - to try out and experiment with new features....
|
||||
* scons will create the binary bin/try
|
||||
*/
|
||||
// 09/26 - ways to build a cyclically wrapping number domain
|
||||
// 03/26 - runtime index access and iteration over tuple-likes
|
||||
// 06/25 - provide a concept to accept _tuple-like_ objects
|
||||
// 06/25 - investigate function type detection of std::bind Binders
|
||||
|
|
@ -11,425 +12,146 @@
|
|||
|
||||
|
||||
/** @file try.cpp
|
||||
* Work out a construction scheme to build an efficient element-accessor to data
|
||||
* in a tuple-like with run-time indexing. As an extension, run-time pulled iteration
|
||||
* can be established, using the IterExplorer framework.
|
||||
* @see lib::meta::FunTrampoline
|
||||
* @see lib::meta::TupleIdxAdaptor
|
||||
* Investigate techniques for creating a cyclically wrapping numeric domain.
|
||||
* Actually this could be achieved with my `floordiv()` utility, yet the ramifications
|
||||
* were not clear -- so I settled to build a stand-alone solution, based on the naive modulus
|
||||
* technique, attempting to fix the negative offsets. It became clear that this approach would
|
||||
* have to consider the domain boundaries and identify cases that could not be handlet. At that
|
||||
* point, I used Claude to scrutinise and improve my draft, leading to a generic tool that is
|
||||
* now relocated into the \ref util-quant.hpp header
|
||||
* @see lib::par::Scale
|
||||
*/
|
||||
|
||||
|
||||
#include "lib/format-cout.hpp"
|
||||
#include "lib/format-util.hpp"
|
||||
#include "test/test-helper.hpp"
|
||||
#include "test/diagnostic-output.hpp"
|
||||
#include "lib/meta/common-result.hpp"
|
||||
#include "lib/meta/tuple-concept.hpp"
|
||||
#include "lib/meta/tuple-helper.hpp"
|
||||
#include "lib/meta/function.hpp"
|
||||
#include "lib/iter-adapter.hpp"
|
||||
#include "lib/hetero-data.hpp"
|
||||
#include "lib/meta/trait.hpp"
|
||||
#include "lib/util-quant.hpp"
|
||||
#include "lib/util.hpp"
|
||||
|
||||
#include <array>
|
||||
|
||||
using std::string;
|
||||
|
||||
|
||||
template<typename X>
|
||||
#include <cmath>
|
||||
|
||||
template<typename NUM>
|
||||
constexpr NUM _MAX = std::numeric_limits<NUM>::max();
|
||||
|
||||
template<typename NUM>
|
||||
constexpr NUM _MIN = std::numeric_limits<NUM>::lowest();
|
||||
|
||||
|
||||
|
||||
|
||||
template<typename N>
|
||||
void
|
||||
show()
|
||||
{
|
||||
SHOW_TYPE(X)
|
||||
}
|
||||
showCase (N val, N minVal, N maxVal)
|
||||
{
|
||||
cout << "val:"<<val<<" (min:"<<minVal<<" , max:"<<maxVal<<") ->"<< util::cyclicWrap (val,minVal,maxVal)<<endl;
|
||||
}
|
||||
|
||||
template<lib::meta::tuple_like X>
|
||||
template<typename N>
|
||||
void
|
||||
show()
|
||||
{
|
||||
cout << "Tulik!! "<< ::test::showType<X>() <<endl;
|
||||
lib::meta::forEachIDX<X> ([](auto i)
|
||||
{
|
||||
using Elm = std::tuple_element_t<i, X>;
|
||||
cout <<" "<<uint(i)<<": "<< ::test::showType<Elm>() <<endl;
|
||||
});
|
||||
}
|
||||
showFloorDiv (N num, N den)
|
||||
{
|
||||
cout << "FloorDiv:"<<num<<" / "<<den<<" ->"<< util::floordiv(num,den)<<endl;
|
||||
}
|
||||
|
||||
namespace lib {
|
||||
namespace meta{
|
||||
|
||||
#if false ///////////////////////////////////////////////////////////////////////////////////////////////////TODO : new generic library functions
|
||||
/**
|
||||
* Concept to require a generic type sequence `Types<TYPES...>`
|
||||
*/
|
||||
template<class TSEQ>
|
||||
concept typeseq = requires (TSEQ seq)
|
||||
{
|
||||
// TSEQ can invoke a function with a Types<TYPES> subclass
|
||||
[]<typename...TYPES>(Types<TYPES...>&){/*placeholder*/}(seq);
|
||||
};
|
||||
#endif ///////////////////////////////////////////////////////////////////////////////////////////////////(End) : new generic library functions
|
||||
|
||||
|
||||
template<template<class> class BUILD, typeseq TSEQ>
|
||||
class FunTrampoline
|
||||
{
|
||||
using AllElm = ElmTypes<TSEQ>;
|
||||
using Indices = AllElm::Idx;
|
||||
using Builder = BUILD<TSEQ>;
|
||||
using Functor = Builder::Functor;
|
||||
using Table = std::array<Functor, AllElm::SIZ>;
|
||||
|
||||
template<size_t...idx>
|
||||
static constexpr Table
|
||||
buildFunctors (std::index_sequence<idx...>)
|
||||
{
|
||||
return {Builder().template build<idx>()...};
|
||||
}
|
||||
|
||||
static constexpr Table functors = buildFunctors (Indices{});
|
||||
|
||||
public:
|
||||
static constexpr size_t size() { return functors.size(); }
|
||||
static constexpr size_t memSiz() { return sizeof(functors); }
|
||||
|
||||
static Functor const&
|
||||
dispatch (size_t idx)
|
||||
{
|
||||
ENSURE (idx < functors.size());
|
||||
return functors[idx];
|
||||
}
|
||||
|
||||
using ResType = _Fun<Functor>::Ret;
|
||||
};
|
||||
|
||||
|
||||
#if false ///////////////////////////////////////////////////////////////////////////////////////////////////TODO : new generic library functions
|
||||
template<typename T>
|
||||
using isConst = std::is_const<remove_reference_t<T>>;
|
||||
template<typename T>
|
||||
using isLRef = std::is_lvalue_reference<T>;
|
||||
|
||||
|
||||
template<class SEQ>
|
||||
using Any_Const = ElmTypes<SEQ>::template OrAll<isConst>;
|
||||
|
||||
template<class SEQ>
|
||||
using All_LRef = ElmTypes<SEQ>::template AndAll<isLRef>;
|
||||
|
||||
template<class SEQ>
|
||||
static constexpr bool Any_Const_v = Any_Const<SEQ>::value;
|
||||
|
||||
template<class SEQ>
|
||||
static constexpr bool All_LRef_v = All_LRef<SEQ>::value;
|
||||
|
||||
|
||||
|
||||
|
||||
template<typename X, typename U, typename...TYPES>
|
||||
struct ProbeCommonType
|
||||
{
|
||||
|
||||
template<typename T1, typename T2>
|
||||
using Reconciled = decltype(true ? std::declval<T1>() : std::declval<T2>());
|
||||
|
||||
|
||||
template<typename RES>
|
||||
struct FoundResult
|
||||
: std::true_type
|
||||
{
|
||||
using Type = RES;
|
||||
};
|
||||
struct NoResultFound
|
||||
: std::false_type
|
||||
{ };
|
||||
|
||||
template<class T1
|
||||
,class T2
|
||||
,typename = Reconciled<T1,T2>>
|
||||
static FoundResult<Reconciled<T1,T2>>
|
||||
check(int);
|
||||
|
||||
template<class,class>
|
||||
static NoResultFound
|
||||
check(...);
|
||||
|
||||
using FirstResult = decltype(check<X,U>(0));
|
||||
|
||||
template<bool success, typename...TS>
|
||||
struct Join
|
||||
{
|
||||
using Result = FirstResult;
|
||||
};
|
||||
|
||||
template<typename T1, typename...TS>
|
||||
struct Join<true, T1,TS...>
|
||||
{
|
||||
//wenn RES erfolgreich
|
||||
using CT = FirstResult::Type;
|
||||
using Result = ProbeCommonType<CT,T1,TS...>::Result;
|
||||
};
|
||||
|
||||
using Result = Join<FirstResult::value, TYPES...>::Result;
|
||||
};
|
||||
|
||||
|
||||
|
||||
template<typename...TYPES>
|
||||
struct CommonResult
|
||||
: ProbeCommonType<TYPES...>::Result
|
||||
{ };
|
||||
|
||||
template<typename T1>
|
||||
struct CommonResult<T1>
|
||||
: CommonResult<T1,T1>
|
||||
{ };
|
||||
|
||||
template<>
|
||||
struct CommonResult<>
|
||||
: std::false_type
|
||||
{ };
|
||||
|
||||
template<typename...TYPES>
|
||||
struct CommonResult<Types<TYPES...>>
|
||||
: CommonResult<TYPES...>
|
||||
{ };
|
||||
|
||||
template<typename...TYPES>
|
||||
using CommonResult_t = CommonResult<TYPES...>::Type;
|
||||
|
||||
template<typename...TYPES>
|
||||
static constexpr bool has_CommonResult_v = CommonResult<TYPES...>::value;
|
||||
|
||||
|
||||
template<typename...TYPES>
|
||||
concept reconcilable = requires
|
||||
{
|
||||
typename CommonResult<TYPES...>::Type;
|
||||
};
|
||||
|
||||
template<typename...TYPES>
|
||||
concept reconcilable_ref = reconcilable<TYPES...>
|
||||
and isLRef_v<CommonResult_t<TYPES...>>;
|
||||
|
||||
template<typename...TYPES>
|
||||
struct CommonResultTraits
|
||||
{
|
||||
static_assert (reconcilable<TYPES...>, "unable to reconcile these types");
|
||||
|
||||
using CommonVal = std::common_type_t<TYPES...>;
|
||||
using ResType = CommonResult_t<TYPES...>;
|
||||
//NOTE: std::common_type decays (strips CV and ref)
|
||||
static constexpr bool makeConst = isConst_v<ResType>;
|
||||
static constexpr bool exposeRef = isLRef_v<ResType>;
|
||||
};
|
||||
#endif ///////////////////////////////////////////////////////////////////////////////////////////////////(End) : new generic library functions
|
||||
|
||||
|
||||
|
||||
template<class TUP>
|
||||
class TupleIndexAdaptor
|
||||
: public TUP
|
||||
{
|
||||
template<typeseq SEQ>
|
||||
struct SubscriptBuilder
|
||||
{
|
||||
using Tuple = RefTraits<TUP>::value_type;
|
||||
using AddRefs = ElmTypes<SEQ>::template Apply<std::add_lvalue_reference_t>;
|
||||
using ResType = CommonResult_t<AddRefs>;
|
||||
using Functor = ResType(*)(Tuple&);
|
||||
|
||||
template<size_t idx>
|
||||
constexpr auto
|
||||
build()
|
||||
{
|
||||
return [](Tuple& tuple) -> ResType
|
||||
{
|
||||
return getElm<idx> (tuple);
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
using TypeSeq = ElmTypes<TUP>::Seq;
|
||||
using Accessor = FunTrampoline<SubscriptBuilder, TypeSeq>;
|
||||
public:
|
||||
using ResType = Accessor::ResType;
|
||||
using reference = ResType;
|
||||
using value_Type = RefTraits<ResType>::value_type;
|
||||
|
||||
template<typename T>
|
||||
TupleIndexAdaptor(T&& dataSrc)
|
||||
: TUP{forward<T> (dataSrc)}
|
||||
{ }
|
||||
|
||||
using TUP::TUP;
|
||||
|
||||
static constexpr size_t size() { return Accessor::size(); }
|
||||
|
||||
ResType
|
||||
operator[] (size_t idx)
|
||||
{
|
||||
return Accessor::dispatch(idx)(*this);
|
||||
}
|
||||
};
|
||||
|
||||
template<class TUP>
|
||||
TupleIndexAdaptor(TUP) -> TupleIndexAdaptor<std::decay_t<TUP>>;
|
||||
}}
|
||||
|
||||
using namespace lib::meta;
|
||||
|
||||
template<typeseq SEQ>
|
||||
struct Stringify
|
||||
{
|
||||
using Tuple = ElmTypes<SEQ>::Tup;
|
||||
using Functor = string(*)(Tuple const&);
|
||||
|
||||
template<size_t idx>
|
||||
constexpr auto
|
||||
build()
|
||||
{
|
||||
using ElmT = std::tuple_element_t<idx, Tuple>;
|
||||
|
||||
return [](Tuple const& tuple)
|
||||
{
|
||||
return util::toString<ElmT> (getElm<idx> (tuple));
|
||||
};
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
||||
struct Uber { };
|
||||
struct Untr : Uber { };
|
||||
struct Unta : Uber { };
|
||||
template<typename N>
|
||||
void
|
||||
showFloorWrap (N num, N den)
|
||||
{
|
||||
auto fwrap = util::floorwrap (num,den);
|
||||
cout << "FloorWrap:"<<num<<" / "<<den<<" -> q="<<fwrap.quot<<" r="<<fwrap.rem<<endl;
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
main (int, char**)
|
||||
{
|
||||
using Tup = std::tuple<long,short>;
|
||||
using Arr = std::array<int,3>;
|
||||
using Hetero = lib::HeteroData<int,string>::Chain<short>::ChainExtent<bool,lib::meta::Nil>::ChainType;
|
||||
const int PER = 3;
|
||||
int i = -5;
|
||||
int d = i / PER;
|
||||
cout << "d="<<d<<endl;
|
||||
cout << "fd="<<util::floordiv(i,PER)<<endl;
|
||||
|
||||
SHOW_EXPR((lib::meta::tuple_like<Tup> ))
|
||||
SHOW_EXPR((lib::meta::tuple_like<Arr> ))
|
||||
SHOW_EXPR((lib::meta::tuple_like<Hetero> ))
|
||||
SHOW_EXPR((lib::meta::tuple_like<string> ))
|
||||
SHOW_EXPR((lib::meta::tuple_like<int> ))
|
||||
uint u = _MAX<uint>;
|
||||
|
||||
show<Tup>();
|
||||
show<Arr>();
|
||||
show<Hetero>();
|
||||
show<int>();
|
||||
cout << u << " fd="<<util::floordiv(u,uint(PER))<<" ...re="<<PER*util::floordiv(u,uint(PER))<<endl;
|
||||
|
||||
using TupStringy = FunTrampoline<Stringify, ElmTypes<Tup>::Seq>;
|
||||
SHOW_EXPR(TupStringy::size())
|
||||
showFloorDiv (7,5);
|
||||
showFloorDiv (-7,5);
|
||||
showFloorDiv (7,-5);
|
||||
showFloorDiv (-7,-5);
|
||||
|
||||
Tup tup23{2,3};
|
||||
Tup tup42{4,2};
|
||||
SHOW_TYPE(decltype(std::get<1>(tup42)))
|
||||
SHOW_EXPR(std::get<1>(tup42))
|
||||
SHOW_EXPR(TupStringy::dispatch(0)(tup23))
|
||||
SHOW_EXPR(TupStringy::dispatch(0)(tup42))
|
||||
SHOW_EXPR(TupStringy::dispatch(1)(tup23))
|
||||
SHOW_EXPR(TupStringy::dispatch(1)(tup42))
|
||||
showFloorWrap (7,5);
|
||||
showFloorWrap (-7,5);
|
||||
showFloorWrap (7,-5);
|
||||
showFloorWrap (-7,-5);
|
||||
|
||||
using T1 = Types<int const&,long&, char&>;
|
||||
using T2 = Types<int*,void*>;
|
||||
using T3 = Types<int,string>;
|
||||
SHOW_EXPR((has_CommonResult_v<T1>))
|
||||
SHOW_EXPR((has_CommonResult_v<T2>))
|
||||
SHOW_EXPR((has_CommonResult_v<T3>))
|
||||
SHOW_EXPR((has_CommonResult_v<>))
|
||||
SHOW_EXPR((has_CommonResult_v<float*>))
|
||||
SHOW_EXPR((has_CommonResult_v<float*,void* const>))
|
||||
|
||||
SHOW_TYPE(CommonResult_t<T1>)
|
||||
SHOW_TYPE(CommonResult_t<T2>)
|
||||
SHOW_TYPE(CommonResult_t<float*>)
|
||||
using CoTx = CommonResult_t<float*,float const*>;
|
||||
SHOW_TYPE(CoTx)
|
||||
showFloorWrap (_MAX<int>,1);
|
||||
showFloorWrap (_MAX<int>,2);
|
||||
showFloorWrap (_MAX<int>,5);
|
||||
|
||||
showFloorWrap (_MIN<int>,1);
|
||||
showFloorWrap (_MIN<int>,2);
|
||||
showFloorWrap (_MIN<int>,5);
|
||||
|
||||
showFloorWrap (_MAX<int>,-1);
|
||||
showFloorWrap (_MAX<int>,-2);
|
||||
showFloorWrap (_MAX<int>,-5);
|
||||
|
||||
showFloorDiv (_MAX<int>,1);
|
||||
showFloorDiv (_MAX<int>,2);
|
||||
showFloorDiv (_MAX<int>,5);
|
||||
|
||||
showFloorDiv (_MIN<int>,1);
|
||||
showFloorDiv (_MIN<int>,2);
|
||||
showFloorDiv (_MIN<int>,5);
|
||||
|
||||
showFloorDiv (_MAX<int>,-1);
|
||||
showFloorDiv (_MAX<int>,-2);
|
||||
showFloorDiv (_MAX<int>,-5);
|
||||
|
||||
showFloorWrap (_MAX<uint>,1u);
|
||||
showFloorWrap (_MAX<uint>,2u);
|
||||
showFloorWrap (_MAX<uint>,5u);
|
||||
|
||||
showFloorDiv (_MAX<uint>,1u);
|
||||
showFloorDiv (_MAX<uint>,2u);
|
||||
showFloorDiv (_MAX<uint>,5u);
|
||||
|
||||
showCase<int>(-7,-5,5);
|
||||
showCase<int>(-6,-5,5);
|
||||
showCase<int>(-5,-5,5);
|
||||
showCase<int>(-4,-5,5);
|
||||
showCase<int>(+5,-5,5);
|
||||
showCase<int>(+6,-5,5);
|
||||
showCase<int>(+7,-5,5);
|
||||
showCase<int>(+8,-5,5);
|
||||
showCase<int>(+9,-5,5);
|
||||
showCase<int>(+10,-5,5);
|
||||
showCase<int>(+11,-5,5);
|
||||
showCase<int>(+12,-5,5);
|
||||
showCase<int>(+14,-5,5);
|
||||
showCase<int>(+15,-5,5);
|
||||
showCase<int>( -7,-10,-7);
|
||||
showCase<int>( -8,-10,-7);
|
||||
showCase<int>( -9,-10,-7);
|
||||
showCase<int>(-10,-10,-7);
|
||||
showCase<int>(-11,-10,-7);
|
||||
showCase<int>(-12,-10,-7);
|
||||
showCase<int>(-13,-10,-7);
|
||||
showCase<int>(-14,-10,-7);
|
||||
showCase<int>(-15,-10,-7);
|
||||
|
||||
showCase<uint>(5,1000,1003);
|
||||
|
||||
|
||||
TupleIndexAdaptor<Tup> idxAdapt{tup42};
|
||||
SHOW_EXPR(sizeof(tup42))
|
||||
SHOW_EXPR(sizeof(idxAdapt))
|
||||
SHOW_TYPE(TupleIndexAdaptor<Tup>::ResType)
|
||||
SHOW_EXPR(idxAdapt.size())
|
||||
SHOW_EXPR(idxAdapt[0])
|
||||
SHOW_EXPR(idxAdapt[1])
|
||||
SHOW_EXPR(getElm<0>(static_cast<Tup&> (idxAdapt)))
|
||||
SHOW_EXPR(getElm<1>(static_cast<Tup&> (idxAdapt)))
|
||||
SHOW_TYPE(decltype(idxAdapt[1]))
|
||||
|
||||
using Tup2 = std::tuple<int,int&, int>;
|
||||
int ii{23};
|
||||
Tup2 tup21{-1,ii,55};
|
||||
TupleIndexAdaptor idxApt2{tup21};
|
||||
SHOW_TYPE(decltype(tup21))
|
||||
using IdxApt2 = decltype(idxApt2);
|
||||
SHOW_TYPE(IdxApt2)
|
||||
SHOW_TYPE(IdxApt2::ResType)
|
||||
SHOW_EXPR(idxApt2[0])
|
||||
SHOW_EXPR(idxApt2[1])
|
||||
SHOW_EXPR(idxApt2[2])
|
||||
SHOW_EXPR(++idxApt2[0])
|
||||
SHOW_EXPR(idxApt2[1]++)
|
||||
SHOW_EXPR(++++idxApt2[2])
|
||||
SHOW_EXPR(tup21)
|
||||
SHOW_EXPR(Tup2(idxApt2))
|
||||
SHOW_EXPR(ii)
|
||||
|
||||
using TupIt = lib::IterableDecorator<lib::IdxStoreCore<IdxApt2>>;
|
||||
TupIt tupIt{move(idxApt2)};
|
||||
SHOW_EXPR( bool(tupIt) )
|
||||
SHOW_EXPR( *tupIt )
|
||||
SHOW_TYPE(decltype(*tupIt))
|
||||
++*tupIt;
|
||||
--ii;
|
||||
SHOW_EXPR( *tupIt )
|
||||
SHOW_EXPR(util::join(tupIt, "▶▶"))
|
||||
|
||||
SHOW_EXPR(( reconcilable<long,string> ))
|
||||
SHOW_EXPR(( reconcilable<long&,string&> ))
|
||||
SHOW_EXPR(( reconcilable<long,short> ))
|
||||
SHOW_EXPR(( reconcilable<long&,short&> ))
|
||||
SHOW_EXPR(( reconcilable_ref<long,string> ))
|
||||
SHOW_EXPR(( reconcilable_ref<long&,string&> ))
|
||||
SHOW_EXPR(( reconcilable_ref<long,short> ))
|
||||
SHOW_EXPR(( reconcilable_ref<long&,const long&> ))
|
||||
using TX = CommonResult_t<long&,const long&>;
|
||||
SHOW_TYPE(TX)
|
||||
SHOW_EXPR(( reconcilable<float*,void*> ))
|
||||
SHOW_EXPR(( reconcilable<void,void*> ))
|
||||
SHOW_EXPR(( reconcilable<float*&,void*&> ))
|
||||
using TX2 = CommonResult_t<float*&,float*&>;
|
||||
SHOW_TYPE(TX2)
|
||||
SHOW_EXPR(( reconcilable<Uber,Unta> ))
|
||||
SHOW_EXPR(( reconcilable<Untr,Unta> ))
|
||||
SHOW_EXPR(( reconcilable<Untr&,Unta&> ))
|
||||
SHOW_EXPR(( reconcilable<Uber&,Unta&> ))
|
||||
SHOW_EXPR(( reconcilable_ref<Uber&,Unta&> ))
|
||||
SHOW_EXPR(( reconcilable_ref<Uber&,Unta> ))
|
||||
SHOW_EXPR(( reconcilable<Uber&,Unta> ))
|
||||
SHOW_EXPR(( reconcilable<Uber,Untr&> ))
|
||||
using TX3 = CommonResult_t<Uber&,Unta>;
|
||||
SHOW_TYPE(TX3)
|
||||
using TX4 = CommonResult_t<Unta&, const Uber&>;
|
||||
SHOW_TYPE(TX4)
|
||||
SHOW_EXPR(( reconcilable<Uber&,Unta&,Untr&> ))
|
||||
using TX5 = CommonResult_t<const Uber&,Unta&,Untr&>;
|
||||
SHOW_TYPE(TX5)
|
||||
using TX6 = CommonResult_t<const int&,uint16_t,int64_t>;
|
||||
SHOW_TYPE(TX6)
|
||||
SHOW_EXPR(( CommonResult<int,string>::value ))
|
||||
SHOW_EXPR(( CommonResult<int,string,long>::value ))
|
||||
SHOW_EXPR(( CommonResult<const int,int&>::value ))
|
||||
SHOW_EXPR(( reconcilable<const int&,uint16_t,int64_t> ))
|
||||
SHOW_EXPR(( reconcilable<const Uber&,Unta&,Untr&> ))
|
||||
SHOW_EXPR(( reconcilable<Uber&,const Unta&,Untr&> ))
|
||||
showCase<float>(0.2 ,-2,-1.8);
|
||||
showCase<float>(0 ,-2,-1.8);
|
||||
showCase<float>(-0.8,-2,-1.8);
|
||||
showCase<float>(-1.79,-2,-1.8);
|
||||
showCase<float>(-1.8,-2,-1.8);
|
||||
showCase<float>(-1.81,-2,-1.8);
|
||||
showCase<float>(-1.99,-2,-1.8);
|
||||
showCase<float>(-2 ,-2,-1.8);
|
||||
showCase<float>(-2.01,-2,-1.8);
|
||||
|
||||
|
||||
cout << "\n.gulp." <<endl;
|
||||
return 0;
|
||||
|
|
|
|||
|
|
@ -30,8 +30,11 @@
|
|||
#ifndef LIB_HASH_VALUE_H
|
||||
#define LIB_HASH_VALUE_H
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
#include <cstdlib>
|
||||
#else
|
||||
#include <stdlib.h>
|
||||
#endif
|
||||
|
||||
/**
|
||||
* storage for a Lumiera unique ID,
|
||||
|
|
@ -44,8 +47,6 @@ typedef lumiera_uid* LumieraUid;
|
|||
|
||||
#ifdef __cplusplus /* =========== C++ definitions ====================== */
|
||||
|
||||
#include <climits>
|
||||
|
||||
namespace lib {
|
||||
|
||||
/** a STL compatible hash value */
|
||||
|
|
|
|||
|
|
@ -21,7 +21,6 @@
|
|||
#ifndef LIB_INTEGRAL_H
|
||||
#define LIB_INTEGRAL_H
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
|
||||
|
|
@ -38,5 +37,5 @@ static_assert(10 <= sizeof(f128));
|
|||
|
||||
const uint LIFE_AND_UNIVERSE_4EVER = 42;
|
||||
|
||||
|
||||
|
||||
#endif /*LIB_INTEGRAL_H*/
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@
|
|||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -46,7 +46,6 @@
|
|||
|
||||
#include "lib/hash-value.h" /* Types lumiera_uid and LumieraUid */
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -22,12 +22,15 @@
|
|||
|
||||
#include "lib/integral.hpp"
|
||||
|
||||
#include <type_traits>
|
||||
#include <concepts>
|
||||
#include <cstdlib>
|
||||
#include <climits>
|
||||
#include <cfloat>
|
||||
#include <limits>
|
||||
#include <cmath>
|
||||
|
||||
#include <nobug.h>
|
||||
|
||||
|
||||
|
||||
namespace util {
|
||||
|
|
@ -136,7 +139,7 @@ namespace util {
|
|||
}
|
||||
|
||||
|
||||
/** quantise towards ceiling.
|
||||
/** quantise towards ceiling.
|
||||
* This function models to fit something into a tiled structure.
|
||||
*/
|
||||
template<typename U> requires std::unsigned_integral<U>
|
||||
|
|
@ -154,6 +157,103 @@ namespace util {
|
|||
|
||||
|
||||
|
||||
/**
|
||||
* Transform into a cyclically wrapping domain.
|
||||
* @tparam N any integral type, signed or unsigned
|
||||
* @return mapping into the range [minVal...maxVal(
|
||||
* @remark This function exploits the equivalence between signed and corresponding unsigned type
|
||||
* to perform the modulus computation with positive numbers, because the modulus operator of C++
|
||||
* works flipped for negative numbers, and oriented towards zero, while we want here always
|
||||
* the same cyclic wrap-around, irrespective of the position relative to zero.
|
||||
* - mathematically we want: `minVal + (rawVal-minVal) modulus PERIOD`
|
||||
* - the unsigned representation of a negative number wraps cleanly, and thus maxVal - minVal,
|
||||
* when converted into unsigned, is off by multiples of the whole number domain length, which
|
||||
* is again absorbed by wrapping. Thus surprisingly `PERIOD = U(U(maxVal) - U(minVal))` is exact
|
||||
* and can always be represented in the unsigned type.
|
||||
* - for rawVal < minVal a flipped definition is used that can also be represented as unsigned:
|
||||
* ** define dist ≔ minVal - rawval, then dist ≥ 1 for rawVal < minVal
|
||||
* ** mathematically we need offset ≡ (-dist) modulus PERIOD
|
||||
* ** we could compute PERIOD - (dist % PERIOD) -- but that would flip for dist ≡ PERIOD
|
||||
* ** thus both arguments are shifted by -1, which keeps them in-range and computable as unsigned
|
||||
* @note the repeated casts `U(signed)` prevent C++ integer promotion to kick in (esp. by short and char)
|
||||
* @remark Claude was used (9/26) to improve and then formally verify this solution.
|
||||
*/
|
||||
template<std::integral N>
|
||||
inline constexpr N
|
||||
cyclicWrap (N rawVal, N minVal, N maxVal)
|
||||
{
|
||||
REQUIRE (minVal < maxVal);
|
||||
using U = std::make_unsigned_t<N>;
|
||||
const U PERIOD = U(U(maxVal) - U(minVal)); // always representable
|
||||
U offset;
|
||||
if (rawVal >= minVal)
|
||||
offset = U(U(rawVal) - U(minVal)) % PERIOD;
|
||||
else
|
||||
{
|
||||
const U dist = U(U(minVal) - U(rawVal)); // dist ≥ 1
|
||||
offset = PERIOD-1 - (dist-1) % PERIOD; // ≡ (-dist) modulus PERIOD
|
||||
}
|
||||
return N(U(U(minVal) + offset));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Transform into a cyclically wrapping domain: floating point implementation.
|
||||
* @return mapping into the range [minVal...maxVal(
|
||||
* @warning this implementation can not cover extreme numeric values beyond #limit_cyclicWrap()
|
||||
* @remark within the usable numeric range, the flipped modulus for values below minVal can simply
|
||||
* be fixed by shifting once by PERIOD. But there is a caveat: extremely small negative offsets
|
||||
* could be absorbed, so that the result lands on +PERIOD
|
||||
*/
|
||||
template<std::floating_point F>
|
||||
inline constexpr F
|
||||
cyclicWrap (F rawVal, F minVal, F maxVal)
|
||||
{
|
||||
REQUIRE (minVal < maxVal);
|
||||
const F PERIOD = maxVal - minVal;
|
||||
F offset = std::fmod (rawVal - minVal, PERIOD);
|
||||
if (offset < 0)
|
||||
offset += PERIOD;
|
||||
if (not (offset < PERIOD)) // can happen due to ε or non-finite values
|
||||
offset = 0;
|
||||
F res = minVal + offset; // could be rounded to maxVal
|
||||
return res < maxVal? res : minVal;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Validation helper for the floating-point implementation of #cyclicWrap().
|
||||
* Values beyond the computed limit can not be handled by the implementation,
|
||||
* due to the limited internal resolution of floating-point computations.
|
||||
* @param period length of the cyclic wrap period (≔ maxVal - minVal)
|
||||
* @return a limit for each parameter values, taken absolutely
|
||||
* @remark at the core of the #cyclicWrap() computation sits a numeric difference,
|
||||
* taken modulo the period length. Each floating point number has a minimum resolution ("ULP").
|
||||
* When the ULP of the difference computation surpasses the periodic cycle length, the cyclicWrap
|
||||
* becomes meaningless (albeit computed correct without glitch). And since that offset is then
|
||||
* added on top of meanValue, the same argument holds, so `std::abs (minVal) <= limit` is required,
|
||||
* and by transitive argument also for `maxVal` and `rawVal`. Furthermore, the difference and the
|
||||
* period length must also be finite numbers by themselves.
|
||||
* @note for performance reasons, that limit should be computed once, checked against `minVal` and `maxVal`,
|
||||
* and then before each computation of #cyclicWrap(), `std::abs (rawVal) <= limit` should be checked.
|
||||
* It is recommended to fail with an hard error, since resolving theses settings typically requires
|
||||
* an intervention by the user (parameters need to be adjusted).
|
||||
* @remark Claude was used (9/26) to analyse the computation and derive a boundary criterion.
|
||||
*/
|
||||
template<std::floating_point F>
|
||||
inline constexpr F
|
||||
limit_cyclicWrap (F period)
|
||||
{
|
||||
REQUIRE (std::isfinite (period));
|
||||
F limit = period / std::numeric_limits<F>::epsilon();
|
||||
if (not std::isfinite (limit))
|
||||
limit = std::numeric_limits<F>::max();
|
||||
return limit / 2; // exact
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* epsilon comparison of doubles.
|
||||
* @remarks Floating point calculations are only accurate up to a certain degree,
|
||||
|
|
@ -170,12 +270,13 @@ namespace util {
|
|||
* @see https://en.wikipedia.org/wiki/Unit_in_the_last_place
|
||||
* @todo 3/2024 seems we have solved this problem several times meanwhile /////////////////////////////////TICKET #1360 sort out floating-point rounding and precision
|
||||
*/
|
||||
template<std::floating_point F>
|
||||
inline constexpr bool
|
||||
almostEqual (double d1, double d2, uint ulp =2) noexcept
|
||||
almostEqual (F d1, F d2, uint ulp =2) noexcept
|
||||
{
|
||||
using std::fabs;
|
||||
return fabs (d1-d2) < DBL_EPSILON * fabs (d1+d2) * ulp
|
||||
or fabs (d1-d2) < DBL_MIN; // special treatment for subnormal results
|
||||
return fabs (d1-d2) < std::numeric_limits<F>::epsilon() * fabs (d1+d2) * ulp
|
||||
or fabs (d1-d2) < std::numeric_limits<F>::min(); // special treatment for subnormal results
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -32,6 +32,7 @@
|
|||
|
||||
#include <unistd.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
|
|
|||
|
|
@ -36,7 +36,7 @@
|
|||
#include "lib/format-string.hpp"
|
||||
#include "lib/format-cout.hpp"
|
||||
#include "lib/time/timevalue.hpp"
|
||||
#include "lib/luid.h"
|
||||
#include "lib/hash-indexed.hpp"
|
||||
#include "lib/util.hpp"
|
||||
|
||||
#include <boost/lexical_cast.hpp>
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
Loading…
Reference in a new issue