1 // SPDX-License-Identifier: Apache-2.0
2 //
3 // Copyright 2008-2016 Conrad Sanderson (http://conradsanderson.id.au)
4 // Copyright 2008-2016 National ICT Australia (NICTA)
5 //
6 // Licensed under the Apache License, Version 2.0 (the "License");
7 // you may not use this file except in compliance with the License.
8 // You may obtain a copy of the License at
9 // http://www.apache.org/licenses/LICENSE-2.0
10 //
11 // Unless required by applicable law or agreed to in writing, software
12 // distributed under the License is distributed on an "AS IS" BASIS,
13 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 // See the License for the specific language governing permissions and
15 // limitations under the License.
16 // ------------------------------------------------------------------------
17 
18 
19 //! \addtogroup op_median
20 //! @{
21 
22 
23 
24 //! \brief
25 //! For each row or for each column, find the median value.
26 //! The result is stored in a dense matrix that has either one column or one row.
27 //! The dimension, for which the medians are found, is set via the median() function.
28 template<typename eT, typename T1>
29 inline
30 void
apply(Mat<eT> & out,const Op<T1,op_median> & in,const typename arma_not_cx<eT>::result * junk)31 op_median::apply(Mat<eT>& out, const Op<T1,op_median>& in, const typename arma_not_cx<eT>::result* junk)
32   {
33   arma_extra_debug_sigprint();
34   arma_ignore(junk);
35 
36   // typedef typename T1::elem_type eT;
37 
38   const uword dim = in.aux_uword_a;
39   arma_debug_check( (dim > 1), "median(): parameter 'dim' must be 0 or 1" );
40 
41   const Proxy<T1> P(in.m);
42 
43   typedef typename Proxy<T1>::stored_type P_stored_type;
44 
45   const bool is_alias = P.is_alias(out);
46 
47   if(is_Mat<P_stored_type>::value || is_alias)
48     {
49     const unwrap_check<P_stored_type> tmp(P.Q, is_alias);
50 
51     const typename unwrap_check<P_stored_type>::stored_type& X = tmp.M;
52 
53     const uword X_n_rows = X.n_rows;
54     const uword X_n_cols = X.n_cols;
55 
56     if(dim == 0)  // in each column
57       {
58       arma_extra_debug_print("op_median::apply(): dim = 0");
59 
60       out.set_size((X_n_rows > 0) ? 1 : 0, X_n_cols);
61 
62       if(X_n_rows > 0)
63         {
64         std::vector<eT> tmp_vec(X_n_rows);
65 
66         for(uword col=0; col < X_n_cols; ++col)
67           {
68           arrayops::copy( &(tmp_vec[0]), X.colptr(col), X_n_rows );
69 
70           out[col] = op_median::direct_median(tmp_vec);
71           }
72         }
73       }
74     else  // in each row
75       {
76       arma_extra_debug_print("op_median::apply(): dim = 1");
77 
78       out.set_size(X_n_rows, (X_n_cols > 0) ? 1 : 0);
79 
80       if(X_n_cols > 0)
81         {
82         std::vector<eT> tmp_vec(X_n_cols);
83 
84         for(uword row=0; row < X_n_rows; ++row)
85           {
86           for(uword col=0; col < X_n_cols; ++col)  { tmp_vec[col] = X.at(row,col); }
87 
88           out[row] = op_median::direct_median(tmp_vec);
89           }
90         }
91       }
92     }
93   else
94     {
95     const uword P_n_rows = P.get_n_rows();
96     const uword P_n_cols = P.get_n_cols();
97 
98     if(dim == 0)  // in each column
99       {
100       arma_extra_debug_print("op_median::apply(): dim = 0");
101 
102       out.set_size((P_n_rows > 0) ? 1 : 0, P_n_cols);
103 
104       if(P_n_rows > 0)
105         {
106         std::vector<eT> tmp_vec(P_n_rows);
107 
108         for(uword col=0; col < P_n_cols; ++col)
109           {
110           for(uword row=0; row < P_n_rows; ++row)  { tmp_vec[row] = P.at(row,col); }
111 
112           out[col] = op_median::direct_median(tmp_vec);
113           }
114         }
115       }
116     else  // in each row
117       {
118       arma_extra_debug_print("op_median::apply(): dim = 1");
119 
120       out.set_size(P_n_rows, (P_n_cols > 0) ? 1 : 0);
121 
122       if(P_n_cols > 0)
123         {
124         std::vector<eT> tmp_vec(P_n_cols);
125 
126         for(uword row=0; row < P_n_rows; ++row)
127           {
128           for(uword col=0; col < P_n_cols; ++col)  { tmp_vec[col] = P.at(row,col); }
129 
130           out[row] = op_median::direct_median(tmp_vec);
131           }
132         }
133       }
134     }
135   }
136 
137 
138 
139 //! Implementation for complex numbers
140 template<typename eT, typename T1>
141 inline
142 void
apply(Mat<eT> & out,const Op<T1,op_median> & in,const typename arma_cx_only<eT>::result * junk)143 op_median::apply(Mat<eT>& out, const Op<T1,op_median>& in, const typename arma_cx_only<eT>::result* junk)
144   {
145   arma_extra_debug_sigprint();
146   arma_ignore(junk);
147 
148   // typedef typename std::complex<T> eT;
149   typedef typename get_pod_type<eT>::result T;
150 
151   arma_type_check(( is_same_type<eT, typename T1::elem_type>::no ));
152 
153   const unwrap_check<T1> tmp(in.m, out);
154   const Mat<eT>&     X = tmp.M;
155 
156   const uword X_n_rows = X.n_rows;
157   const uword X_n_cols = X.n_cols;
158 
159   const uword dim = in.aux_uword_a;
160   arma_debug_check( (dim > 1), "median(): parameter 'dim' must be 0 or 1" );
161 
162   if(dim == 0)  // in each column
163     {
164     arma_extra_debug_print("op_median::apply(): dim = 0");
165 
166     out.set_size((X_n_rows > 0) ? 1 : 0, X_n_cols);
167 
168     if(X_n_rows > 0)
169       {
170       std::vector< arma_cx_median_packet<T> > tmp_vec(X_n_rows);
171 
172       for(uword col=0; col<X_n_cols; ++col)
173         {
174         const eT* colmem = X.colptr(col);
175 
176         for(uword row=0; row<X_n_rows; ++row)
177           {
178           tmp_vec[row].val   = std::abs(colmem[row]);
179           tmp_vec[row].index = row;
180           }
181 
182         uword index1;
183         uword index2;
184         op_median::direct_cx_median_index(index1, index2, tmp_vec);
185 
186         out[col] = op_mean::robust_mean(colmem[index1], colmem[index2]);
187         }
188       }
189     }
190   else
191   if(dim == 1)  // in each row
192     {
193     arma_extra_debug_print("op_median::apply(): dim = 1");
194 
195     out.set_size(X_n_rows, (X_n_cols > 0) ? 1 : 0);
196 
197     if(X_n_cols > 0)
198       {
199       std::vector< arma_cx_median_packet<T> > tmp_vec(X_n_cols);
200 
201       for(uword row=0; row<X_n_rows; ++row)
202         {
203         for(uword col=0; col<X_n_cols; ++col)
204           {
205           tmp_vec[col].val   = std::abs(X.at(row,col));
206           tmp_vec[col].index = col;
207           }
208 
209         uword index1;
210         uword index2;
211         op_median::direct_cx_median_index(index1, index2, tmp_vec);
212 
213         out[row] = op_mean::robust_mean( X.at(row,index1), X.at(row,index2) );
214         }
215       }
216     }
217   }
218 
219 
220 
221 template<typename T1>
222 inline
223 typename T1::elem_type
median_vec(const T1 & X,const typename arma_not_cx<typename T1::elem_type>::result * junk)224 op_median::median_vec
225   (
226   const T1& X,
227   const typename arma_not_cx<typename T1::elem_type>::result* junk
228   )
229   {
230   arma_extra_debug_sigprint();
231   arma_ignore(junk);
232 
233   typedef typename T1::elem_type eT;
234 
235   typedef typename Proxy<T1>::stored_type P_stored_type;
236 
237   const Proxy<T1> P(X);
238 
239   const uword n_elem = P.get_n_elem();
240 
241   if(n_elem == 0)
242     {
243     arma_debug_check(true, "median(): object has no elements");
244 
245     return Datum<eT>::nan;
246     }
247 
248   std::vector<eT> tmp_vec(n_elem);
249 
250   if(is_Mat<P_stored_type>::value)
251     {
252     const unwrap<P_stored_type> tmp(P.Q);
253 
254     const typename unwrap<P_stored_type>::stored_type& Y = tmp.M;
255 
256     arrayops::copy( &(tmp_vec[0]), Y.memptr(), n_elem );
257     }
258   else
259     {
260     if(Proxy<T1>::use_at == false)
261       {
262       typedef typename Proxy<T1>::ea_type ea_type;
263 
264       ea_type A = P.get_ea();
265 
266       for(uword i=0; i<n_elem; ++i)  { tmp_vec[i] = A[i]; }
267       }
268     else
269       {
270       const uword n_rows = P.get_n_rows();
271       const uword n_cols = P.get_n_cols();
272 
273       if(n_cols == 1)
274         {
275         for(uword row=0; row < n_rows; ++row)  { tmp_vec[row] = P.at(row,0); }
276         }
277       else
278       if(n_rows == 1)
279         {
280         for(uword col=0; col < n_cols; ++col)  { tmp_vec[col] = P.at(0,col); }
281         }
282       else
283         {
284         arma_stop_logic_error("op_median::median_vec(): expected a vector" );
285         }
286       }
287     }
288 
289   return op_median::direct_median(tmp_vec);
290   }
291 
292 
293 
294 template<typename T1>
295 inline
296 typename T1::elem_type
median_vec(const T1 & X,const typename arma_cx_only<typename T1::elem_type>::result * junk)297 op_median::median_vec
298   (
299   const T1& X,
300   const typename arma_cx_only<typename T1::elem_type>::result* junk
301   )
302   {
303   arma_extra_debug_sigprint();
304   arma_ignore(junk);
305 
306   typedef typename T1::elem_type eT;
307   typedef typename T1::pod_type   T;
308 
309   const Proxy<T1> P(X);
310 
311   const uword n_elem = P.get_n_elem();
312 
313   if(n_elem == 0)
314     {
315     arma_debug_check(true, "median(): object has no elements");
316 
317     return Datum<eT>::nan;
318     }
319 
320   std::vector< arma_cx_median_packet<T> > tmp_vec(n_elem);
321 
322   if(Proxy<T1>::use_at == false)
323     {
324     typedef typename Proxy<T1>::ea_type ea_type;
325 
326     ea_type A = P.get_ea();
327 
328     for(uword i=0; i<n_elem; ++i)
329       {
330       tmp_vec[i].val   = std::abs( A[i] );
331       tmp_vec[i].index = i;
332       }
333 
334     uword index1;
335     uword index2;
336     op_median::direct_cx_median_index(index1, index2, tmp_vec);
337 
338     return op_mean::robust_mean( A[index1], A[index2] );
339     }
340   else
341     {
342     const uword n_rows = P.get_n_rows();
343     const uword n_cols = P.get_n_cols();
344 
345     if(n_cols == 1)
346       {
347       for(uword row=0; row < n_rows; ++row)
348         {
349         tmp_vec[row].val   = std::abs( P.at(row,0) );
350         tmp_vec[row].index = row;
351         }
352 
353       uword index1;
354       uword index2;
355       op_median::direct_cx_median_index(index1, index2, tmp_vec);
356 
357       return op_mean::robust_mean( P.at(index1,0), P.at(index2,0) );
358       }
359     else
360     if(n_rows == 1)
361       {
362       for(uword col=0; col < n_cols; ++col)
363         {
364         tmp_vec[col].val   = std::abs( P.at(0,col) );
365         tmp_vec[col].index = col;
366         }
367 
368       uword index1;
369       uword index2;
370       op_median::direct_cx_median_index(index1, index2, tmp_vec);
371 
372       return op_mean::robust_mean( P.at(0,index1), P.at(0,index2) );
373       }
374     else
375       {
376       arma_stop_logic_error("op_median::median_vec(): expected a vector" );
377 
378       return eT(0);
379       }
380     }
381   }
382 
383 
384 
385 //! find the median value of a std::vector (contents is modified)
386 template<typename eT>
387 inline
388 eT
direct_median(std::vector<eT> & X)389 op_median::direct_median(std::vector<eT>& X)
390   {
391   arma_extra_debug_sigprint();
392 
393   // TODO: if NaN is detected, return NaN
394 
395   const uword n_elem = uword(X.size());
396   const uword half   = n_elem/2;
397 
398   typename std::vector<eT>::iterator first    = X.begin();
399   typename std::vector<eT>::iterator nth      = first + half;
400   typename std::vector<eT>::iterator pastlast = X.end();
401 
402   std::nth_element(first, nth, pastlast);
403 
404   if((n_elem % 2) == 0)  // even number of elements
405     {
406     typename std::vector<eT>::iterator start   = X.begin();
407     typename std::vector<eT>::iterator pastend = start + half;
408 
409     const eT val1 = (*nth);
410     const eT val2 = (*(std::max_element(start, pastend)));
411 
412     return op_mean::robust_mean(val1, val2);
413     }
414   else  // odd number of elements
415     {
416     return (*nth);
417     }
418   }
419 
420 
421 
422 template<typename T>
423 inline
424 void
direct_cx_median_index(uword & out_index1,uword & out_index2,std::vector<arma_cx_median_packet<T>> & X)425 op_median::direct_cx_median_index
426   (
427   uword& out_index1,
428   uword& out_index2,
429   std::vector< arma_cx_median_packet<T> >& X
430   )
431   {
432   arma_extra_debug_sigprint();
433 
434   typedef arma_cx_median_packet<T> eT;
435 
436   // TODO: if NaN is detected, return bool set to false, indicating presence of NaN
437 
438   const uword n_elem = uword(X.size());
439   const uword half   = n_elem/2;
440 
441   typename std::vector<eT>::iterator first    = X.begin();
442   typename std::vector<eT>::iterator nth      = first + half;
443   typename std::vector<eT>::iterator pastlast = X.end();
444 
445   std::nth_element(first, nth, pastlast);
446 
447   out_index1 = (*nth).index;
448 
449   if((n_elem % 2) == 0)  // even number of elements
450     {
451     typename std::vector<eT>::iterator start   = X.begin();
452     typename std::vector<eT>::iterator pastend = start + half;
453 
454     out_index2 = (*(std::max_element(start, pastend))).index;
455     }
456   else  // odd number of elements
457     {
458     out_index2 = out_index1;
459     }
460   }
461 
462 
463 
464 //! @}
465 
466