Stroika Library 3.0d23x
 
Loading...
Searching...
No Matches
ReBin.inl
1/*
2 * Copyright(c) Sophist Solutions, Inc. 1990-2026. All rights reserved
3 */
5#include "Stroika/Foundation/Math/Common.h"
9
10namespace Stroika::Foundation::Math::ReBin {
11
12 /*
13 ********************************************************************************
14 ********************** Math::ReBin::DataDescriptorBase *************************
15 ********************************************************************************
16 */
17 template <typename X_TYPE, typename VALUE_TYPE>
18 inline bool DataDescriptorBase<X_TYPE, VALUE_TYPE>::RangeElementsNearlyEqual (XType lhs, XType rhs)
19 {
20 return NearlyEquals (lhs, rhs);
21 }
22
23 /*
24 ********************************************************************************
25 ********************** Math::ReBin::BasicDataDescriptor ************************
26 ********************************************************************************
27 */
28 template <typename X_TYPE, typename VALUE_TYPE>
29 inline BasicDataDescriptor<X_TYPE, VALUE_TYPE>::BasicDataDescriptor (const ValueType* bucketStart, const ValueType* bucketEnd, XType xStart, XType xEnd)
30 : _fBucketDataStart{bucketStart}
31 , _fBucketDataEnd{bucketEnd}
32 , fXStart_{xStart}
33 , fXEnd_{xEnd}
34 {
35 RequireNotNull (bucketStart);
36 RequireNotNull (bucketEnd);
37 Require (bucketStart < bucketEnd);
38 Require (xStart < xEnd);
39 }
40 template <typename X_TYPE, typename VALUE_TYPE>
41 inline typename BasicDataDescriptor<X_TYPE, VALUE_TYPE>::BucketIndexType BasicDataDescriptor<X_TYPE, VALUE_TYPE>::GetBucketCount () const
42 {
43 BucketIndexType result = _fBucketDataEnd - _fBucketDataStart;
44 Ensure (result > 0);
45 return result;
46 }
47 template <typename X_TYPE, typename VALUE_TYPE>
48 inline auto BasicDataDescriptor<X_TYPE, VALUE_TYPE>::GetBucketRange (BucketIndexType bucket) const -> Traversal::Range<XType>
49 {
50 using Traversal::Range;
51 Require (bucket < GetBucketCount ());
52 X_TYPE kDelta_ = (fXEnd_ - fXStart_) * (static_cast<X_TYPE> (1) / static_cast<X_TYPE> (GetBucketCount ()));
53 X_TYPE s = fXStart_ + (static_cast<X_TYPE> (bucket) * kDelta_);
54 return Range<X_TYPE>{s, s + kDelta_, Traversal::Openness::eClosed, Traversal::Openness::eOpen};
55 }
56 template <typename X_TYPE, typename VALUE_TYPE>
59 {
61 if (xrange.GetUpperBound () < fXStart_) {
63 }
64 if (xrange.GetLowerBound () > fXEnd_) {
66 }
67
68 X_TYPE kDelta_ = (fXEnd_ - fXStart_) * (static_cast<X_TYPE> (1) / static_cast<X_TYPE> (GetBucketCount ()));
69
70 // very roughly (quick hack)
71 X_TYPE bucketLowerBound = (xrange.GetLowerBound () - fXStart_) / kDelta_;
72 X_TYPE bucketUpperBound = (xrange.GetUpperBound () - fXStart_) / kDelta_;
73
74 bucketLowerBound = (bucketLowerBound < 0) ? 0 : bucketLowerBound;
75 bucketUpperBound = (bucketUpperBound < 0) ? 0 : bucketUpperBound;
76
77 BucketIndexType bucketLB = clamp<BucketIndexType> (static_cast<BucketIndexType> (floor (bucketLowerBound)), 0, GetBucketCount () - 1);
78 BucketIndexType bucketUB = clamp<BucketIndexType> (static_cast<BucketIndexType> (ceil (bucketUpperBound)), bucketLB, GetBucketCount () - 1);
79
80 return Containers::Set<BucketIndexType> (DiscreteRange<BucketIndexType> (bucketLB, bucketUB).Elements ());
81 }
82 template <typename X_TYPE, typename VALUE_TYPE>
83 inline auto BasicDataDescriptor<X_TYPE, VALUE_TYPE>::GetValue (BucketIndexType bucket) const -> ValueType
84 {
85 Require (bucket < GetBucketCount ());
86 return _fBucketDataStart[bucket];
87 }
88 template <typename X_TYPE, typename VALUE_TYPE>
89 inline auto BasicDataDescriptor<X_TYPE, VALUE_TYPE>::GetValues () const -> Containers::Sequence<ValueType>
90 {
91 return Containers::Sequence<ValueType> (_fBucketDataStart, _fBucketDataEnd);
92 }
93
94 /*
95 ********************************************************************************
96 ***************** Math::ReBin::UpdatableDataDescriptor *************************
97 ********************************************************************************
98 */
99 template <typename X_TYPE, typename VALUE_TYPE>
100 inline UpdatableDataDescriptor<X_TYPE, VALUE_TYPE>::UpdatableDataDescriptor (VALUE_TYPE* bucketStart, VALUE_TYPE* bucketEnd, X_TYPE xStart, X_TYPE xEnd)
101 : inherited{bucketStart, bucketEnd, xStart, xEnd}
102 {
103 }
104 template <typename X_TYPE, typename VALUE_TYPE>
105 inline void UpdatableDataDescriptor<X_TYPE, VALUE_TYPE>::AccumulateValue (typename inherited::BucketIndexType bucket, VALUE_TYPE delta)
106 {
107 Require (bucket < inherited::GetBucketCount ());
108 VALUE_TYPE* updatableStart = const_cast<VALUE_TYPE*> (inherited::_fBucketDataStart);
109 updatableStart[bucket] += delta;
110 }
111 template <typename X_TYPE, typename VALUE_TYPE>
112 inline void UpdatableDataDescriptor<X_TYPE, VALUE_TYPE>::clear ()
113 {
114 VALUE_TYPE* updatableStart = const_cast<VALUE_TYPE*> (inherited::_fBucketDataStart);
115 for (VALUE_TYPE* i = updatableStart; i != inherited::_fBucketDataEnd; ++i) {
116 *i = inherited::kNullValue;
117 }
118 }
119
120 /*
121 ********************************************************************************
122 ************************************* PRIVATE_ *********************************
123 ********************************************************************************
124 */
125 namespace PRIVATE_ {
126 template <typename DATA_DESCRIPTOR_TYPE>
127 void CheckRebinDataDescriptorInvariant_ (const DATA_DESCRIPTOR_TYPE& d)
128 {
130 using namespace Traversal;
131 using BucketIndexType = typename DATA_DESCRIPTOR_TYPE::BucketIndexType;
132 using XType = typename DATA_DESCRIPTOR_TYPE::XType;
133 auto myContext = Memory::MakeSharedPtr<BucketIndexType> (0);
134 auto bucketCount = d.GetBucketCount ();
135 auto getNext = [myContext, bucketCount, d] () -> optional<Range<XType>> {
136 /*
137 * Intentionally skip empty range elements, as legal in ReBin () - but which make
138 * the set not technically a partition.
139 */
140 optional<Range<XType>> result;
141 while (not result.has_value () and *myContext < bucketCount) {
142 Range<XType> tmp{d.GetBucketRange (*myContext)};
143 if (not tmp.empty ()) {
144 result = tmp;
145 }
146 ++(*myContext);
147 }
148 return result;
149 };
150 Assert (IsPartition (CreateGenerator<Range<XType>> (getNext), DATA_DESCRIPTOR_TYPE::RangeElementsNearlyEqual));
151 }
152 }
153 }
154
155 /*
156 ********************************************************************************
157 ********************************** Math::ReBin *********************************
158 ********************************************************************************
159 */
160 template <typename SRC_DATA_DESCRIPTOR, typename TRG_DATA_DESCRIPTOR>
161 void ReBin (const SRC_DATA_DESCRIPTOR& srcData, TRG_DATA_DESCRIPTOR* trgData)
162 {
163 RequireNotNull (trgData);
164
165 using namespace Traversal;
166 using BucketIndexType = typename SRC_DATA_DESCRIPTOR::BucketIndexType;
167
168 /*
169 * OLD OBSOLETE Algorithm:
170 * Two iterators - one marking (start/end of target buckets), and one marking current
171 * src bucket. Iterate over outer buckets. Move contents to new bucket. And adjust new
172 * iterators. When they overlap and must advance - proportionally add to bucket,
173 * advance and add rest to next target bucket.
174 *
175 * This is probably more efficent than the algorithm below, but trickier to generalize
176 * so we can have differently scaled source buckets.
177 */
178
179#if qStroika_Foundation_Debug_AssertionsChecked
180 PRIVATE_::CheckRebinDataDescriptorInvariant_ (srcData);
181 PRIVATE_::CheckRebinDataDescriptorInvariant_ (*trgData);
182#endif
183
184 /*
185 * x 0 1 2 3 4 5
186 *
187 * SRC-BUCKETS: | | | | | |
188 * TRG-BUCKETS: | | | |
189 *
190 * Iterate over each source bucket. It represnts data from its sourceXStart, to srcXEnd (bucket X domain)
191 * all data spread evently.
192 *
193 * See where that start x/endx map to in y buckets, and spread it over them. Assume that in the TARGET
194 * the buckets are contiguous.
195 *
196 * For each one source bucket, put part into current ti (proportional), and put reset into next
197 * ti (proportional).
198 */
199 BucketIndexType srcBucketCount = srcData.GetBucketCount ();
200 for (BucketIndexType srcBucketIdx = 0; srcBucketIdx < srcBucketCount; ++srcBucketIdx) {
201 Range<typename SRC_DATA_DESCRIPTOR::XType> curSrcBucketX = srcData.GetBucketRange (srcBucketIdx);
202 auto curSrcBucketXWidth = curSrcBucketX.GetDistanceSpanned ();
203 typename SRC_DATA_DESCRIPTOR::ValueType thisSrcBucketValue = srcData.GetValue (srcBucketIdx);
204
205 Assert (curSrcBucketXWidth >= 0); // can ever be zero?
206
207 // Check special value of zero so we don't waste time spreading zeros around
208 if (thisSrcBucketValue != SRC_DATA_DESCRIPTOR::kNullValue and curSrcBucketXWidth != 0) {
209 /*
210 * find range of target buckets to distribute value.
211 *
212 * Each bucket returned may have little overall contribution from the source bucket. We look at
213 * the degree of overlap.
214 */
215 for (const auto& targetBucket : trgData->GetIntersectingBuckets (curSrcBucketX)) {
216 Range<typename SRC_DATA_DESCRIPTOR::XType> trgBucketIntersectRange =
217 trgData->GetBucketRange (targetBucket).Intersection (curSrcBucketX);
218 auto trgBucketXWidth = trgBucketIntersectRange.GetDistanceSpanned ();
219 trgData->AccumulateValue (targetBucket, thisSrcBucketValue * (trgBucketXWidth / curSrcBucketXWidth));
220 }
221 }
222 }
223 }
224 template <typename SRC_BUCKET_TYPE, typename TRG_BUCKET_TYPE, typename X_OFFSET_TYPE>
225 void ReBin (const SRC_BUCKET_TYPE* srcStart, const SRC_BUCKET_TYPE* srcEnd, TRG_BUCKET_TYPE* trgStart, TRG_BUCKET_TYPE* trgEnd)
226 {
227 using SRC_DATA_DESCRIPTOR = BasicDataDescriptor<X_OFFSET_TYPE, SRC_BUCKET_TYPE>;
228 using TRG_DATA_DESCRIPTOR = UpdatableDataDescriptor<X_OFFSET_TYPE, TRG_BUCKET_TYPE>;
229
230 SRC_DATA_DESCRIPTOR srcData{srcStart, srcEnd, 1, 2};
231 TRG_DATA_DESCRIPTOR trgData{trgStart, trgEnd, 1, 2};
232 trgData.clear (); // zero all the target buckets since this is a simple-usage variant and thats typically what will be wanted
233 ReBin (srcData, &trgData);
234 }
235
236}
#define qStroika_Foundation_Debug_AssertionsChecked
The qStroika_Foundation_Debug_AssertionsChecked flag determines if assertions are checked and validat...
Definition Assertions.h:48
#define RequireNotNull(p)
Definition Assertions.h:347
void ReBin(const SRC_DATA_DESCRIPTOR &srcData, TRG_DATA_DESCRIPTOR *trgData)
Definition ReBin.inl:161
A generalization of a vector: a container whose elements are keyed by the natural numbers.
Set<T> is a container of T, where once an item is added, additionally adds () do nothing.
A DiscreteRange is a Range where the underlying endpoints are integral (discrete, not continuous); th...