/*

  xmunipack - fits implementation of statistics

  Copyright © 2018 F.Hroch (hroch@physics.muni.cz)

  This file is part of Munipack.

  Munipack is free software: you can redistribute it and/or modify
  it under the terms of the GNU General Public License as published by
  the Free Software Foundation, either version 3 of the License, or
  (at your option) any later version.

  Munipack is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  GNU General Public License for more details.

  You should have received a copy of the GNU General Public License
  along with Munipack.  If not, see <http://www.gnu.org/licenses/>.

*/

#include "fits.h"
#include <cstdio>
#include <cstring>
#include <fitsio.h>
#include <cmath>
#include <cfloat>
#include <algorithm>
#include <wx/wx.h>

using namespace std;

FitsArrayStat::FitsArrayStat(const FitsArray& a, int nskip):
  FitsArray(a),statistics(false),
  skip(nskip > 0 ? nskip : max(int(Npixels())/32768,1)),
  med(0.0),mad(0.0),xmin(0.0),xmax(0.0)
{
  FitsArrayData *data = static_cast<FitsArrayData *>(m_refData);
  wxASSERT(data && data->array && data->naxes);

  long nd = Npixels() / skip;
  float *d = new float[nd+1];

  // maximum, minimum
  for(long i = 0, j = 0; j < nd; i++, j++) {
    float x = Pixel(i);
    if( x > xmax ) xmax = x;
    if( x < xmin ) xmin = x;
  }

  // median
  for(long i = 0, j = 0; j < nd; i += skip, j++) {
    float x = Pixel(i);
    d[j] = x;
  }
  med = QMed(nd,d,nd/2+1);

  // mad = median of absolute deviations
  long n = 0;
  int imax = Npixels() - skip;
  for(int i = 0; n < nd && i < imax; i += skip) {
    float r = Pixel(i) - med;
    if( r > 0.0 )
      d[n++] = r;
  }
  mad = QMed(n,d,n/2+1);

  delete[] d;

  wxLogDebug("stat: %f %f %ld %f %d",mad,med,n,Pixel(1),skip);
  statistics = true;
}

float FitsArrayStat::GetMed() const
{
  wxASSERT(statistics);
  return med;
}

float FitsArrayStat::GetMad() const
{
  wxASSERT(statistics);
  return mad;
}
float FitsArrayStat::GetMin() const
{
  wxASSERT(statistics);
  return xmin;
}
float FitsArrayStat::GetMax() const
{
  wxASSERT(statistics);
  return xmax;
}

float FitsArrayStat::QMed(long n, float *a, int k)
{
  float w,x;
  long l,r,i,j;

  x = 0.0;

  l = 0;
  r = n - 1;

  while( l < r ) {
    x = a[k]; i = l; j = r;

    do {

      while( a[i] < x && i < r) i++;
      while( x < a[j] && j > l) j--;

      if( i <= j ) { w = a[i]; a[i] = a[j]; a[j] = w; i++; j--; }

    } while ( i <= j );

    if( j < k ) l = i;
    if( k < i ) r = j;
  }

  return(x);
}
