Stroika Library 3.0d16
 
Loading...
Searching...
No Matches
ReBin.h
Go to the documentation of this file.
1/*
2 * Copyright(c) Sophist Solutions, Inc. 1990-2025. All rights reserved
3 */
4#ifndef _Stroika_Foundation_Math_ReBin_h_
5#define _Stroika_Foundation_Math_ReBin_h_ 1
6
7#include "Stroika/Foundation/StroikaPreComp.h"
8
9#include "Stroika/Foundation/Containers/Sequence.h"
10#include "Stroika/Foundation/Containers/Set.h"
13
14/**
15 * \file
16 *
17 * \note Code-Status: <a href="Code-Status.md#Alpha">Alpha</a>
18 *
19 * @todo Clearly DOCUMENT and CHECK assumption in this code that X-value is non-decreasingly a function of bucket#
20 * In debug build, could do a pre-pass to assert this (if its not otherwise easy to test/verify)
21 *
22 * PROBABLY FALSE: INSTEAD TRY THIS STATEMENT AFTER I TEST/REIVEW:
23 * There is no requirement about how the X-values vary with the bucket numbers (though typically
24 * they increase monotonically with bucket number).
25 *
26 * The only requirement is that the buckets form a partition of some domain (so in other words, they
27 * don't overlap, but have no 'holes' in the middle. So they can be parition Range<XType> (some minx/maxx).
28 *
29 * @todo Consider adding an x-offset, so that we logically re-bin, plus shift along the x-axis by
30 * a (given) offset (in units of src-bin widths).
31 */
32
33namespace Stroika::Foundation::Math::ReBin {
34
35 /**
36 */
37 template <typename X_TYPE, typename VALUE_TYPE>
38 class DataDescriptorBase {
39 public:
40 using BucketIndexType = size_t;
41 using XType = X_TYPE;
42 using ValueType = VALUE_TYPE;
43
44 /**
45 * kNullValue is a special value, such that Accumulate() will have no effect if this value is applied. Typically
46 * this will be zero (if we accumulate by adding - the default).
47 */
48 public:
49 static constexpr ValueType kNullValue{0};
50
51 public:
52 static bool RangeElementsNearlyEqual (XType lhs, XType rhs);
53
54#if 0
55 // CONCEPT: WHEN WE HAVE - saying we support these methods!
56 public:
57 nonvirtual BucketIndexType GetBucketCount () const;
58
59 public:
60 nonvirtual Traversal::Range<XType> GetBucketRange (BucketIndexType bucket) const;
61
62 public:
63 /**
64 * For the given argument x-range, find the range of intersecting buckets
65 * This need not be contiguous (for now).
66 */
67 nonvirtual Containers::Set<BucketIndexType> GetIntersectingBuckets (const Traversal::Range<XType>& xrange) const;
68
69 public:
70 /*
71 * \pre 0 <= bucket and bucket < GetBucketCount ()
72 */
73 nonvirtual ValueType GetValue (BucketIndexType bucket) const;
74#endif
75 };
76
77 /**
78 * Utility to describe source data (bins) for use in the ReBin() API.
79 *
80 * This is not needed explicitly for simple usage, but is just for complicated cases.
81 */
82 template <typename X_TYPE, typename VALUE_TYPE>
83 class BasicDataDescriptor : public DataDescriptorBase<X_TYPE, VALUE_TYPE> {
84 private:
85 using inherited = DataDescriptorBase<X_TYPE, VALUE_TYPE>;
86
87 public:
88 using BucketIndexType = typename inherited::BucketIndexType;
89 using XType = typename inherited::XType;
90 using ValueType = typename inherited::ValueType;
91
92 public:
93 BasicDataDescriptor (const ValueType* bucketStart, const ValueType* bucketEnd, XType xStart, XType xEnd);
94
95 public:
96 nonvirtual BucketIndexType GetBucketCount () const;
97
98 public:
99 nonvirtual Traversal::Range<XType> GetBucketRange (BucketIndexType bucket) const;
100
101 public:
102 /**
103 * Tor the given argument x-range, find the range of intersecting buckets.
104 * The ReBin code does NOT assume this is contiguous, but the BasicDataDescriptor<> does.
105 */
107
108 public:
109 /*
110 * \pre 0 <= bucket and bucket < GetBucketCount ()
111 */
112 nonvirtual ValueType GetValue (BucketIndexType bucket) const;
113
114 public:
115 /*
116 */
117 nonvirtual Containers::Sequence<ValueType> GetValues () const;
118
119 protected:
120 const ValueType* _fBucketDataStart;
121 const ValueType* _fBucketDataEnd;
122
123 private:
124 XType fXStart_;
125 XType fXEnd_;
126 };
127
128 /**
129 * Utility to describe target data (bins) for use in the ReBin() API.
130 * This is not needed explicitly for simple usage, but is just for complicated cases.
131 */
132 template <typename X_TYPE, typename VALUE_TYPE>
133 class UpdatableDataDescriptor : public BasicDataDescriptor<X_TYPE, VALUE_TYPE> {
134 private:
136
137 public:
138 UpdatableDataDescriptor (VALUE_TYPE* bucketStart, VALUE_TYPE* bucketEnd, X_TYPE xStart, X_TYPE xEnd);
139
140 public:
141 nonvirtual void AccumulateValue (typename inherited::BucketIndexType bucket, VALUE_TYPE delta);
142
143 public:
144 nonvirtual void clear ();
145 };
146
147 /**
148 * Take one array of counts (buckets/samples) - and stretch them (rebin them) to another number
149 * of buckets.
150 *
151 * Logically, think of a 2D graph, x versus y. The source buckets represent an approximation
152 * of a real curve (y=f(x)). The buckets represent equally spaced measurements along the x-axis
153 * and the corresponding y-axis value.
154 *
155 * Re-binning means selecting a different (could be larger or smaller) bin count, and inferring the
156 * curve from the source bins, and producing target bins that match that curve as best as possible.
157 *
158 * The new bins could also - have been offset slightly versus the new bins (that is the zeroth
159 * bin of the new set of bins need not start at the same x-value as the original set of bins).
160 *
161 * Classically - this assumes the curve was fairly linear across the new set original set of bins.
162 * As a future exercise, we may want to experiment with different assumptions (like linear
163 * up/down according to prev and successive bins?).
164 *
165 * \note The bucket index really doesn't mean much of anything (if you use the SRC_DATA_DESCRIPTOR
166 * variant of the API), except that its used to address (name) buckets.
167 * Buckets underlying X-Range need not vary monotonically with bucket index
168 * (though beware that BasicDataDescriptor assumes this).
169 *
170 * The only requirement ReBin() makes is that each bucket represents a Range, and that
171 * these ranges are contiguous and non-overlapping (a partition).
172 *
173 * \note When calling the simple ReBin() overload (with no source description) - the target data is all
174 * automatically zeroed.
175 *
176 * But for the more ReBin() template variant with the SOURCE_DESCRIPTION, the caller must clear if desired.
177 * The reason to NOT clear (null) the target in this variant is that the caller may want to accumulate sereral
178 * ReBin sources into one.
179 *
180 * \par Example Usage
181 * \code
182 * uint32_t srcBinData[] = { 3, 5, 19, 2, 0, 0, 0 };
183 * double resultData[4];
184 * ReBin (begin (srcBinData), end (srcBinData), begin (resultData), end (resultData));
185 * EXPECT_TRUE (NearlyEquals ((3 + (5 * ((7.0 / 4.0) - 1))), resultData[0]));
186 * EXPECT_TRUE (0 == resultData[3]);
187 * \endcode
188 *
189 * \par Example Usage
190 * \code
191 * uint32_t srcBinData[] = { 3, 5, 19, 2 };
192 * double resultData[8];
193 * ReBin (begin (srcBinData), end (srcBinData), begin (resultData), end (resultData));
194 * EXPECT_TRUE (NearlyEquals (1.5, resultData[0]));
195 * EXPECT_TRUE (NearlyEquals (1.5, resultData[1]));
196 * EXPECT_TRUE (NearlyEquals (2.5, resultData[2]));
197 * EXPECT_TRUE (NearlyEquals (2.5, resultData[3]));
198 * \endcode
199 *
200 * \par Example Usage
201 * \code
202 * // Shifting by partial bin (1/10 of 4, or by 2/5)
203 * uint32_t srcBinData[] = { 3, 5, 19, 2 };
204 * double resultData[4];
205 * using SRC_DATA_DESCRIPTOR = BasicDataDescriptor<double, uint32_t>;
206 * using TRG_DATA_DESCRIPTOR = UpdatableDataDescriptor<double, double>;
207 * SRC_DATA_DESCRIPTOR srcData (srcStart, srcEnd, 0, 10);
208 * TRG_DATA_DESCRIPTOR trgData (trgStart, trgEnd, 1, 11);
209 * ReBin (srcData, &trgData);
210 *
211 * // In src, srcBinData[0] extends from 0 .. 10/4 (ie 0..2.5), and srcBinData[1] extends from 2.5 to 5.0
212 * // In target, resultData[0] extends from 1 to (11-1)/4 + 1, or 1 to 3.5
213 * // SO... resultData[0] = 3 * (pct of src bucket 0) + 5 * (pct of src bucket 1)
214 * // = 3 * (1.5/2.5) + 5 * (1/2.5) = 1.8 + 2
215 * EXPECT_TRUE (NearlyEquals (3.8, resultData[0]));
216 * \endcode
217 *
218 * @todo SHOW MORE COMPLICATED EXAMPLE BASED ON:...
219 * struct SRC_DATA_DESCRIPTOR : BasicDataDescriptor<double, double> {
220 * ...
221 * static Range<XType> GetBucketRange (unsigned int bucket) {
222 * return fMapper_->ToFrequency (fMapper_->BinToAtoDValue (bucket));
223 * }
224 * }
225 * };
226 * using TRG_DATA_DESCRIPTOR = UpdatableDataDescriptor<double, double>;
227 *
228 */
229 template <typename SRC_DATA_DESCRIPTOR, typename TRG_DATA_DESCRIPTOR>
230 void ReBin (const SRC_DATA_DESCRIPTOR& srcData, TRG_DATA_DESCRIPTOR* trgData);
231 template <typename SRC_BUCKET_TYPE, typename TRG_BUCKET_TYPE, typename X_OFFSET_TYPE = double>
232 void ReBin (const SRC_BUCKET_TYPE* srcStart, const SRC_BUCKET_TYPE* srcEnd, TRG_BUCKET_TYPE* trgStart, TRG_BUCKET_TYPE* trgEnd);
233
234}
235
236/*
237 ********************************************************************************
238 ***************************** Implementation Details ***************************
239 ********************************************************************************
240 */
241#include "ReBin.inl"
242
243#endif /*_Stroika_Foundation_Math_ReBin_h_*/
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.
Definition Sequence.h:187
Set<T> is a container of T, where once an item is added, additionally adds () do nothing.
Definition Set.h:105
nonvirtual Containers::Set< BucketIndexType > GetIntersectingBuckets(const Traversal::Range< XType > &xrange) const
Definition ReBin.inl:56