1 module des.math.method.stat.moment;
2 
3 import std.algorithm;
4 import std.traits;
5 import std.range;
6 import std..string : format;
7 
8 import des.ts;
9 
10 /// expected value
11 auto mean(R)( R r ) pure nothrow @property @nogc
12 if( isInputRange!R )
13 in { assert( !r.empty ); } body
14 {
15     alias T = Unqual!(ElementType!R);
16 
17     static assert( canSumWithSelfAndMulWithFloat!T,
18             "range elements must can sum with selfs and mul with float" );
19 
20     size_t cnt = 0;
21     T res = r.front * 0.0f; // neitral value for summate ( a + b*0 == a )
22     foreach( v; r )
23     {
24         res = res + v;
25         cnt++;
26     }
27     assert( cnt > 0, "no elements in range" );
28     return res * ( 1.0f / cnt );
29 }
30 
31 template canSumWithSelfAndMulWithFloat(T)
32 {
33     enum canSumWithSelfAndMulWithFloat = is( typeof( T.init + T.init ) == T ) &&
34                                          is( typeof( T.init * 0.0f ) == T );
35 }
36 
37 ///
38 unittest
39 {
40     auto a = [ 1.0f, 2, 3 ];
41     assertEq( a.mean, 2.0f );
42 
43     static assert( !__traits(compiles,mean(a[0])) );
44     static assert( !__traits(compiles,[1,2,3].mean) );
45     static assert(  __traits(compiles,[1.0f,2,3].mean) );
46 
47     import std.conv : to;
48 
49     assertEq( iota(11).map!(a=>to!float(a)).mean, 5 );
50 }
51 
52 ///
53 unittest
54 {
55     import des.math.linear.vector;
56 
57     auto a = [ vec3(1,2,3), vec3(2,3,4), vec3(3,4,5) ];
58     assertEq( a.mean, vec3(2,3,4) );
59 }
60 
61 ///
62 auto variance(bool return_mean=false,R)( R r ) pure nothrow @property @nogc
63 if( isInputRange!R )
64 {
65     alias T = Unqual!(ElementType!R);
66 
67     static assert( canSumWithSelfAndMulWithFloat!T,
68             "range elements must can sum with selfs and mul with float" );
69 
70     T res = r.front * 0.0f; // neitral value for summate ( a + b*0 == a )
71     size_t cnt = 0;
72     auto m = r.mean;
73     T buf;
74     foreach( val; r )
75     {
76         static if( is( typeof( T.init - T.init ) == T ) )
77             buf = m - val;
78         else
79             buf = m + val * (-1.0f);
80 
81         res = res + buf * buf;
82         cnt++;
83     }
84 
85     assert( cnt > 1, "only one elements in range, must be greater 1" );
86 
87     static if( return_mean )
88         return cast(T[2])[ m, res * ( 1.0f / (cnt-1) ) ];
89     else
90         return res * ( 1.0f / (cnt-1) );
91 }
92 
93 ///
94 unittest
95 {
96     auto a = [ 1.0f, 1, 1 ];
97     assertEq( a.variance, 0.0f );
98 
99     auto b = [ 1.0f, 2, 3 ];
100     assertEq( b.variance, 1.0f );
101 }
102 
103 ///
104 unittest
105 {
106     import des.math.linear.vector;
107 
108     auto a = [ vec3(1,2,3), vec3(2,3,4), vec3(3,4,5) ];
109     assertEq( a.variance, vec3(1,1,1) );
110 }
111 
112 /++ returns:
113     mean (0 element), variance (1 element)
114 +/
115 auto mean_variance(R)( R r ) pure nothrow @property @nogc
116 if( isInputRange!R ) { return variance!true(r); }
117 
118 ///
119 unittest
120 {
121     auto a = [ 1.0f, 1, 1 ];
122     assertEq( a.mean_variance, [ 1.0f, 0.0f ] );
123 
124     auto b = [ 1.0f, 2, 3 ];
125     assertEq( b.mean_variance, [ 2.0f, 1.0f ] );
126 }
127 
128 ///
129 unittest
130 {
131     import des.math.linear.vector;
132 
133     auto a = [ vec3(1,2,3), vec3(2,3,4), vec3(3,4,5) ];
134     assertEq( a.mean_variance, [ vec3(2,3,4), vec3(1,1,1) ] );
135 }
136 
137 ///
138 auto rawMoment(R)( R r, size_t k=1 ) pure nothrow @property @nogc
139 if( isInputRange!R )
140 in { assert( !r.empty ); } body
141 {
142     alias T = Unqual!(ElementType!R);
143 
144     static assert( canSumWithSelfAndMulWithFloat!T,
145             "range elements must can sum with selfs and mul with float" );
146 
147     T res = r.front * 0.0f;
148     size_t cnt = 0;
149     foreach( v; r )
150     {
151         res = res + spow( v, k );
152         cnt++;
153     }
154     return res * ( 1.0f / cnt );
155 }
156 
157 ///
158 unittest
159 {
160     auto a = [ 1.0f, 2 ];
161     assertEq( a.rawMoment, 1.5 );
162     assertEq( a.rawMoment(2), 2.5 );
163 }
164 
165 /// power ( works with vectors )
166 T spow(T)( in T val, size_t k )
167 if( is( typeof( T.init / T.init ) == T ) && is( typeof( T.init * T.init ) == T ) )
168 {
169     if( k == 0 ) return val / val;
170     if( k == 1 ) return val;
171     if( k == 2 ) return val * val;
172 
173     /+ small types (numbers,vectors)
174      + recursion is faster
175      + on BigInt logarifmic is faster
176      +/
177 
178     // recursion
179     T ret = spow( val*val, k/2 );
180     if( k % 2 ) ret = ret * val;
181     return ret;
182 
183     // logarifmic
184     //auto n = k;
185     //T buf = val / val;
186     //T ret = val;
187     //while( n > 1 )
188     //{
189     //    if( n % 2 ) buf = buf * ret;
190     //    ret = ret * ret;
191     //    n /= 2;
192     //}
193     //return ret * buf;
194 }
195 
196 ///
197 unittest
198 {
199     import des.math.linear.vector;
200     assertEq( spow( vec3( 1, 2, 3 ), 3 ), vec3( 1, 8, 27 ) );
201     foreach( i; 0 .. 16 ) assertEq( spow( 10, i ), 10 ^^ i,
202             format( "spow fails (%%s != %%s) with i: %s",  i ) );
203 }
204 
205 ///
206 auto centralMoment(R)( R r, size_t k=1 ) pure nothrow @property @nogc
207 if( isInputRange!R )
208 {
209     alias T = Unqual!(ElementType!R);
210 
211     static assert( canSumWithSelfAndMulWithFloat!T,
212             "range elements must can sum with selfs and mul with float" );
213 
214     T res = r.front * 0.0f; // neitral value for summate ( a + b*0 == a )
215     size_t cnt = 0;
216     auto m = r.mean;
217     T buf;
218     foreach( val; r )
219     {
220         static if( is( typeof( T.init - T.init ) == T ) )
221             buf = val - m;
222         else
223             buf = val + m * (-1.0f);
224 
225         res = res + spow( buf, k );
226         cnt++;
227     }
228 
229     return res * ( 1.0f / cnt );
230 }
231 
232 ///
233 unittest
234 {
235     auto a = [ 1.0f, 2, 3, 4 ];
236     assertEq( a.centralMoment(1), 0 );
237     assertEq( a.centralMoment(2), 1.25 );
238     assertEq( a.centralMoment(3), 0 );
239 }
240 
241 ///
242 class MovingAverage(T) if( is(typeof(T[].init.mean)) )
243 {
244     ///
245     T[] array;
246 
247     size_t cur = 0;
248     size_t fill = 0;
249 
250     ///
251     this( size_t mlen ) { array.length = mlen; }
252 
253     invariant()
254     {
255         assert( array.length > 0 );
256         assert( array.length >= fill );
257     }
258 
259     ///
260     void put( in T val )
261     {
262         if( array.length > fill )
263         {
264             array[fill] = val;
265             cur++;
266             fill++;
267         }
268         else array[cur++%$] = val;
269     }
270 
271     ///
272     T avg() const @property
273     { return array[0..fill].mean; }
274 }
275 
276 ///
277 unittest
278 {
279     auto ma = new MovingAverage!float( 3 );
280     assertThrown!AssertError( ma.avg );
281     ma.put( 1 );
282     assertEq( ma.avg, 1 );
283     ma.put( 1 );
284     assertEq( ma.avg, 1 );
285     ma.put( 4 );
286     assertEq( ma.avg, 2 );
287     ma.put( 4 );
288     ma.put( 4 );
289     assertEq( ma.avg, 4 );
290     assertEq( ma.array.length, 3 );
291 }