/*
    zavgfilter.c - Filters extremes and returns average
    Copyright (C) 2019 Ladislav Vaiz <ok1zia@nagano.cz>

    This program is free software; you can redistribute it and/or
    modify it under the terms of the GNU General Public License
    version 2 as published by the Free Software Foundation.

*/


#include <zavgfilter.h>
#include <zdebug.h>
#include <assert.h>

double zavg(double *data, int cnt){
    int i, n = 0;
	double sum = 0.0;

    for (i = 0; i < cnt; i++){
		if (isnan(data[i])) continue;
        
        sum += data[i];
        n++;
    }
	if (n == 0) return NAN;
    return sum / n;
}

double zavgfilter(double *data, int incnt, int outcnt, int step){
    
	while(1){
        double avg = zavg(data, incnt);
        double max = NAN;
        int maxi = 0;
        int i, n = 0;

        for (i = 0; i < incnt; i++){
			if (isnan(data[i])) continue;

            double d = fabs(data[i] - avg);
            if (isnan(max) || d > max){
                max = d;
                maxi = i;
            }
            n++;
        }
        if (n <= outcnt || max <= step) return avg;
        data[maxi] = NAN;
    }
    return NAN;
}

double zstddev(double *data, int cnt){
    double avg, ret = 0.0;
    int i, n;

    avg = zavg(data, cnt);
    dbg("avg=%f\n", avg);

    n = 0;
    for (i = 0; i < cnt; i++){
        if (isnan(data[i])) continue;

        ret += pow(data[i] - avg, 2);
        n++;
    }

    dbg("n=%d  var=%f\n", n, ret);

    if (n == 0) return NAN;
    if (n == 1) return 0;
    ret = sqrt(ret / (n - 1));
    dbg("ret=%f\n", ret);
    return ret;
}

double zminimum(double *data, int cnt){
    int i;
	double min = DBL_MAX;

    for (i = 0; i < cnt; i++){
		if (isnan(data[i])) continue;
        
        if (data[i] < min) min = data[i];
    }
	if (min == DBL_MAX) return NAN;
    return min;
}

double zmaximum(double *data, int cnt){
    int i;
	double max = DBL_MIN;

    for (i = 0; i < cnt; i++){
		if (isnan(data[i])) continue;
        
        if (data[i] > max) max = data[i];
    }
	if (max == DBL_MAX) return NAN;
    return max;
}


void zavgfilter_test(){
    double avg;

    double arr1[] = {10, 0, 9, 30, 11};
    avg = zavgfilter(arr1, 5, 3, 2);
    assert(avg == 10.0);

    double arr2[] = {10, NAN, 9, 30, 11};
	avg = zavgfilter(arr2, 5, 3, 2);
    assert(avg == 10.0);

    double arr3[] = {10, NAN, 9, 11};
	avg = zavgfilter(arr3, 4, 4, 2);
    assert(avg == 10.0);

    double arr4[] = {10, NAN, 9, 11};
	avg = zavgfilter(arr4, 4, 3, 2);
    assert(avg == 10.0);

    double arr5[] = { NAN, 9, 11};
	avg = zavgfilter(arr5, 3, 3, 2);
    assert(avg == 10.0);
	
	double arr6[] = { -1 };
	avg = zavgfilter(arr6, 0, 0, 2);
	assert(isnan(avg));

	double arr7[] = { 28, 28, 28, 28, 28, 28, 28, 24, 28, 20};
	avg = zavgfilter(arr7, 10, 5, 2);
	assert(avg == 28);
	
	double arr8[] = {27, 27, 27, 27, 27, 27, 26, 27, 27, 27};
	avg = zavgfilter(arr8, 10, 5, 2);
	assert(avg == 26.899999999999999);

	double arr9[] = { 1206, 1206, 1206, 1206, 1206, 1206, 1189, 1189, 1206, 1206 };
	avg = zavgfilter(arr9, 10, 5, 20);
	assert(avg == 1202.5999999999999);
	
}
