@@ -53,9 +53,22 @@ extern "C" {
5353 * define an accumulator for _Decimal64.
5454 */
5555typedef struct {
56- long int count; /* *< Number of samples */
57- double M; /* *< Running mean */
58- double S; /* *< Running square difference */
56+ /* * Number of samples. */
57+ long int count;
58+
59+ /* * Running mean.
60+ *
61+ * Is equal to the mean of all the values added so far.
62+ */
63+ double M;
64+
65+ /* * Running square difference.
66+ *
67+ * If the values x[0], ..., x[count - 1] have been added so
68+ * far, this is equal to sum_i (x[i] - M)^2, where M is the
69+ * sample mean as above.
70+ */
71+ double S;
5972} csnip_meanvar;
6073
6174/* * Define an accumulator type.
@@ -84,7 +97,8 @@ CSNIP_MEANVAR_DEF_TYPE(csnip_meanvarl, long double)
8497#define CSNIP_MEANVAR_DECL_FUNCS (scope, prefix, accum_type, val_type ) \
8598 scope void prefix ## add(accum_type* A, val_type v); \
8699 scope val_type prefix ## mean(const accum_type* A); \
87- scope val_type prefix ## var(const accum_type* A, val_type ddof);
100+ scope val_type prefix ## var(const accum_type* A, val_type ddof); \
101+ scope void prefix ## merge(accum_type* into, const accum_type* other);
88102
89103/* * Add sample to accumulator.
90104 *
@@ -122,6 +136,22 @@ double csnip_meanvar_mean(const csnip_meanvar* A);
122136 */
123137double csnip_meanvar_var (const csnip_meanvar* A, double ddof);
124138
139+ /* * Combine two meanvar instances into one.
140+ *
141+ * This allows combining separately collected data. Note that with
142+ * the present implementation, numerical stability is not
143+ * guaranteed as well as individual add steps do.
144+ *
145+ * @param into
146+ * the merge destination. The summary of the samples from
147+ * *other will be added into this accumulator, so it
148+ * represents all the sammles.
149+ *
150+ * @param other
151+ * the data to merge.
152+ */
153+ void csnip_meanvar_merge (csnip_meanvar* into, const csnip_meanvar* other);
154+
125155CSNIP_MEANVAR_DECL_FUNCS (, csnip_meanvarf_, csnip_meanvarf, float )
126156CSNIP_MEANVAR_DECL_FUNCS(, csnip_meanvarl_, csnip_meanvarl, long double )
127157
@@ -137,7 +167,7 @@ CSNIP_MEANVAR_DECL_FUNCS(, csnip_meanvarl_, csnip_meanvarl, long double)
137167 const val_type last_M = A->M ; \
138168 ++A->count ; \
139169 A->M = last_M + (v - last_M) / A->count ; \
140- A->S = A-> S + (v - last_M) * (v - A->M ); \
170+ A->S += (v - last_M) * (v - A->M ); \
141171 } \
142172 \
143173 scope val_type prefix ## mean(const accum_type* A) \
@@ -150,6 +180,19 @@ CSNIP_MEANVAR_DECL_FUNCS(, csnip_meanvarl_, csnip_meanvarl, long double)
150180 { \
151181 return A->S / (A->count - ddof); \
152182 } \
183+ \
184+ scope void prefix ## merge(accum_type* into, const accum_type* other) \
185+ { \
186+ const val_type last_M = into->M ; \
187+ const long int new_count = into->count + other->count ; \
188+ into->M = last_M + other->count * (other->M - last_M) / new_count; \
189+ val_type into_S = into->S ; \
190+ into_S += into->count * (last_M - into->M ) * (last_M - into->M ); \
191+ val_type other_S = other->S ; \
192+ other_S += other->count * (other->M - into->M ) * (other->M - into->M ); \
193+ into->S = into_S + other_S; \
194+ into->count = new_count; \
195+ }
153196
154197#ifdef __cplusplus
155198}
@@ -223,6 +266,30 @@ CSNIP_MEANVAR_DECL_FUNCS(, csnip_meanvarl_, csnip_meanvarl, long double)
223266 csnip_meanvarl: \
224267 csnip_meanvarl_var((csnip_meanvarl*)(accumptr), (ddof)))
225268
269+ /* * Merge another accumulator into the current one.
270+ *
271+ * @param into
272+ * Pointer of destination accumulator.
273+ *
274+ * @param other
275+ * Pointer of source accumulator.
276+ *
277+ * *Requirements*: C11's _Generic or C++ templates. The
278+ * corresponding typed functions csnip_meanvar_merge{f,,l} are not
279+ * dependent on C11.
280+ */
281+ #define csnip_meanvar_Merge (into, other ) \
282+ _Generic (*(into), \
283+ csnip_meanvarf: \
284+ csnip_meanvarf_merge((csnip_meanvarf*)into, \
285+ (const csnip_meanvarf*)other), \
286+ csnip_meanvar: \
287+ csnip_meanvar_merge((csnip_meanvar*)into, \
288+ (const csnip_meanvar*)other), \
289+ csnip_meanvarl: \
290+ csnip_meanvarl_merge((csnip_meanvarl*)into, \
291+ (const csnip_meanvarl*)other))
292+
226293#else /* __cplusplus */
227294
228295/* C++ version of the generic csnip_meanvar_* API */
@@ -304,10 +371,28 @@ CSNIP__DEF_VAR(dummy,f)
304371CSNIP__DEF_VAR(dummy,)
305372CSNIP__DEF_VAR(dummy,l)
306373#undef CSNIP__DEF_VAR
307- #undef CSNIP__SCALAR
308374
309375#define csnip_meanvar_Var (accum, ddof ) csnip_meanvar__cxx_var(accum, ddof)
310376
377+ template <typename T> \
378+ void csnip_meanvar__cxx_merge (T*, const T*);
379+
380+ #define CSNIP__DEF_MERGE (dummy,suffix ) \
381+ template <> void \
382+ csnip_meanvar__cxx_merge (csnip_meanvar##suffix* into, \
383+ const csnip_meanvar##suffix* other) \
384+ { \
385+ csnip_meanvar##suffix##_merge (into, other); \
386+ }
387+ CSNIP__DEF_MERGE (dummy,f)
388+ CSNIP__DEF_MERGE(dummy,)
389+ CSNIP__DEF_MERGE(dummy,l)
390+ #undef CSNIP__DEF_MERGE
391+
392+ #define csnip_meanvar_Merge (into, other ) csnip_meanvar__cxx_merge(into, other)
393+
394+ #undef CSNIP__SCALAR
395+
311396#endif /* __cplusplus */
312397
313398/* * @} */
@@ -328,8 +413,12 @@ CSNIP__DEF_VAR(dummy,l)
328413#define meanvarf_var csnip_meanvarf_var
329414#define meanvar_var csnip_meanvar_var
330415#define meanvarl_var csnip_meanvarl_var
416+ #define meanvarf_merge csnip_meanvarf_merge
417+ #define meanvar_merge csnip_meanvar_merge
418+ #define meanvarl_merge csnip_meanvarl_merge
331419#define meanvar_Add csnip_meanvar_Add
332420#define meanvar_Mean csnip_meanvar_Mean
333421#define meanvar_Var csnip_meanvar_Var
422+ #define meanvar_Merge csnip_meanvar_Merge
334423#define CSNIP_MEANVAR_HAVE_SHORT_NAMES
335424#endif /* CSNIP_SHORT_NAMES && !CSNIP_MEANVAR_HAVE_SHORT_NAMES */
0 commit comments