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