1 // Copyright 2015-2018 Hans Dembinski
2 //
3 // Distributed under the Boost Software License, Version 1.0.
4 // (See accompanying file LICENSE_1_0.txt
5 // or copy at http://www.boost.org/LICENSE_1_0.txt)
6 
7 #include <boost/core/lightweight_test.hpp>
8 #include <boost/histogram/accumulators/mean.hpp>
9 #include <boost/histogram/accumulators/ostream.hpp>
10 #include <boost/histogram/accumulators/sum.hpp>
11 #include <boost/histogram/accumulators/thread_safe.hpp>
12 #include <boost/histogram/accumulators/weighted_mean.hpp>
13 #include <boost/histogram/accumulators/weighted_sum.hpp>
14 #include <boost/histogram/weight.hpp>
15 #include <sstream>
16 #include "is_close.hpp"
17 #include "throw_exception.hpp"
18 
19 using namespace boost::histogram;
20 using namespace std::literals;
21 
22 template <class T>
str(const T & t,int w=0,bool left=true)23 auto str(const T& t, int w = 0, bool left = true) {
24   std::ostringstream os;
25   os.width(w);
26   if (left)
27     os << std::left;
28   else
29     os << std::right;
30   os << t;
31   return os.str();
32 }
33 
main()34 int main() {
35   {
36     using w_t = accumulators::weighted_sum<double>;
37     w_t w;
38     BOOST_TEST_EQ(str(w), "weighted_sum(0, 0)"s);
39     BOOST_TEST_EQ(str(w, 20, false), "  weighted_sum(0, 0)"s);
40     BOOST_TEST_EQ(str(w, 20, true), "weighted_sum(0, 0)  "s);
41     BOOST_TEST_EQ(w, w_t{});
42 
43     BOOST_TEST_EQ(w, w_t(0));
44     BOOST_TEST_NE(w, w_t(1));
45     w = w_t(1);
46     BOOST_TEST_EQ(w.value(), 1);
47     BOOST_TEST_EQ(w.variance(), 1);
48     BOOST_TEST_EQ(w, 1);
49     BOOST_TEST_NE(w, 2);
50 
51     w += 2;
52     BOOST_TEST_EQ(w.value(), 3);
53     BOOST_TEST_EQ(w.variance(), 5);
54     BOOST_TEST_EQ(w, w_t(3, 5));
55     BOOST_TEST_NE(w, w_t(3));
56 
57     w += w_t(1, 2);
58     BOOST_TEST_EQ(w.value(), 4);
59     BOOST_TEST_EQ(w.variance(), 7);
60 
61     // consistency: a weighted counter increased by weight 1 multiplied
62     // by 2 must be the same as a weighted counter increased by weight 2
63     w_t u(0);
64     ++u;
65     u *= 2;
66     BOOST_TEST_EQ(u, w_t(2, 4));
67 
68     w_t v(0);
69     v += 2;
70     BOOST_TEST_EQ(u, v);
71 
72     // conversion to RealType
73     w_t y(1, 2);
74     BOOST_TEST_NE(y, 1);
75     BOOST_TEST_EQ(static_cast<double>(y), 1);
76 
77     BOOST_TEST_EQ(w_t() += w_t(), w_t());
78   }
79 
80   {
81     using m_t = accumulators::mean<double>;
82     m_t a;
83     BOOST_TEST_EQ(a.count(), 0);
84     BOOST_TEST_EQ(a, m_t{});
85 
86     a(4);
87     a(7);
88     a(13);
89     a(16);
90 
91     BOOST_TEST_EQ(a.count(), 4);
92     BOOST_TEST_EQ(a.value(), 10);
93     BOOST_TEST_EQ(a.variance(), 30);
94 
95     BOOST_TEST_EQ(str(a), "mean(4, 10, 30)"s);
96     BOOST_TEST_EQ(str(a, 20, false), "     mean(4, 10, 30)"s);
97     BOOST_TEST_EQ(str(a, 20, true), "mean(4, 10, 30)     "s);
98 
99     m_t b;
100     b(1e8 + 4);
101     b(1e8 + 7);
102     b(1e8 + 13);
103     b(1e8 + 16);
104 
105     BOOST_TEST_EQ(b.count(), 4);
106     BOOST_TEST_EQ(b.value(), 1e8 + 10);
107     BOOST_TEST_EQ(b.variance(), 30);
108 
109     auto c = a;
110     c += a; // same as feeding all samples twice
111 
112     BOOST_TEST_EQ(c.count(), 8);
113     BOOST_TEST_EQ(c.value(), 10);
114     BOOST_TEST_IS_CLOSE(c.variance(), 25.714, 1e-3);
115 
116     // also same as feeding all samples twice
117     m_t d;
118     d(weight(2), 4);
119     d(weight(2), 7);
120     d(weight(2), 13);
121     d(weight(2), 16);
122 
123     BOOST_TEST_EQ(d, c);
124 
125     BOOST_TEST_EQ(m_t() += m_t(), m_t());
126     BOOST_TEST_EQ(m_t(1, 2, 3) += m_t(), m_t(1, 2, 3));
127     BOOST_TEST_EQ(m_t() += m_t(1, 2, 3), m_t(1, 2, 3));
128   }
129 
130   {
131     using m_t = accumulators::weighted_mean<double>;
132     m_t a;
133     BOOST_TEST_EQ(a.sum_of_weights(), 0);
134     BOOST_TEST_EQ(a, m_t{});
135 
136     a(weight(0.5), 1);
137     a(weight(1.0), 2);
138     a(weight(0.5), 3);
139 
140     BOOST_TEST_EQ(a.sum_of_weights(), 2);
141     BOOST_TEST_EQ(a.sum_of_weights_squared(), 1.5);
142     BOOST_TEST_EQ(a.value(), 2);
143     BOOST_TEST_IS_CLOSE(a.variance(), 0.8, 1e-3);
144 
145     BOOST_TEST_EQ(str(a), "weighted_mean(2, 2, 0.8)"s);
146     BOOST_TEST_EQ(str(a, 25, false), " weighted_mean(2, 2, 0.8)"s);
147     BOOST_TEST_EQ(str(a, 25, true), "weighted_mean(2, 2, 0.8) "s);
148 
149     auto b = a;
150     b += a; // same as feeding all samples twice
151 
152     BOOST_TEST_EQ(b.sum_of_weights(), 4);
153     BOOST_TEST_EQ(b.value(), 2);
154     BOOST_TEST_IS_CLOSE(b.variance(), 0.615, 1e-3);
155 
156     BOOST_TEST_EQ(m_t() += m_t(), m_t());
157     BOOST_TEST_EQ(m_t(1, 2, 3, 4) += m_t(), m_t(1, 2, 3, 4));
158     BOOST_TEST_EQ(m_t() += m_t(1, 2, 3, 4), m_t(1, 2, 3, 4));
159   }
160 
161   {
162     double bad_sum = 0;
163     bad_sum += 1;
164     bad_sum += 1e100;
165     bad_sum += 1;
166     bad_sum += -1e100;
167     BOOST_TEST_EQ(bad_sum, 0); // instead of 2
168 
169     using s_t = accumulators::sum<double>;
170     s_t sum;
171     ++sum;
172     BOOST_TEST_EQ(sum.large(), 1);
173     BOOST_TEST_EQ(sum.small(), 0);
174     BOOST_TEST_EQ(str(sum), "sum(1 + 0)"s);
175     BOOST_TEST_EQ(str(sum, 15, false), "     sum(1 + 0)"s);
176     BOOST_TEST_EQ(str(sum, 15, true), "sum(1 + 0)     "s);
177 
178     sum += 1e100;
179     BOOST_TEST_EQ(str(sum), "sum(1e+100 + 1)"s);
180     ++sum;
181     BOOST_TEST_EQ(str(sum), "sum(1e+100 + 2)"s);
182     sum += -1e100;
183     BOOST_TEST_EQ(str(sum), "sum(0 + 2)"s);
184     BOOST_TEST_EQ(sum, 2); // correct answer
185     BOOST_TEST_EQ(sum.large(), 0);
186     BOOST_TEST_EQ(sum.small(), 2);
187 
188     accumulators::sum<double> a(3), b(2), c(3);
189     BOOST_TEST_LT(b, c);
190     BOOST_TEST_LE(b, c);
191     BOOST_TEST_LE(a, c);
192     BOOST_TEST_GT(a, b);
193     BOOST_TEST_GE(a, b);
194     BOOST_TEST_GE(a, c);
195 
196     BOOST_TEST_EQ(s_t() += s_t(), s_t());
197   }
198 
199   {
200     using s_t = accumulators::weighted_sum<accumulators::sum<double>>;
201     s_t w;
202 
203     ++w;
204     w += 1e100;
205     ++w;
206     w += -1e100;
207 
208     BOOST_TEST_EQ(w.value(), 2);
209     BOOST_TEST_EQ(w.variance(), 2e200);
210 
211     BOOST_TEST_EQ(s_t() += s_t(), s_t());
212   }
213 
214   {
215     using ts_t = accumulators::thread_safe<int>;
216     ts_t i;
217     ++i;
218     i += 1000;
219 
220     BOOST_TEST_EQ(i, 1001);
221     BOOST_TEST_EQ(str(i), "1001"s);
222 
223     BOOST_TEST_EQ(ts_t() += ts_t(), ts_t());
224   }
225 
226   return boost::report_errors();
227 }
228