1 module des.math.method.stat.moment;
2 
3 import des.util.testsuite;
4 
5 /// expected value
6 T mean(T)( in T[] arr ) pure nothrow @property @nogc
7 if( is( typeof( T.init + T.init ) == T ) && is( typeof( T.init / 1UL ) == T ) )
8 in { assert( arr.length > 0 ); } body
9 {
10     if( arr.length == 1 ) return arr[0];
11     T res = arr[0];
12     foreach( i; 1 .. arr.length )
13         res = res + arr[i];
14     return res / arr.length;
15 }
16 
17 ///
18 unittest
19 {
20     auto a = [ 1.0f, 2, 3 ];
21     assert( a.mean == 2.0f );
22 
23     static assert( !__traits(compiles,[1,2,3].mean) );
24     static assert(  __traits(compiles,[1.0f,2,3].mean) );
25 }
26 
27 ///
28 unittest
29 {
30     import des.math.linear.vector;
31 
32     auto a = [ vec3(1,2,3), vec3(2,3,4), vec3(3,4,5) ];
33     assert( a.mean == vec3(2,3,4) );
34 }
35 
36 ///
37 T variance(T)( in T[] arr ) pure nothrow @property @nogc
38 if( is( typeof( T.init + T.init ) == T ) &&
39     is( typeof( T.init - T.init ) == T ) &&
40     is( typeof( T.init * T.init ) == T ) &&
41     is( typeof( T.init / 1UL ) == T ) )
42 in { assert( arr.length > 1 ); } body
43 {
44     T res = arr[0] - arr[0];
45     auto m = arr.mean;
46     foreach( val; arr )
47         res = res + ( m - val ) * ( m - val );
48     return res / ( arr.length - 1 );
49 }
50 
51 ///
52 unittest
53 {
54     auto a = [ 1.0f, 1, 1 ];
55     assert( a.variance == 0.0f );
56 
57     auto b = [ 1.0f, 2, 3 ];
58     assert( b.variance == 1.0f );
59 }
60 
61 ///
62 unittest
63 {
64     import des.math.linear.vector;
65 
66     auto a = [ vec3(1,2,3), vec3(2,3,4), vec3(3,4,5) ];
67     assert( a.variance == vec3(1,1,1) );
68 }
69 
70 /++ returns:
71     mean (0 element), variance (1 element)
72 +/
73 T[2] mean_variance(T)( in T[] arr ) pure nothrow @property @nogc
74 if( is( typeof( T.init + T.init ) == T ) &&
75     is( typeof( T.init - T.init ) == T ) &&
76     is( typeof( T.init * T.init ) == T ) &&
77     is( typeof( T.init / 1UL ) == T ) )
78 in { assert( arr.length > 1 ); } body
79 {
80     T res = arr[0] - arr[0];
81     auto m = arr.mean;
82     foreach( val; arr )
83         res = res + ( m - val ) * ( m - val );
84     return [ m, res / ( arr.length - 1 ) ];
85 }
86 
87 ///
88 unittest
89 {
90     auto a = [ 1.0f, 1, 1 ];
91     assert( a.mean_variance == [ 1.0f, 0.0f ] );
92 
93     auto b = [ 1.0f, 2, 3 ];
94     assert( b.mean_variance == [ 2.0f, 1.0f ] );
95 }
96 
97 ///
98 unittest
99 {
100     import des.math.linear.vector;
101 
102     auto a = [ vec3(1,2,3), vec3(2,3,4), vec3(3,4,5) ];
103     assert( a.mean_variance == [ vec3(2,3,4), vec3(1,1,1) ] );
104 }
105 
106 ///
107 T rawMoment(T)( in T[] arr, size_t k=1 ) pure nothrow @property @nogc
108 if( is( typeof( T.init + T.init ) == T ) &&
109     is( typeof( T.init * T.init ) == T ) &&
110     is( typeof( T.init / T.init ) == T ) &&
111     is( typeof( T.init / 1UL ) == T ) )
112 in { assert( arr.length > 0 ); } body
113 {
114     if( arr.length == 1 ) return spow( arr[0], k );
115     T res = arr[0];
116     foreach( i; 1 .. arr.length )
117         res = res + spow( arr[i], k );
118     return res / arr.length;
119 }
120 
121 ///
122 unittest
123 {
124     auto a = [ 1.0f, 2 ];
125     assert( a.rawMoment == 1.5 );
126     assert( a.rawMoment(2) == 2.5 );
127 }
128 
129 ///
130 T spow(T)( in T val, size_t k ) pure nothrow @nogc
131 if( is( typeof( T.init / T.init ) == T ) && is( typeof( T.init * T.init ) == T ) )
132 {
133     //TODO: optimize
134     T ret = val / val;
135     if( k == 0 ) return ret;
136     if( k == 1 ) return val;
137     if( k == 2 ) return val * val;
138     foreach( i; 0 .. k ) ret = ret * val;
139     return ret;
140 }
141 
142 ///
143 unittest
144 {
145     import des.math.linear.vector;
146     assert( spow( vec3( 1, 2, 3 ), 3 ) == vec3( 1, 8, 27 ) );
147     assert( spow( 10, 0 ) == 1 );
148     assert( spow( 10, 1 ) == 10 );
149     assert( spow( 10, 2 ) == 100 );
150     assert( spow( 10, 3 ) == 1000 );
151     assert( spow( 10, 4 ) == 10000 );
152 }
153 
154 ///
155 T centralMoment(T)( in T[] arr, size_t k=1 ) pure nothrow @property @nogc
156 if( is( typeof( T.init + T.init ) == T ) &&
157     is( typeof( T.init * T.init ) == T ) &&
158     is( typeof( T.init / T.init ) == T ) &&
159     is( typeof( T.init / 1UL ) == T ) )
160 in { assert( arr.length > 0 ); } body
161 {
162     T res = arr[0] - arr[0];
163     auto m = arr.mean;
164     foreach( val; arr )
165         res = res + spow( val - m, k );
166     return res / arr.length;
167 }
168 
169 ///
170 unittest
171 {
172     auto a = [ 1.0f, 2, 3, 4 ];
173     assert( a.centralMoment(1) == 0 );
174     assert( a.centralMoment(2) == 1.25 );
175     assert( a.centralMoment(3) == 0 );
176 }
177 
178 ///
179 class MovingAverage(T) if( is(typeof(T[].init.mean)) )
180 {
181     ///
182     T[] array;
183     ///
184     size_t cur_index = 0;
185     ///
186     size_t max_length = 1;
187 
188     ///
189     this( size_t mlen ) { max_length = mlen; }
190 
191     invariant()
192     {
193         assert( array.length <= max_length );
194     }
195 
196     ///
197     void append( in T val )
198     {
199         if( array.length < max_length ) array ~= val;
200         else array[cur_index++%max_length] = val;
201     }
202 
203     @property
204     {
205         ///
206         size_t maxLength() const { return max_length; }
207         ///
208         void maxLength( size_t mlen )
209         {
210             max_length = mlen;
211             if( array.length > mlen )
212                 array.length = mlen;
213         }
214 
215         ///
216         T avg() const { return array.mean; }
217     }
218 }
219 
220 unittest
221 {
222     auto ma = new MovingAverage!float( 3 );
223     mustExcept({ ma.avg; });
224     ma.append( 1 );
225     assertEq( ma.avg, 1 );
226     ma.append( 1 );
227     assertEq( ma.avg, 1 );
228     ma.append( 4 );
229     assertEq( ma.avg, 2 );
230     ma.append( 4 );
231     ma.append( 4 );
232     assertEq( ma.avg, 4 );
233     assertEq( ma.array.length, 3 );
234 }